Add share type change to Share Migration
This patch adds a 'new_share_type_id' parameter to Share Migration, where the destination share can be provisioned under a different share type of choice. Host-assisted migration handles it by creating a totally new share, as before. Driver-assisted migration handles by creating the destination instance model with the new share type, the driver is responsible for making the necessary changes to satisfy the provided model. In order to accomplish this, a database change was required, transferring the 'share_type_id' field from the 'shares' table to the 'share_instances' table. APIImpact Partially implements: blueprint newton-migration-improvements Change-Id: I3200eaaa5b66d9b8ce1cbd16c1658db8516c70fb
This commit is contained in:
parent
9639e72692
commit
6dad1666de
@ -110,6 +110,7 @@ class ShareController(shares.ShareMixin,
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
new_share_network = None
|
||||
new_share_type = None
|
||||
|
||||
preserve_metadata = params.get('preserve_metadata', True)
|
||||
try:
|
||||
@ -145,13 +146,23 @@ class ShareController(shares.ShareMixin,
|
||||
except exception.NotFound:
|
||||
msg = _("Share network %s not "
|
||||
"found.") % new_share_network_id
|
||||
raise exc.HTTPNotFound(explanation=msg)
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
new_share_type_id = params.get('new_share_type_id', None)
|
||||
if new_share_type_id:
|
||||
try:
|
||||
new_share_type = db.share_type_get(
|
||||
context, new_share_type_id)
|
||||
except exception.NotFound:
|
||||
msg = _("Share type %s not found.") % new_share_type_id
|
||||
raise exc.HTTPBadRequest(explanation=msg)
|
||||
|
||||
try:
|
||||
self.share_api.migration_start(
|
||||
context, share, host, force_host_assisted_migration,
|
||||
preserve_metadata, writable, nondisruptive,
|
||||
new_share_network=new_share_network)
|
||||
new_share_network=new_share_network,
|
||||
new_share_type=new_share_type)
|
||||
except exception.Conflict as e:
|
||||
raise exc.HTTPConflict(explanation=six.text_type(e))
|
||||
|
||||
|
@ -22,6 +22,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
"remove_export_locations",
|
||||
"add_access_rules_status_field",
|
||||
"add_replication_fields",
|
||||
"add_share_type_field",
|
||||
]
|
||||
|
||||
def detail_list(self, request, instances):
|
||||
@ -78,3 +79,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
@common.ViewBuilder.versioned_method("2.11")
|
||||
def add_replication_fields(self, context, instance_dict, share_instance):
|
||||
instance_dict['replica_state'] = share_instance.get('replica_state')
|
||||
|
||||
@common.ViewBuilder.versioned_method("2.22")
|
||||
def add_share_type_field(self, context, instance_dict, share_instance):
|
||||
instance_dict['share_type_id'] = share_instance.get('share_type_id')
|
||||
|
@ -0,0 +1,83 @@
|
||||
# Copyright 2016, Hitachi Data Systems.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
"""move_share_type_id_to_instances
|
||||
|
||||
Revision ID: 48a7beae3117
|
||||
Revises: 63809d875e32
|
||||
Create Date: 2016-07-19 13:04:50.035139
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '48a7beae3117'
|
||||
down_revision = '63809d875e32'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from manila.db.migrations import utils
|
||||
|
||||
|
||||
def upgrade():
|
||||
"""Move share_type_id from Shares to Share Instances table."""
|
||||
|
||||
# NOTE(ganso): Adding share_type_id as a foreign key to share_instances
|
||||
# table. Please note that share_type_id is NOT a foreign key in shares
|
||||
# table prior to this migration.
|
||||
op.add_column(
|
||||
'share_instances',
|
||||
sa.Column('share_type_id', sa.String(36),
|
||||
sa.ForeignKey('share_types.id', name='si_st_id_fk'),
|
||||
nullable=True))
|
||||
connection = op.get_bind()
|
||||
shares_table = utils.load_table('shares', connection)
|
||||
share_instances_table = utils.load_table('share_instances', connection)
|
||||
|
||||
for instance in connection.execute(share_instances_table.select()):
|
||||
share = connection.execute(shares_table.select().where(
|
||||
instance['share_id'] == shares_table.c.id)).first()
|
||||
op.execute(share_instances_table.update().where(
|
||||
share_instances_table.c.id == instance['id']).values(
|
||||
{'share_type_id': share['share_type_id']}))
|
||||
|
||||
op.drop_column('shares', 'share_type_id')
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""Move share_type_id from Share Instances to Shares table.
|
||||
|
||||
This method can lead to data loss because only the share_type_id from the
|
||||
first share instance is moved to the shares table.
|
||||
"""
|
||||
|
||||
# NOTE(ganso): Adding back share_type_id to the shares table NOT as a
|
||||
# foreign key, as it was before.
|
||||
op.add_column(
|
||||
'shares',
|
||||
sa.Column('share_type_id', sa.String(36), nullable=True))
|
||||
connection = op.get_bind()
|
||||
shares_table = utils.load_table('shares', connection)
|
||||
share_instances_table = utils.load_table('share_instances', connection)
|
||||
|
||||
for share in connection.execute(shares_table.select()):
|
||||
instance = connection.execute(share_instances_table.select().where(
|
||||
share['id'] == share_instances_table.c.share_id)).first()
|
||||
op.execute(shares_table.update().where(
|
||||
shares_table.c.id == instance['share_id']).values(
|
||||
{'share_type_id': instance['share_type_id']}))
|
||||
|
||||
op.drop_constraint('si_st_id_fk', 'share_instances', type_='foreignkey')
|
||||
op.drop_column('share_instances', 'share_type_id')
|
@ -1100,7 +1100,7 @@ def _extract_share_instance_values(values):
|
||||
share_instance_model_fields = [
|
||||
'status', 'host', 'scheduled_at', 'launched_at', 'terminated_at',
|
||||
'share_server_id', 'share_network_id', 'availability_zone',
|
||||
'replica_state',
|
||||
'replica_state', 'share_type_id', 'share_type',
|
||||
]
|
||||
share_instance_values, share_values = (
|
||||
_extract_instance_values(values, share_instance_model_fields)
|
||||
@ -1174,6 +1174,7 @@ def share_instance_get(context, share_instance_id, session=None,
|
||||
id=share_instance_id,
|
||||
).options(
|
||||
joinedload('export_locations'),
|
||||
joinedload('share_type'),
|
||||
).first()
|
||||
if result is None:
|
||||
raise exception.NotFound()
|
||||
@ -1421,8 +1422,7 @@ def _share_get_query(context, session=None):
|
||||
if session is None:
|
||||
session = get_session()
|
||||
return model_query(context, models.Share, session=session).\
|
||||
options(joinedload('share_metadata')).\
|
||||
options(joinedload('share_type'))
|
||||
options(joinedload('share_metadata'))
|
||||
|
||||
|
||||
def _metadata_refs(metadata_dict, meta_class):
|
||||
@ -1565,7 +1565,7 @@ def _share_get_all_with_filters(context, project_id=None, share_server_id=None,
|
||||
query = query.join(
|
||||
models.ShareTypeExtraSpecs,
|
||||
models.ShareTypeExtraSpecs.share_type_id ==
|
||||
models.Share.share_type_id)
|
||||
models.ShareInstance.share_type_id)
|
||||
for k, v in filters['extra_specs'].items():
|
||||
query = query.filter(or_(models.ShareTypeExtraSpecs.key == k,
|
||||
models.ShareTypeExtraSpecs.value == v))
|
||||
@ -3222,7 +3222,7 @@ def share_type_destroy(context, id):
|
||||
session = get_session()
|
||||
with session.begin():
|
||||
_share_type_get(context, id, session)
|
||||
results = model_query(context, models.Share, session=session,
|
||||
results = model_query(context, models.ShareInstance, session=session,
|
||||
read_deleted="no").\
|
||||
filter_by(share_type_id=id).count()
|
||||
cg_count = model_query(context,
|
||||
|
@ -189,7 +189,7 @@ class Share(BASE, ManilaBase):
|
||||
__tablename__ = 'shares'
|
||||
_extra_keys = ['name', 'export_location', 'export_locations', 'status',
|
||||
'host', 'share_server_id', 'share_network_id',
|
||||
'availability_zone', 'access_rules_status']
|
||||
'availability_zone', 'access_rules_status', 'share_type_id']
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@ -227,7 +227,8 @@ class Share(BASE, ManilaBase):
|
||||
|
||||
def __getattr__(self, item):
|
||||
deprecated_properties = ('host', 'share_server_id', 'share_network_id',
|
||||
'availability_zone')
|
||||
'availability_zone', 'share_type_id',
|
||||
'share_type')
|
||||
proxified_properties = ('status',) + deprecated_properties
|
||||
|
||||
if item in deprecated_properties:
|
||||
@ -303,8 +304,6 @@ class Share(BASE, ManilaBase):
|
||||
snapshot_support = Column(Boolean, default=True)
|
||||
replication_type = Column(String(255), nullable=True)
|
||||
share_proto = Column(String(255))
|
||||
share_type_id = Column(String(36), ForeignKey('share_types.id'),
|
||||
nullable=True)
|
||||
is_public = Column(Boolean, default=False)
|
||||
consistency_group_id = Column(String(36),
|
||||
ForeignKey('consistency_groups.id'),
|
||||
@ -323,13 +322,6 @@ class Share(BASE, ManilaBase):
|
||||
viewonly=True,
|
||||
join_depth=2,
|
||||
)
|
||||
share_type = orm.relationship(
|
||||
"ShareTypes",
|
||||
lazy=True,
|
||||
foreign_keys=share_type_id,
|
||||
primaryjoin='and_('
|
||||
'Share.share_type_id == ShareTypes.id, '
|
||||
'ShareTypes.deleted == "False")')
|
||||
|
||||
|
||||
class ShareInstance(BASE, ManilaBase):
|
||||
@ -339,8 +331,8 @@ class ShareInstance(BASE, ManilaBase):
|
||||
'replica_state']
|
||||
_proxified_properties = ('user_id', 'project_id', 'size',
|
||||
'display_name', 'display_description',
|
||||
'snapshot_id', 'share_proto', 'share_type_id',
|
||||
'is_public', 'consistency_group_id',
|
||||
'snapshot_id', 'share_proto', 'is_public',
|
||||
'consistency_group_id',
|
||||
'source_cgsnapshot_member_id')
|
||||
|
||||
def set_share_data(self, share):
|
||||
@ -386,7 +378,8 @@ class ShareInstance(BASE, ManilaBase):
|
||||
launched_at = Column(DateTime)
|
||||
terminated_at = Column(DateTime)
|
||||
replica_state = Column(String(255), nullable=True)
|
||||
|
||||
share_type_id = Column(String(36), ForeignKey('share_types.id'),
|
||||
nullable=True)
|
||||
availability_zone_id = Column(String(36),
|
||||
ForeignKey('availability_zones.id'),
|
||||
nullable=True)
|
||||
@ -416,6 +409,13 @@ class ShareInstance(BASE, ManilaBase):
|
||||
nullable=True)
|
||||
share_server_id = Column(String(36), ForeignKey('share_servers.id'),
|
||||
nullable=True)
|
||||
share_type = orm.relationship(
|
||||
"ShareTypes",
|
||||
lazy='immediate',
|
||||
foreign_keys=share_type_id,
|
||||
primaryjoin='and_('
|
||||
'ShareInstance.share_type_id == ShareTypes.id, '
|
||||
'ShareTypes.deleted == "False")')
|
||||
|
||||
|
||||
class ShareInstanceExportLocations(BASE, ManilaBase):
|
||||
|
@ -143,10 +143,10 @@ class SchedulerManager(manager.Manager):
|
||||
share_rpcapi.ShareAPI().manage_share(context, share_ref,
|
||||
driver_options)
|
||||
|
||||
def migrate_share_to_host(self, context, share_id, host,
|
||||
force_host_assisted_migration, preserve_metadata,
|
||||
writable, nondisruptive, new_share_network_id,
|
||||
request_spec, filter_properties=None):
|
||||
def migrate_share_to_host(
|
||||
self, context, share_id, host, force_host_assisted_migration,
|
||||
preserve_metadata, writable, nondisruptive, new_share_network_id,
|
||||
new_share_type_id, request_spec, filter_properties=None):
|
||||
"""Ensure that the host exists and can accept the share."""
|
||||
|
||||
share_ref = db.share_get(context, share_id)
|
||||
@ -177,7 +177,7 @@ class SchedulerManager(manager.Manager):
|
||||
share_rpcapi.ShareAPI().migration_start(
|
||||
context, share_ref, tgt_host.host,
|
||||
force_host_assisted_migration, preserve_metadata, writable,
|
||||
nondisruptive, new_share_network_id)
|
||||
nondisruptive, new_share_network_id, new_share_type_id)
|
||||
except Exception as ex:
|
||||
with excutils.save_and_reraise_exception():
|
||||
_migrate_share_set_error(self, context, ex, request_spec)
|
||||
|
@ -84,7 +84,7 @@ class SchedulerAPI(object):
|
||||
def migrate_share_to_host(
|
||||
self, context, share_id, host, force_host_assisted_migration,
|
||||
preserve_metadata, writable, nondisruptive, new_share_network_id,
|
||||
request_spec=None, filter_properties=None):
|
||||
new_share_type_id, request_spec=None, filter_properties=None):
|
||||
|
||||
call_context = self.client.prepare(version='1.4')
|
||||
request_spec_p = jsonutils.to_primitive(request_spec)
|
||||
@ -97,6 +97,7 @@ class SchedulerAPI(object):
|
||||
writable=writable,
|
||||
nondisruptive=nondisruptive,
|
||||
new_share_network_id=new_share_network_id,
|
||||
new_share_type_id=new_share_type_id,
|
||||
request_spec=request_spec_p,
|
||||
filter_properties=filter_properties)
|
||||
|
||||
|
@ -114,11 +114,11 @@ class API(base.Base):
|
||||
if share_type is None:
|
||||
# Grab the source share's share_type if no new share type
|
||||
# has been provided.
|
||||
share_type_id = source_share['share_type_id']
|
||||
share_type_id = source_share['instance']['share_type_id']
|
||||
share_type = share_types.get_share_type(context, share_type_id)
|
||||
else:
|
||||
share_type_id = share_type['id']
|
||||
if share_type_id != source_share['share_type_id']:
|
||||
if share_type_id != source_share['instance']['share_type_id']:
|
||||
msg = _("Invalid share type specified: the requested "
|
||||
"share type must match the type of the source "
|
||||
"share. If a share type is not specified when "
|
||||
@ -231,7 +231,6 @@ class API(base.Base):
|
||||
'display_name': name,
|
||||
'display_description': description,
|
||||
'share_proto': share_proto,
|
||||
'share_type_id': share_type_id,
|
||||
'is_public': is_public,
|
||||
'consistency_group_id': consistency_group_id,
|
||||
}
|
||||
@ -258,7 +257,8 @@ class API(base.Base):
|
||||
self.create_instance(context, share, share_network_id=share_network_id,
|
||||
host=host, availability_zone=availability_zone,
|
||||
consistency_group=consistency_group,
|
||||
cgsnapshot_member=cgsnapshot_member)
|
||||
cgsnapshot_member=cgsnapshot_member,
|
||||
share_type_id=share_type_id)
|
||||
|
||||
# Retrieve the share with instance details
|
||||
share = self.db.share_get(context, share['id'])
|
||||
@ -267,14 +267,16 @@ class API(base.Base):
|
||||
|
||||
def create_instance(self, context, share, share_network_id=None,
|
||||
host=None, availability_zone=None,
|
||||
consistency_group=None, cgsnapshot_member=None):
|
||||
consistency_group=None, cgsnapshot_member=None,
|
||||
share_type_id=None):
|
||||
policy.check_policy(context, 'share', 'create')
|
||||
|
||||
request_spec, share_instance = (
|
||||
self.create_share_instance_and_get_request_spec(
|
||||
context, share, availability_zone=availability_zone,
|
||||
consistency_group=consistency_group, host=host,
|
||||
share_network_id=share_network_id))
|
||||
share_network_id=share_network_id,
|
||||
share_type_id=share_type_id))
|
||||
|
||||
if cgsnapshot_member:
|
||||
# Inherit properties from the cgsnapshot_member
|
||||
@ -309,7 +311,8 @@ class API(base.Base):
|
||||
|
||||
def create_share_instance_and_get_request_spec(
|
||||
self, context, share, availability_zone=None,
|
||||
consistency_group=None, host=None, share_network_id=None):
|
||||
consistency_group=None, host=None, share_network_id=None,
|
||||
share_type_id=None):
|
||||
|
||||
availability_zone_id = None
|
||||
if availability_zone:
|
||||
@ -327,6 +330,7 @@ class API(base.Base):
|
||||
'scheduled_at': timeutils.utcnow(),
|
||||
'host': host if host else '',
|
||||
'availability_zone_id': availability_zone_id,
|
||||
'share_type_id': share_type_id,
|
||||
}
|
||||
)
|
||||
|
||||
@ -339,7 +343,7 @@ class API(base.Base):
|
||||
'share_server_id': share_instance['share_server_id'],
|
||||
'snapshot_support': share['snapshot_support'],
|
||||
'share_proto': share['share_proto'],
|
||||
'share_type_id': share['share_type_id'],
|
||||
'share_type_id': share_type_id,
|
||||
'is_public': share['is_public'],
|
||||
'consistency_group_id': share['consistency_group_id'],
|
||||
'source_cgsnapshot_member_id': share[
|
||||
@ -356,12 +360,13 @@ class API(base.Base):
|
||||
'host': share_instance['host'],
|
||||
'status': share_instance['status'],
|
||||
'replica_state': share_instance['replica_state'],
|
||||
'share_type_id': share_instance['share_type_id'],
|
||||
}
|
||||
|
||||
share_type = None
|
||||
if share['share_type_id']:
|
||||
if share_instance['share_type_id']:
|
||||
share_type = self.db.share_type_get(
|
||||
context, share['share_type_id'])
|
||||
context, share_instance['share_type_id'])
|
||||
|
||||
request_spec = {
|
||||
'share_properties': share_properties,
|
||||
@ -395,7 +400,8 @@ class API(base.Base):
|
||||
request_spec, share_replica = (
|
||||
self.create_share_instance_and_get_request_spec(
|
||||
context, share, availability_zone=availability_zone,
|
||||
share_network_id=share_network_id))
|
||||
share_network_id=share_network_id,
|
||||
share_type_id=share['instance']['share_type_id']))
|
||||
|
||||
all_replicas = self.db.share_replicas_get_all_by_share(
|
||||
context, share['id'])
|
||||
@ -873,10 +879,10 @@ class API(base.Base):
|
||||
|
||||
return snapshot
|
||||
|
||||
def migration_start(self, context, share, dest_host,
|
||||
force_host_assisted_migration, preserve_metadata=True,
|
||||
writable=True, nondisruptive=False,
|
||||
new_share_network=None):
|
||||
def migration_start(
|
||||
self, context, share, dest_host, force_host_assisted_migration,
|
||||
preserve_metadata=True, writable=True, nondisruptive=False,
|
||||
new_share_network=None, new_share_type=None):
|
||||
"""Migrates share to a new host."""
|
||||
|
||||
share_instance = share.instance
|
||||
@ -921,13 +927,42 @@ class API(base.Base):
|
||||
service = self.db.service_get_by_args(
|
||||
context, dest_host_host, 'manila-share')
|
||||
|
||||
share_type = {}
|
||||
share_type_id = share['share_type_id']
|
||||
if share_type_id:
|
||||
share_type = share_types.get_share_type(context, share_type_id)
|
||||
if new_share_type:
|
||||
share_type = new_share_type
|
||||
new_share_type_id = new_share_type['id']
|
||||
dhss = share_type['extra_specs']['driver_handles_share_servers']
|
||||
dhss = strutils.bool_from_string(dhss, strict=True)
|
||||
if (dhss and not new_share_network and
|
||||
not share_instance['share_network_id']):
|
||||
msg = _(
|
||||
"New share network must be provided when share type of"
|
||||
" given share %s has extra_spec "
|
||||
"'driver_handles_share_servers' as True.") % share['id']
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
else:
|
||||
share_type = {}
|
||||
share_type_id = share_instance['share_type_id']
|
||||
if share_type_id:
|
||||
share_type = share_types.get_share_type(context, share_type_id)
|
||||
new_share_type_id = share_instance['share_type_id']
|
||||
|
||||
new_share_network_id = (new_share_network['id'] if new_share_network
|
||||
else share_instance['share_network_id'])
|
||||
dhss = share_type['extra_specs']['driver_handles_share_servers']
|
||||
dhss = strutils.bool_from_string(dhss, strict=True)
|
||||
|
||||
if dhss:
|
||||
if new_share_network:
|
||||
new_share_network_id = new_share_network['id']
|
||||
else:
|
||||
new_share_network_id = share_instance['share_network_id']
|
||||
else:
|
||||
if new_share_network:
|
||||
msg = _(
|
||||
"New share network must not be provided when share type of"
|
||||
" given share %s has extra_spec "
|
||||
"'driver_handles_share_servers' as False.") % share['id']
|
||||
raise exception.InvalidInput(reason=msg)
|
||||
|
||||
new_share_network_id = None
|
||||
|
||||
request_spec = self._get_request_spec_dict(
|
||||
share,
|
||||
@ -945,7 +980,7 @@ class API(base.Base):
|
||||
self.scheduler_rpcapi.migrate_share_to_host(
|
||||
context, share['id'], dest_host, force_host_assisted_migration,
|
||||
preserve_metadata, writable, nondisruptive, new_share_network_id,
|
||||
request_spec)
|
||||
new_share_type_id, request_spec)
|
||||
|
||||
def migration_complete(self, context, share):
|
||||
|
||||
|
@ -664,9 +664,9 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
share_server)
|
||||
|
||||
def _migration_start_driver(
|
||||
self, context, share_ref, src_share_instance, dest_host,
|
||||
writable, preserve_metadata, nondisruptive, new_share_network_id,
|
||||
new_az_id):
|
||||
self, context, share_ref, src_share_instance, dest_host, writable,
|
||||
preserve_metadata, nondisruptive, new_share_network_id, new_az_id,
|
||||
new_share_type_id):
|
||||
|
||||
share_server = self._get_share_server(context, src_share_instance)
|
||||
|
||||
@ -675,7 +675,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
request_spec, dest_share_instance = (
|
||||
share_api.create_share_instance_and_get_request_spec(
|
||||
context, share_ref, new_az_id, None, dest_host,
|
||||
new_share_network_id))
|
||||
new_share_network_id, new_share_type_id))
|
||||
|
||||
self.db.share_instance_update(
|
||||
context, dest_share_instance['id'],
|
||||
@ -852,10 +852,10 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
LOG.exception(msg)
|
||||
|
||||
@utils.require_driver_initialized
|
||||
def migration_start(self, context, share_id, dest_host,
|
||||
force_host_assisted_migration, preserve_metadata=True,
|
||||
writable=True, nondisruptive=False,
|
||||
new_share_network_id=None):
|
||||
def migration_start(
|
||||
self, context, share_id, dest_host, force_host_assisted_migration,
|
||||
preserve_metadata=True, writable=True, nondisruptive=False,
|
||||
new_share_network_id=None, new_share_type_id=None):
|
||||
"""Migrates a share from current host to another host."""
|
||||
LOG.debug("Entered migration_start method for share %s.", share_id)
|
||||
|
||||
@ -878,7 +878,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
success = self._migration_start_driver(
|
||||
context, share_ref, share_instance, dest_host, writable,
|
||||
preserve_metadata, nondisruptive, new_share_network_id,
|
||||
new_az_id)
|
||||
new_az_id, new_share_type_id)
|
||||
|
||||
except Exception as e:
|
||||
if not isinstance(e, NotImplementedError):
|
||||
@ -907,7 +907,7 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
|
||||
self._migration_start_host_assisted(
|
||||
context, share_ref, share_instance, dest_host,
|
||||
new_share_network_id, new_az_id)
|
||||
new_share_network_id, new_az_id, new_share_type_id)
|
||||
|
||||
except Exception:
|
||||
msg = _("Host-assisted migration failed for share %s.") % share_id
|
||||
@ -921,8 +921,8 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
raise exception.ShareMigrationFailed(reason=msg)
|
||||
|
||||
def _migration_start_host_assisted(
|
||||
self, context, share, src_share_instance,
|
||||
dest_host, new_share_network_id, new_az_id):
|
||||
self, context, share, src_share_instance, dest_host,
|
||||
new_share_network_id, new_az_id, new_share_type_id):
|
||||
|
||||
rpcapi = share_rpcapi.ShareAPI()
|
||||
|
||||
@ -939,7 +939,8 @@ class ShareManager(manager.SchedulerDependentManager):
|
||||
|
||||
try:
|
||||
dest_share_instance = helper.create_instance_and_wait(
|
||||
share, dest_host, new_share_network_id, new_az_id)
|
||||
share, dest_host, new_share_network_id, new_az_id,
|
||||
new_share_type_id)
|
||||
|
||||
self.db.share_instance_update(
|
||||
context, dest_share_instance['id'],
|
||||
|
@ -84,11 +84,12 @@ class ShareMigrationHelper(object):
|
||||
else:
|
||||
time.sleep(tries ** 2)
|
||||
|
||||
def create_instance_and_wait(
|
||||
self, share, dest_host, new_share_network_id, new_az_id):
|
||||
def create_instance_and_wait(self, share, dest_host, new_share_network_id,
|
||||
new_az_id, new_share_type_id):
|
||||
|
||||
new_share_instance = self.api.create_instance(
|
||||
self.context, share, new_share_network_id, dest_host, new_az_id)
|
||||
self.context, share, new_share_network_id, dest_host,
|
||||
new_az_id, share_type_id=new_share_type_id)
|
||||
|
||||
# Wait for new_share_instance to become ready
|
||||
starttime = time.time()
|
||||
|
@ -126,7 +126,8 @@ class ShareAPI(object):
|
||||
|
||||
def migration_start(self, context, share, dest_host,
|
||||
force_host_assisted_migration, preserve_metadata,
|
||||
writable, nondisruptive, new_share_network_id):
|
||||
writable, nondisruptive, new_share_network_id,
|
||||
new_share_type_id):
|
||||
new_host = utils.extract_host(share['instance']['host'])
|
||||
call_context = self.client.prepare(server=new_host, version='1.12')
|
||||
call_context.cast(
|
||||
@ -138,7 +139,8 @@ class ShareAPI(object):
|
||||
preserve_metadata=preserve_metadata,
|
||||
writable=writable,
|
||||
nondisruptive=nondisruptive,
|
||||
new_share_network_id=new_share_network_id)
|
||||
new_share_network_id=new_share_network_id,
|
||||
new_share_type_id=new_share_type_id)
|
||||
|
||||
def connection_get_info(self, context, share_instance):
|
||||
new_host = utils.extract_host(share_instance['host'])
|
||||
|
@ -287,6 +287,7 @@ class ShareAPITest(test.TestCase):
|
||||
def test_migration_start(self):
|
||||
share = db_utils.create_share()
|
||||
share_network = db_utils.create_share_network()
|
||||
share_type = {'share_type_id': 'fake_type_id'}
|
||||
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
|
||||
use_admin_context=True, version='2.22')
|
||||
req.method = 'POST'
|
||||
@ -296,10 +297,14 @@ class ShareAPITest(test.TestCase):
|
||||
|
||||
self.mock_object(db, 'share_network_get', mock.Mock(
|
||||
return_value=share_network))
|
||||
self.mock_object(db, 'share_type_get', mock.Mock(
|
||||
return_value=share_type))
|
||||
|
||||
body = {
|
||||
'migration_start': {
|
||||
'host': 'fake_host',
|
||||
'new_share_network_id': 'fake_net_id',
|
||||
'new_share_type_id': 'fake_type_id',
|
||||
}
|
||||
}
|
||||
method = 'migration_start'
|
||||
@ -310,12 +315,15 @@ class ShareAPITest(test.TestCase):
|
||||
response = getattr(self.controller, method)(req, share['id'], body)
|
||||
|
||||
self.assertEqual(202, response.status_int)
|
||||
|
||||
share_api.API.get.assert_called_once_with(context, share['id'])
|
||||
share_api.API.migration_start.assert_called_once_with(
|
||||
context, share, 'fake_host', False, True, True, False,
|
||||
new_share_network=share_network)
|
||||
new_share_network=share_network, new_share_type=share_type)
|
||||
db.share_network_get.assert_called_once_with(
|
||||
context, 'fake_net_id')
|
||||
db.share_type_get.assert_called_once_with(
|
||||
context, 'fake_type_id')
|
||||
|
||||
def test_migration_start_has_replicas(self):
|
||||
share = db_utils.create_share()
|
||||
@ -378,11 +386,30 @@ class ShareAPITest(test.TestCase):
|
||||
|
||||
self.mock_object(db, 'share_network_get',
|
||||
mock.Mock(side_effect=exception.NotFound()))
|
||||
self.assertRaises(webob.exc.HTTPNotFound,
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.migration_start,
|
||||
req, share['id'], body)
|
||||
db.share_network_get.assert_called_once_with(context, 'nonexistent')
|
||||
|
||||
def test_migration_start_new_share_type_not_found(self):
|
||||
share = db_utils.create_share()
|
||||
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
|
||||
use_admin_context=True, version='2.22')
|
||||
context = req.environ['manila.context']
|
||||
req.method = 'POST'
|
||||
req.headers['content-type'] = 'application/json'
|
||||
req.api_version_request.experimental = True
|
||||
|
||||
body = {'migration_start': {'host': 'fake_host',
|
||||
'new_share_type_id': 'nonexistent'}}
|
||||
|
||||
self.mock_object(db, 'share_type_get',
|
||||
mock.Mock(side_effect=exception.NotFound()))
|
||||
self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.controller.migration_start,
|
||||
req, share['id'], body)
|
||||
db.share_type_get.assert_called_once_with(context, 'nonexistent')
|
||||
|
||||
def test_migration_start_invalid_force_host_assisted_migration(self):
|
||||
share = db_utils.create_share()
|
||||
req = fakes.HTTPRequest.blank('/shares/%s/action' % share['id'],
|
||||
|
@ -984,3 +984,100 @@ class AddAccessKeyToShareAccessMapping(BaseMigrationChecks):
|
||||
for row in rows:
|
||||
self.test_case.assertFalse(hasattr(row,
|
||||
self.access_key_column_name))
|
||||
|
||||
|
||||
@map_to_migration('48a7beae3117')
|
||||
class MoveShareTypeIdToInstancesCheck(BaseMigrationChecks):
|
||||
|
||||
some_shares = [
|
||||
{
|
||||
'id': 's1',
|
||||
'share_type_id': 't1',
|
||||
},
|
||||
{
|
||||
'id': 's2',
|
||||
'share_type_id': 't2',
|
||||
},
|
||||
{
|
||||
'id': 's3',
|
||||
'share_type_id': 't3',
|
||||
},
|
||||
]
|
||||
|
||||
share_ids = [x['id'] for x in some_shares]
|
||||
|
||||
some_instances = [
|
||||
{
|
||||
'id': 'i1',
|
||||
'share_id': 's3',
|
||||
},
|
||||
{
|
||||
'id': 'i2',
|
||||
'share_id': 's2',
|
||||
},
|
||||
{
|
||||
'id': 'i3',
|
||||
'share_id': 's2',
|
||||
},
|
||||
{
|
||||
'id': 'i4',
|
||||
'share_id': 's1',
|
||||
},
|
||||
]
|
||||
|
||||
instance_ids = [x['id'] for x in some_instances]
|
||||
|
||||
some_share_types = [
|
||||
{'id': 't1'},
|
||||
{'id': 't2'},
|
||||
{'id': 't3'},
|
||||
]
|
||||
|
||||
def setup_upgrade_data(self, engine):
|
||||
|
||||
shares_table = utils.load_table('shares', engine)
|
||||
share_instances_table = utils.load_table('share_instances', engine)
|
||||
share_types_table = utils.load_table('share_types', engine)
|
||||
|
||||
for stype in self.some_share_types:
|
||||
engine.execute(share_types_table.insert(stype))
|
||||
|
||||
for share in self.some_shares:
|
||||
engine.execute(shares_table.insert(share))
|
||||
|
||||
for instance in self.some_instances:
|
||||
engine.execute(share_instances_table.insert(instance))
|
||||
|
||||
def check_upgrade(self, engine, data):
|
||||
|
||||
shares_table = utils.load_table('shares', engine)
|
||||
share_instances_table = utils.load_table('share_instances', engine)
|
||||
|
||||
for instance in engine.execute(share_instances_table.select().where(
|
||||
share_instances_table.c.id in self.instance_ids)):
|
||||
share = engine.execute(shares_table.select().where(
|
||||
instance['share_id'] == shares_table.c.id)).first()
|
||||
self.test_case.assertEqual(
|
||||
next((x for x in self.some_shares if share['id'] == x['id']),
|
||||
None)['share_type_id'],
|
||||
instance['share_type_id'])
|
||||
|
||||
for share in engine.execute(share_instances_table.select().where(
|
||||
shares_table.c.id in self.share_ids)):
|
||||
self.test_case.assertNotIn('share_type_id', share)
|
||||
|
||||
def check_downgrade(self, engine):
|
||||
|
||||
shares_table = utils.load_table('shares', engine)
|
||||
share_instances_table = utils.load_table('share_instances', engine)
|
||||
|
||||
for instance in engine.execute(share_instances_table.select().where(
|
||||
share_instances_table.c.id in self.instance_ids)):
|
||||
self.test_case.assertNotIn('share_type_id', instance)
|
||||
|
||||
for share in engine.execute(share_instances_table.select().where(
|
||||
shares_table.c.id in self.share_ids)):
|
||||
self.test_case.assertEqual(
|
||||
next((x for x in self.some_shares if share['id'] == x['id']),
|
||||
None)['share_type_id'],
|
||||
share['share_type_id'])
|
||||
|
@ -233,15 +233,15 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
|
||||
self.assertRaises(
|
||||
TypeError, self.manager.migrate_share_to_host,
|
||||
self.context, share['id'], 'fake@backend#pool', False, True,
|
||||
True, False, 'fake_net_id', {}, None)
|
||||
self.context, share['id'], 'fake@backend#pool', False, True, True,
|
||||
False, 'fake_net_id', 'fake_type_id', {}, None)
|
||||
|
||||
db.share_get.assert_called_once_with(self.context, share['id'])
|
||||
base.Scheduler.host_passes_filters.assert_called_once_with(
|
||||
self.context, 'fake@backend#pool', {}, None)
|
||||
share_rpcapi.ShareAPI.migration_start.assert_called_once_with(
|
||||
self.context, share, host.host, False, True, True, False,
|
||||
'fake_net_id')
|
||||
'fake_net_id', 'fake_type_id')
|
||||
|
||||
@ddt.data(exception.NoValidHost(reason='fake'), TypeError)
|
||||
def test_migrate_share_to_host_exception(self, exc):
|
||||
@ -263,7 +263,7 @@ class SchedulerManagerTestCase(test.TestCase):
|
||||
self.assertRaises(
|
||||
capture, self.manager.migrate_share_to_host,
|
||||
self.context, share['id'], host, False, True, True, False,
|
||||
'fake_net_id', request_spec, None)
|
||||
'fake_net_id', 'fake_type_id', request_spec, None)
|
||||
|
||||
base.Scheduler.host_passes_filters.assert_called_once_with(
|
||||
self.context, host, request_spec, None)
|
||||
|
@ -110,7 +110,8 @@ class SchedulerRpcAPITestCase(test.TestCase):
|
||||
preserve_metadata=True,
|
||||
writable=True,
|
||||
nondisruptive=False,
|
||||
new_share_network_id='fake_id',
|
||||
new_share_network_id='fake_net_id',
|
||||
new_share_type_id='fake_type_id',
|
||||
request_spec='fake_request_spec',
|
||||
filter_properties='filter_properties',
|
||||
version='1.4')
|
||||
|
@ -214,9 +214,10 @@ class ShareAPITestCase(test.TestCase):
|
||||
user_id=self.context.user_id,
|
||||
project_id=self.context.project_id,
|
||||
create_share_instance=False,
|
||||
share_type_id=share_type_id,
|
||||
)
|
||||
share_instance = db_utils.create_share_instance(share_id=share['id'])
|
||||
share_instance = db_utils.create_share_instance(
|
||||
share_id=share['id'],
|
||||
share_type_id=share_type_id)
|
||||
share_type = {'fake': 'fake'}
|
||||
self.mock_object(db_api, 'share_instance_create',
|
||||
mock.Mock(return_value=share_instance))
|
||||
@ -253,7 +254,6 @@ class ShareAPITestCase(test.TestCase):
|
||||
|
||||
share, share_data = self._setup_create_mocks(
|
||||
snapshot_id=snapshot['id'], share_type_id=share_type['id'])
|
||||
|
||||
request_spec = {
|
||||
'share_properties': share.to_dict(),
|
||||
'share_proto': share['share_proto'],
|
||||
@ -694,7 +694,8 @@ class ShareAPITestCase(test.TestCase):
|
||||
host, share, share_instance = self._setup_create_instance_mocks()
|
||||
|
||||
self.api.create_instance(self.context, share, host=host,
|
||||
availability_zone='fake')
|
||||
availability_zone='fake',
|
||||
share_type_id='fake_share_type')
|
||||
|
||||
db_api.share_instance_create.assert_called_once_with(
|
||||
self.context, share['id'],
|
||||
@ -704,10 +705,11 @@ class ShareAPITestCase(test.TestCase):
|
||||
'scheduled_at': self.dt_utc,
|
||||
'host': host,
|
||||
'availability_zone_id': 'fake_id',
|
||||
'share_type_id': 'fake_share_type',
|
||||
}
|
||||
)
|
||||
db_api.share_type_get.assert_called_once_with(self.context,
|
||||
share['share_type_id'])
|
||||
db_api.share_type_get.assert_called_once_with(
|
||||
self.context, share_instance['share_type_id'])
|
||||
self.api.share_rpcapi.create_share_instance.assert_called_once_with(
|
||||
self.context,
|
||||
share_instance,
|
||||
@ -1310,7 +1312,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
db_api.share_create.call_args[0][1])
|
||||
self.api.create_instance.assert_called_once_with(
|
||||
self.context, share, share_network_id=share['share_network_id'],
|
||||
host=valid_host,
|
||||
host=valid_host, share_type_id=share_type['id'],
|
||||
availability_zone=snapshot['share']['availability_zone'],
|
||||
consistency_group=None, cgsnapshot_member=None)
|
||||
share_api.policy.check_policy.assert_has_calls([
|
||||
@ -2010,25 +2012,48 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.context, share, new_size
|
||||
)
|
||||
|
||||
def test_migration_start(self):
|
||||
@ddt.data({'share_type': True, 'share_net': True, 'dhss': True},
|
||||
{'share_type': False, 'share_net': True, 'dhss': True},
|
||||
{'share_type': False, 'share_net': False, 'dhss': True},
|
||||
{'share_type': True, 'share_net': False, 'dhss': False},
|
||||
{'share_type': False, 'share_net': False, 'dhss': False})
|
||||
@ddt.unpack
|
||||
def test_migration_start(self, share_type, share_net, dhss):
|
||||
host = 'fake2@backend#pool'
|
||||
service = {'availability_zone_id': 'fake_az_id'}
|
||||
share_network = db_utils.create_share_network(id='fake_net_id')
|
||||
share_network = None
|
||||
share_network_id = None
|
||||
if share_net:
|
||||
share_network = db_utils.create_share_network(id='fake_net_id')
|
||||
share_network_id = share_network['id']
|
||||
|
||||
fake_type = {
|
||||
'id': 'fake_type_id',
|
||||
'extra_specs': {
|
||||
'snapshot_support': False,
|
||||
'driver_handles_share_servers': dhss,
|
||||
},
|
||||
}
|
||||
|
||||
if share_type:
|
||||
fake_type_2 = {
|
||||
'id': 'fake_type_2_id',
|
||||
'extra_specs': {
|
||||
'snapshot_support': False,
|
||||
'driver_handles_share_servers': dhss,
|
||||
},
|
||||
}
|
||||
else:
|
||||
fake_type_2 = fake_type
|
||||
|
||||
share = db_utils.create_share(
|
||||
status=constants.STATUS_AVAILABLE,
|
||||
host='fake@backend#pool', share_type_id=fake_type['id'])
|
||||
host='fake@backend#pool', share_type_id=fake_type['id'],
|
||||
share_network_id=share_network_id)
|
||||
|
||||
request_spec = self._get_request_spec_dict(
|
||||
share, fake_type, size=0, availability_zone_id='fake_az_id',
|
||||
share_network_id='fake_net_id')
|
||||
share, fake_type_2, size=0, availability_zone_id='fake_az_id',
|
||||
share_network_id=share_network_id)
|
||||
|
||||
self.mock_object(self.scheduler_rpcapi, 'migrate_share_to_host')
|
||||
self.mock_object(share_types, 'get_share_type',
|
||||
@ -2039,14 +2064,19 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.mock_object(db_api, 'service_get_by_args',
|
||||
mock.Mock(return_value=service))
|
||||
|
||||
self.api.migration_start(self.context, share, host, True, True,
|
||||
True, True, share_network)
|
||||
if share_type:
|
||||
self.api.migration_start(self.context, share, host, True, True,
|
||||
True, True, share_network, fake_type_2)
|
||||
else:
|
||||
self.api.migration_start(self.context, share, host, True, True,
|
||||
True, True, share_network, None)
|
||||
|
||||
self.scheduler_rpcapi.migrate_share_to_host.assert_called_once_with(
|
||||
self.context, share['id'], host, True, True, True, True,
|
||||
'fake_net_id', request_spec)
|
||||
share_types.get_share_type.assert_called_once_with(
|
||||
self.context, fake_type['id'])
|
||||
share_network_id, fake_type_2['id'], request_spec)
|
||||
if not share_type:
|
||||
share_types.get_share_type.assert_called_once_with(
|
||||
self.context, fake_type['id'])
|
||||
utils.validate_service_host.assert_called_once_with(
|
||||
self.context, 'fake2@backend')
|
||||
db_api.service_get_by_args.assert_called_once_with(
|
||||
@ -2058,6 +2088,47 @@ class ShareAPITestCase(test.TestCase):
|
||||
self.context, share.instance['id'],
|
||||
{'status': constants.STATUS_MIGRATING})
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_migration_start_invalid_share_network_type_combo(self, dhss):
|
||||
host = 'fake2@backend#pool'
|
||||
service = {'availability_zone_id': 'fake_az_id'}
|
||||
share_network = None
|
||||
if not dhss:
|
||||
share_network = db_utils.create_share_network(id='fake_net_id')
|
||||
|
||||
fake_type = {
|
||||
'id': 'fake_type_id',
|
||||
'extra_specs': {
|
||||
'snapshot_support': False,
|
||||
'driver_handles_share_servers': not dhss,
|
||||
},
|
||||
}
|
||||
|
||||
fake_type_2 = {
|
||||
'id': 'fake_type_2_id',
|
||||
'extra_specs': {
|
||||
'snapshot_support': False,
|
||||
'driver_handles_share_servers': dhss,
|
||||
},
|
||||
}
|
||||
|
||||
share = db_utils.create_share(
|
||||
status=constants.STATUS_AVAILABLE,
|
||||
host='fake@backend#pool', share_type_id=fake_type['id'])
|
||||
|
||||
self.mock_object(utils, 'validate_service_host')
|
||||
self.mock_object(db_api, 'service_get_by_args',
|
||||
mock.Mock(return_value=service))
|
||||
|
||||
self.assertRaises(
|
||||
exception.InvalidInput, self.api.migration_start, self.context,
|
||||
share, host, True, True, True, True, share_network, fake_type_2)
|
||||
|
||||
utils.validate_service_host.assert_called_once_with(
|
||||
self.context, 'fake2@backend')
|
||||
db_api.service_get_by_args.assert_called_once_with(
|
||||
self.context, 'fake2@backend', 'manila-share')
|
||||
|
||||
def test_migration_start_status_unavailable(self):
|
||||
host = 'fake2@backend#pool'
|
||||
share = db_utils.create_share(
|
||||
@ -2186,7 +2257,7 @@ class ShareAPITestCase(test.TestCase):
|
||||
def test_create_share_replica(self, has_snapshots):
|
||||
request_spec = fakes.fake_replica_request_spec()
|
||||
replica = request_spec['share_instance_properties']
|
||||
share = fakes.fake_share(
|
||||
share = db_utils.create_share(
|
||||
id=replica['share_id'], replication_type='dr')
|
||||
snapshots = (
|
||||
[fakes.fake_snapshot(), fakes.fake_snapshot()]
|
||||
|
@ -3645,7 +3645,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
# run
|
||||
self.share_manager.migration_start(
|
||||
self.context, 'fake_id', host, False, False, False, False,
|
||||
'fake_net_id')
|
||||
'fake_net_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
self.share_manager.db.share_get.assert_called_once_with(
|
||||
@ -3667,12 +3667,12 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.share_manager.db.share_update.assert_has_calls(share_update_calls)
|
||||
self.share_manager._migration_start_driver.assert_called_once_with(
|
||||
self.context, share, instance, host, False, False, False,
|
||||
'fake_net_id', 'fake_az_id')
|
||||
'fake_net_id', 'fake_az_id', 'fake_type_id')
|
||||
if not success:
|
||||
(self.share_manager._migration_start_host_assisted.
|
||||
assert_called_once_with(
|
||||
self.context, share, instance, host, 'fake_net_id',
|
||||
'fake_az_id'))
|
||||
'fake_az_id', 'fake_type_id'))
|
||||
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||
self.context, 'fake2@backend', 'manila-share')
|
||||
|
||||
@ -3743,7 +3743,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
exception.ShareMigrationFailed,
|
||||
self.share_manager.migration_start,
|
||||
self.context, 'fake_id', host, False, False, False, False,
|
||||
'fake_net_id')
|
||||
'fake_net_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
self.share_manager.db.share_get.assert_called_once_with(
|
||||
@ -3766,7 +3766,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
{'status': constants.STATUS_AVAILABLE})
|
||||
self.share_manager._migration_start_driver.assert_called_once_with(
|
||||
self.context, share, instance, host, False, False, False,
|
||||
'fake_net_id', 'fake_az_id')
|
||||
'fake_net_id', 'fake_az_id', 'fake_type_id')
|
||||
self.share_manager.db.service_get_by_args.assert_called_once_with(
|
||||
self.context, 'fake2@backend', 'manila-share')
|
||||
|
||||
@ -3815,7 +3815,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
exception.ShareMigrationFailed,
|
||||
self.share_manager._migration_start_host_assisted,
|
||||
self.context, share, instance, 'fake_host', 'fake_net_id',
|
||||
'fake_az_id')
|
||||
'fake_az_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
self.share_manager.db.share_server_get.assert_called_once_with(
|
||||
@ -3827,7 +3827,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.share_manager.driver)
|
||||
migration_api.ShareMigrationHelper.create_instance_and_wait.\
|
||||
assert_called_once_with(share, 'fake_host', 'fake_net_id',
|
||||
'fake_az_id')
|
||||
'fake_az_id', 'fake_type_id')
|
||||
migration_api.ShareMigrationHelper.\
|
||||
cleanup_access_rules.assert_called_once_with(
|
||||
instance, server, self.share_manager.driver)
|
||||
@ -3904,11 +3904,11 @@ class ShareManagerTestCase(test.TestCase):
|
||||
exception.ShareMigrationFailed,
|
||||
self.share_manager._migration_start_driver,
|
||||
self.context, share, src_instance, fake_dest_host, False,
|
||||
False, False, share_network_id, 'fake_az_id')
|
||||
False, False, share_network_id, 'fake_az_id', 'fake_type_id')
|
||||
else:
|
||||
result = self.share_manager._migration_start_driver(
|
||||
self.context, share, src_instance, fake_dest_host, False,
|
||||
False, False, share_network_id, 'fake_az_id')
|
||||
False, False, share_network_id, 'fake_az_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
if not exc:
|
||||
@ -3936,7 +3936,8 @@ class ShareManagerTestCase(test.TestCase):
|
||||
self.context, 'fake_src_server_id')
|
||||
(api.API.create_share_instance_and_get_request_spec.
|
||||
assert_called_once_with(self.context, share, 'fake_az_id', None,
|
||||
'fake_host', share_network_id))
|
||||
'fake_host', share_network_id,
|
||||
'fake_type_id'))
|
||||
(self.share_manager.driver.migration_check_compatibility.
|
||||
assert_called_once_with(self.context, src_instance,
|
||||
migrating_instance, src_server, dest_server))
|
||||
@ -4011,7 +4012,7 @@ class ShareManagerTestCase(test.TestCase):
|
||||
exception.ShareMigrationFailed,
|
||||
self.share_manager._migration_start_driver,
|
||||
self.context, share, src_instance, fake_dest_host, True, True,
|
||||
True, 'fake_net_id', 'fake_az_id')
|
||||
True, 'fake_net_id', 'fake_az_id', 'fake_new_type_id')
|
||||
|
||||
# asserts
|
||||
self.share_manager.db.share_server_get.assert_called_once_with(
|
||||
@ -4027,7 +4028,8 @@ class ShareManagerTestCase(test.TestCase):
|
||||
assert_called_once_with('fake_dest_share_server_id'))
|
||||
(api.API.create_share_instance_and_get_request_spec.
|
||||
assert_called_once_with(self.context, share, 'fake_az_id', None,
|
||||
'fake_host', 'fake_net_id'))
|
||||
'fake_host', 'fake_net_id',
|
||||
'fake_new_type_id'))
|
||||
self.share_manager._migration_delete_instance.assert_called_once_with(
|
||||
self.context, migrating_instance['id'])
|
||||
|
||||
|
@ -132,11 +132,12 @@ class ShareMigrationHelperTestCase(test.TestCase):
|
||||
|
||||
# run
|
||||
self.helper.create_instance_and_wait(
|
||||
self.share, host, 'fake_net_id', 'fake_az_id')
|
||||
self.share, host, 'fake_net_id', 'fake_az_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
share_api.API.create_instance.assert_called_once_with(
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id')
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id',
|
||||
share_type_id='fake_type_id')
|
||||
|
||||
db.share_instance_get.assert_has_calls([
|
||||
mock.call(self.context, share_instance_creating['id'],
|
||||
@ -165,11 +166,12 @@ class ShareMigrationHelperTestCase(test.TestCase):
|
||||
self.assertRaises(
|
||||
exception.ShareMigrationFailed,
|
||||
self.helper.create_instance_and_wait, self.share,
|
||||
host, 'fake_net_id', 'fake_az_id')
|
||||
host, 'fake_net_id', 'fake_az_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
share_api.API.create_instance.assert_called_once_with(
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id')
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id',
|
||||
share_type_id='fake_type_id')
|
||||
|
||||
db.share_instance_get.assert_called_once_with(
|
||||
self.context, share_instance_error['id'], with_share_data=True)
|
||||
@ -203,12 +205,13 @@ class ShareMigrationHelperTestCase(test.TestCase):
|
||||
# run
|
||||
self.assertRaises(
|
||||
exception.ShareMigrationFailed,
|
||||
self.helper.create_instance_and_wait, self.share,
|
||||
host, 'fake_net_id', 'fake_az_id')
|
||||
self.helper.create_instance_and_wait, self.share,
|
||||
host, 'fake_net_id', 'fake_az_id', 'fake_type_id')
|
||||
|
||||
# asserts
|
||||
share_api.API.create_instance.assert_called_once_with(
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id')
|
||||
self.context, self.share, 'fake_net_id', 'fake_host', 'fake_az_id',
|
||||
share_type_id='fake_type_id')
|
||||
|
||||
db.share_instance_get.assert_called_once_with(
|
||||
self.context, share_instance_creating['id'], with_share_data=True)
|
||||
|
@ -261,7 +261,8 @@ class ShareRpcAPITestCase(test.TestCase):
|
||||
preserve_metadata=True,
|
||||
writable=True,
|
||||
nondisruptive=False,
|
||||
new_share_network_id='fake_id')
|
||||
new_share_network_id='fake_net_id',
|
||||
new_share_type_id='fake_type_id')
|
||||
|
||||
def test_connection_get_info(self):
|
||||
self._test_share_api('connection_get_info',
|
||||
|
@ -1021,13 +1021,14 @@ class SharesV2Client(shares_client.SharesClient):
|
||||
force_host_assisted_migration=False,
|
||||
new_share_network_id=None, writable=False,
|
||||
preserve_metadata=False, nondisruptive=False,
|
||||
version=LATEST_MICROVERSION):
|
||||
new_share_type_id=None, version=LATEST_MICROVERSION):
|
||||
|
||||
body = {
|
||||
'migration_start': {
|
||||
'host': host,
|
||||
'force_host_assisted_migration': force_host_assisted_migration,
|
||||
'new_share_network_id': new_share_network_id,
|
||||
'new_share_type_id': new_share_type_id,
|
||||
'writable': writable,
|
||||
'preserve_metadata': preserve_metadata,
|
||||
'nondisruptive': nondisruptive,
|
||||
|
@ -13,8 +13,11 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
import ddt
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest import test
|
||||
|
||||
from manila_tempest_tests.common import constants
|
||||
@ -59,6 +62,18 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
CONF.share.run_driver_assisted_migration_tests):
|
||||
raise cls.skipException("Share migration tests are disabled.")
|
||||
|
||||
extra_specs = {
|
||||
'storage_protocol': CONF.share.capability_storage_protocol,
|
||||
'driver_handles_share_servers': (
|
||||
CONF.share.multitenancy_enabled),
|
||||
'snapshot_support': six.text_type(
|
||||
CONF.share.capability_snapshot_support),
|
||||
}
|
||||
cls.new_type = cls.create_share_type(
|
||||
name=data_utils.rand_name('new_share_type_for_migration'),
|
||||
cleanup_in_class=True,
|
||||
extra_specs=extra_specs)
|
||||
|
||||
@test.attr(type=[base.TAG_POSITIVE, base.TAG_BACKEND])
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
@ddt.data(True, False)
|
||||
@ -115,16 +130,19 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
old_share_network_id = share['share_network_id']
|
||||
new_share_network_id = self._create_secondary_share_network(
|
||||
old_share_network_id)
|
||||
old_share_type_id = share['share_type']
|
||||
new_share_type_id = self.new_type['share_type']['id']
|
||||
|
||||
share = self.migrate_share(
|
||||
share['id'], dest_pool,
|
||||
force_host_assisted_migration=force_host_assisted,
|
||||
wait_for_status=task_state,
|
||||
wait_for_status=task_state, new_share_type_id=new_share_type_id,
|
||||
new_share_network_id=new_share_network_id)
|
||||
|
||||
self._validate_migration_successful(
|
||||
dest_pool, share, task_state,
|
||||
complete=False, share_network_id=old_share_network_id)
|
||||
dest_pool, share, task_state, complete=False,
|
||||
share_network_id=old_share_network_id,
|
||||
share_type_id=old_share_type_id)
|
||||
|
||||
progress = self.shares_v2_client.migration_get_progress(share['id'])
|
||||
|
||||
@ -135,7 +153,8 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
|
||||
self._validate_migration_successful(
|
||||
dest_pool, share, constants.TASK_STATE_MIGRATION_SUCCESS,
|
||||
complete=True, share_network_id=new_share_network_id)
|
||||
complete=True, share_network_id=new_share_network_id,
|
||||
share_type_id=new_share_type_id)
|
||||
|
||||
def _setup_migration(self):
|
||||
|
||||
@ -146,7 +165,7 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
"needed to run share migration tests.")
|
||||
|
||||
share = self.create_share(self.protocol)
|
||||
share = self.shares_client.get_share(share['id'])
|
||||
share = self.shares_v2_client.get_share(share['id'])
|
||||
|
||||
self.shares_v2_client.create_access_rule(
|
||||
share['id'], access_to="50.50.50.50", access_level="rw")
|
||||
@ -174,7 +193,8 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
|
||||
def _validate_migration_successful(self, dest_pool, share, status_to_wait,
|
||||
version=CONF.share.max_api_microversion,
|
||||
complete=True, share_network_id=None):
|
||||
complete=True, share_network_id=None,
|
||||
share_type_id=None):
|
||||
|
||||
statuses = ((status_to_wait,)
|
||||
if not isinstance(status_to_wait, (tuple, list, set))
|
||||
@ -190,6 +210,8 @@ class MigrationNFSTest(base.BaseSharesAdminTest):
|
||||
self.assertIn(share['task_state'], statuses)
|
||||
if share_network_id:
|
||||
self.assertEqual(share_network_id, share['share_network_id'])
|
||||
if share_type_id:
|
||||
self.assertEqual(share_type_id, share['share_type'])
|
||||
|
||||
# Share migrated
|
||||
if complete:
|
||||
|
@ -13,7 +13,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import six
|
||||
|
||||
from tempest import config
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
from tempest import test
|
||||
import testtools
|
||||
@ -65,6 +68,18 @@ class MigrationTest(base.BaseSharesAdminTest):
|
||||
|
||||
cls.dest_pool = dest_pool['name']
|
||||
|
||||
extra_specs = {
|
||||
'storage_protocol': CONF.share.capability_storage_protocol,
|
||||
'driver_handles_share_servers': CONF.share.multitenancy_enabled,
|
||||
'snapshot_support': six.text_type(
|
||||
not CONF.share.capability_snapshot_support),
|
||||
}
|
||||
cls.new_type = cls.create_share_type(
|
||||
name=data_utils.rand_name(
|
||||
'new_invalid_share_type_for_migration'),
|
||||
cleanup_in_class=True,
|
||||
extra_specs=extra_specs)
|
||||
|
||||
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
def test_migration_cancel_invalid(self):
|
||||
@ -144,7 +159,18 @@ class MigrationTest(base.BaseSharesAdminTest):
|
||||
force_host_assisted_migration=True, writable=True,
|
||||
preserve_metadata=True)
|
||||
self.shares_v2_client.wait_for_migration_status(
|
||||
self.share['id'], self.dest_pool, 'migration_error')
|
||||
self.share['id'], self.dest_pool,
|
||||
constants.TASK_STATE_MIGRATION_ERROR)
|
||||
|
||||
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
def test_migrate_share_change_type_no_valid_host(self):
|
||||
self.shares_v2_client.migrate_share(
|
||||
self.share['id'], self.dest_pool,
|
||||
new_share_type_id=self.new_type['share_type']['id'])
|
||||
self.shares_v2_client.wait_for_migration_status(
|
||||
self.share['id'], self.dest_pool,
|
||||
constants.TASK_STATE_MIGRATION_ERROR)
|
||||
|
||||
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
@ -172,6 +198,14 @@ class MigrationTest(base.BaseSharesAdminTest):
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
def test_migrate_share_invalid_share_network(self):
|
||||
self.assertRaises(
|
||||
lib_exc.NotFound, self.shares_v2_client.migrate_share,
|
||||
lib_exc.BadRequest, self.shares_v2_client.migrate_share,
|
||||
self.share['id'], self.dest_pool,
|
||||
new_share_network_id='invalid_net_id')
|
||||
|
||||
@test.attr(type=[base.TAG_NEGATIVE, base.TAG_API_WITH_BACKEND])
|
||||
@base.skip_if_microversion_lt("2.22")
|
||||
def test_migrate_share_invalid_share_type(self):
|
||||
self.assertRaises(
|
||||
lib_exc.BadRequest, self.shares_v2_client.migrate_share,
|
||||
self.share['id'], self.dest_pool, True,
|
||||
new_share_type_id='invalid_type_id')
|
||||
|
@ -404,14 +404,14 @@ class BaseSharesTest(test.BaseTestCase):
|
||||
def migrate_share(
|
||||
cls, share_id, dest_host, wait_for_status, client=None,
|
||||
force_host_assisted_migration=False, new_share_network_id=None,
|
||||
**kwargs):
|
||||
new_share_type_id=None, **kwargs):
|
||||
client = client or cls.shares_v2_client
|
||||
client.migrate_share(
|
||||
share_id, dest_host,
|
||||
force_host_assisted_migration=force_host_assisted_migration,
|
||||
new_share_network_id=new_share_network_id,
|
||||
writable=False, preserve_metadata=False, nondisruptive=False,
|
||||
**kwargs)
|
||||
new_share_type_id=new_share_type_id, **kwargs)
|
||||
share = client.wait_for_migration_status(
|
||||
share_id, dest_host, wait_for_status, **kwargs)
|
||||
return share
|
||||
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Administrators can now change a share's type during a migration.
|
Loading…
Reference in New Issue
Block a user