Share server migration enhancements

A series of enhancements can be performed in the share server
migration operation, and share backends might support
nondisruptive share server migrations, which was not covered
in the previous approach.

So in this change, we enhance the operation by:
- Added the `is_ip_reusage_supported_on_server_migration`
interface, so we can ask drivers if they are capable of reusing
network allocations.

- Added the `server_migration_mechanism_can_reuse_share_server`
driver interface, so share backends can define if they need new
share servers being created in the backend to serve as destination.

-  Skip the destination share server network allocation if the
driver is capable of reusing the source share server allocation.
Manila will switch the allocations in the migration complete phase.

- Added a new parameter to the API called `cleanup_source_server`,
which will be used by manila to check if the source share server
must be deleted right after the migration is completed.

Co-Authored-By: Fabio Oliveira <fabioaurelio1269@gmail.com>
Change-Id: I48bafd92fe7a4d4ae0bafd5bf1961dace56b6005
This commit is contained in:
silvacarloss 2021-06-03 09:19:53 -03:00
parent ca9213ef40
commit 680b9f659e
18 changed files with 697 additions and 143 deletions

View File

@ -171,13 +171,15 @@ REST_API_VERSION_HISTORY = """
actions on the share network's endpoint:
'update_security_service', 'update_security_service_check' and
'add_security_service_check'.
* 2.64 - Added 'cleanup_source_server' to share server migration start
operation.
"""
# The minimum and maximum versions of the API supported
# The default api version request is defined to be the
# minimum version of the API supported.
_MIN_API_VERSION = "2.0"
_MAX_API_VERSION = "2.63"
_MAX_API_VERSION = "2.64"
DEFAULT_API_VERSION = _MIN_API_VERSION

View File

@ -353,3 +353,9 @@ user documentation.
endpoint: 'update_security_service', 'update_security_service_check' and
'add_security_service_check'.
2.64
----
Added 'cleanup_source_server' to share server migration start operation, so
the share drivers can automatically remove the source share server when
requested.

View File

@ -215,12 +215,8 @@ class ShareServerController(share_servers.ShareServerController,
return identifier, host, share_network, driver_opts, network_subnet
@wsgi.Controller.api_version('2.57', experimental=True)
@wsgi.action("migration_start")
@wsgi.Controller.authorize
@wsgi.response(http_client.ACCEPTED)
def share_server_migration_start(self, req, id, body):
"""Migrate a share server to the specified host."""
def _share_server_migration_start(
self, req, id, body, cleanup_source_server=False):
context = req.environ['manila.context']
try:
share_server = db_api.share_server_get(
@ -233,7 +229,10 @@ class ShareServerController(share_servers.ShareServerController,
if not params:
raise exc.HTTPBadRequest(explanation=_("Request is missing body."))
bool_params = ['writable', 'nondisruptive', 'preserve_snapshots']
params['cleanup_source_server'] = cleanup_source_server
bool_params = ['writable', 'nondisruptive', 'preserve_snapshots',
'cleanup_source_server']
mandatory_params = bool_params + ['host']
utils.check_params_exist(mandatory_params, params)
@ -270,7 +269,8 @@ class ShareServerController(share_servers.ShareServerController,
bool_param_values['writable'],
bool_param_values['nondisruptive'],
bool_param_values['preserve_snapshots'],
new_share_network=new_share_network)
new_share_network=new_share_network,
cleanup_source_server=cleanup_source_server)
except exception.ServiceIsDown as e:
# NOTE(dviroel): user should check if the host is healthy
raise exc.HTTPBadRequest(explanation=e.msg)
@ -279,6 +279,28 @@ class ShareServerController(share_servers.ShareServerController,
# resource have a invalid state.
raise exc.HTTPConflict(explanation=e.msg)
@wsgi.Controller.api_version('2.57', '2.63', experimental=True)
@wsgi.action("migration_start")
@wsgi.Controller.authorize
@wsgi.response(http_client.ACCEPTED)
def share_server_migration_start(self, req, id, body):
"""Migrate a share server to the specified host."""
self._share_server_migration_start(req, id, body)
@wsgi.Controller.api_version('2.64', experimental=True) # noqa
@wsgi.action("migration_start")
@wsgi.Controller.authorize
@wsgi.response(http_client.ACCEPTED)
def share_server_migration_start(self, req, id, body): # pylint: disable=function-redefined # noqa F811
"""Migrate a share server to the specified host."""
body_params = body.get('migration_start')
cleanup_source_server = False
if body_params:
cleanup_source_server = body_params.get(
'cleanup_source_server', False)
self._share_server_migration_start(
req, id, body, cleanup_source_server=cleanup_source_server)
@wsgi.Controller.api_version('2.57', experimental=True)
@wsgi.action("migration_complete")
@wsgi.Controller.authorize

View File

@ -2579,7 +2579,8 @@ class API(base.Base):
def share_server_migration_start(
self, context, share_server, dest_host, writable, nondisruptive,
preserve_snapshots, new_share_network=None):
preserve_snapshots, new_share_network=None,
cleanup_source_server=False):
"""Migrates share server to a new host."""
shares, types, dest_service, new_share_network_id = (
@ -2614,7 +2615,7 @@ class API(base.Base):
# checks
self.share_rpcapi.share_server_migration_start(
context, share_server, dest_host, writable, nondisruptive,
preserve_snapshots, new_share_network_id)
preserve_snapshots, new_share_network_id, cleanup_source_server)
def share_server_migration_complete(self, context, share_server):
"""Invokes 2nd phase of share server migration."""

View File

@ -2888,7 +2888,8 @@ class ShareDriver(object):
raise NotImplementedError()
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server):
"""Starts migration of a given share server to another host.
.. note::
@ -2909,6 +2910,18 @@ class ShareDriver(object):
migrated.
:param snapshots: All snapshots in the source share server that should
be migrated.
:param cleanup_source_server: whether the backend should delete the
source share server right after the operation is completed or not.
:return: Dict with migration information to be set in the destination
share server.
Example::
{
'backend_details': {
'migration_info_key': 'migration_info_value',
}
}
"""
raise NotImplementedError()
@ -3312,3 +3325,29 @@ class ShareDriver(object):
otherwise.
"""
raise NotImplementedError()
def is_ip_reusage_supported_on_server_migration(
self, source_host, dest_host):
"""Check if network allocation can be reused on share migration.
If the driver supports reusing ip allocations during the share server
migration, manila will not allocate new addresses and reuse the
existing allocations for the destination share server.
:param source_host: The host from the share server to be migrated.
:param dest_host: The host where the share server will be migrated to.
"""
return False
def server_migration_mechanism_can_reuse_share_server(
self, source_host, dest_host):
"""Check if the share server migration mechanism can reuse the server.
If the driver does not need a new share server to be created, this
function must return False, then manila won't request a new share
server to be created in the share back end.
:param source_host: The host from the share server to be migrated.
:param dest_host: The host where the share server will be migrated to.
"""
return False

View File

@ -492,7 +492,8 @@ class ContainerShareDriver(driver.ShareDriver, driver.ExecuteMixin):
new_share_network, shares_request_spec)
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server):
"""Is called to perform 1st phase of migration of a share server."""
LOG.debug(
"Migration of share server with ID '%s' has been started.",

View File

@ -278,7 +278,8 @@ class LVMHelper(driver.ExecuteMixin):
}
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server=False):
"""Is called to perform 1st phase of migration of a share server."""
# NOTE(felipe_rodrigues): Since they are in the same volume group,

View File

@ -299,7 +299,8 @@ class NetAppCmodeMultiSvmShareDriver(driver.ShareDriver):
new_share_network, shares_request_spec)
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server=False):
self.library.share_server_migration_start(
context, src_share_server, dest_share_server, shares, snapshots)

View File

@ -285,7 +285,8 @@ class NetAppCmodeSingleSvmShareDriver(driver.ShareDriver):
return self.library.get_share_status(share_instance, share_server)
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server=False):
raise NotImplementedError
def share_server_migration_continue(self, context, src_share_server,

View File

@ -830,6 +830,8 @@ class ShareManager(manager.SchedulerDependentManager):
# request can use this metadata to take actions.
server_metadata['migration_destination'] = True
server_metadata['request_host'] = destination_host
server_metadata['source_share_server'] = (
source_share_server)
destination_share_server = (
self._create_share_server_in_backend(
context, destination_share_server,
@ -3957,13 +3959,14 @@ class ShareManager(manager.SchedulerDependentManager):
def _form_server_setup_info(self, context, share_server, share_network,
share_network_subnet):
share_server_id = share_server['id']
# Network info is used by driver for setting up share server
# and getting server info on share creation.
network_allocations = self.db.network_allocations_get_for_share_server(
context, share_server['id'], label='user')
context, share_server_id, label='user')
admin_network_allocations = (
self.db.network_allocations_get_for_share_server(
context, share_server['id'], label='admin'))
context, share_server_id, label='admin'))
# NOTE(vponomaryov): following network_info fields are deprecated:
# 'segmentation_id', 'cidr' and 'network_type'.
# And they should be used from network allocations directly.
@ -3982,22 +3985,78 @@ class ShareManager(manager.SchedulerDependentManager):
}
return network_info
def _get_setup_server_info_for_share_server_migration(
self, share_server, source_share_server):
"""Gets info to be used during the share server setup for migration"""
# Whether the share server migration mechanism is capable of reusing
# the same network allocations
driver_can_reuse_source_server_allocations_in_migration = (
self.driver.is_ip_reusage_supported_on_server_migration(
share_server['host'], source_share_server['host']
))
share_network_subnet_id = share_server['share_network_subnet']['id']
source_subnet_is_equal_to_dest = (
source_share_server['share_network_subnet_id'] ==
share_network_subnet_id)
# NOTE(carloss): Need to check if it is possible to reuse the network
# allocation. The driver being able to reuse the allocation in a
# migration scenario does not mean that it will in fact be reused. If
# a new share network was provided, there is no way to reuse the
# allocation.
can_reuse_network_allocations = (
driver_can_reuse_source_server_allocations_in_migration
and source_subnet_is_equal_to_dest)
need_new_network_allocations = can_reuse_network_allocations is False
# Whether the migration will be able to reuse the share server or not
migration_can_reuse_server = (
self.driver.server_migration_mechanism_can_reuse_share_server(
source_share_server['host'], share_server['host']))
return need_new_network_allocations, migration_can_reuse_server
def _setup_server(self, context, share_server, metadata):
try:
share_network_subnet = share_server['share_network_subnet']
share_network_subnet_id = share_network_subnet['id']
share_network_id = share_network_subnet['share_network_id']
need_new_network_allocation = True
setup_server_in_backend = True
share_network = self.db.share_network_get(
context, share_network_id)
self.driver.allocate_network(context, share_server, share_network,
share_network_subnet)
self.driver.allocate_admin_network(context, share_server)
is_migrating = metadata.get('migration_destination')
share_server_to_get_network_info = share_server
if is_migrating:
source_share_server = metadata.get('source_share_server')
need_new_network_allocation, migration_can_reuse_server = (
self._get_setup_server_info_for_share_server_migration(
share_server, source_share_server))
setup_server_in_backend = not migration_can_reuse_server
if not need_new_network_allocation:
share_server_to_get_network_info = source_share_server
# NOTE(carloss): In case the share driver supports reusing the
# network allocations in a migration scenario, we will keep these
# allocations only related to the source share server, and when the
# migration-complete command is triggered, we switch the
# allocations belonging to the source share server and make them
# related to the destination share server.
if need_new_network_allocation:
self.driver.allocate_network(
context, share_server, share_network, share_network_subnet)
self.driver.allocate_admin_network(context, share_server)
# Get share_network_subnet in case it was updated.
share_network_subnet = self.db.share_network_subnet_get(
context, share_network_subnet_id)
network_info = self._form_server_setup_info(
context, share_server, share_network, share_network_subnet)
context, share_server_to_get_network_info, share_network,
share_network_subnet)
self._validate_segmentation_id(network_info)
# NOTE(vponomaryov): Save security services data to share server
@ -4020,20 +4079,25 @@ class ShareManager(manager.SchedulerDependentManager):
context, share_server['id'],
{'security_service_' + ss_type: jsonutils.dumps(data)})
server_info = self.driver.setup_server(
network_info, metadata=metadata)
server_info = None
if setup_server_in_backend:
server_info = self.driver.setup_server(
network_info, metadata=metadata)
self.driver.update_network_allocation(context, share_server)
self.driver.update_admin_network_allocation(context, share_server)
if need_new_network_allocation:
self.driver.update_network_allocation(context, share_server)
self.driver.update_admin_network_allocation(
context, share_server)
identifier = share_server['id']
if server_info and isinstance(server_info, dict):
self.db.share_server_backend_details_set(
context, share_server['id'], server_info)
identifier = server_info.get('identifier', share_server['id'])
return self.db.share_server_update(
context, share_server['id'],
{'status': constants.STATUS_ACTIVE,
'identifier': server_info.get(
'identifier', share_server['id'])})
'identifier': identifier})
except Exception as e:
with excutils.save_and_reraise_exception():
details = getattr(e, "detail_data", {})
@ -4072,7 +4136,8 @@ class ShareManager(manager.SchedulerDependentManager):
self.db.share_server_update(
context, share_server['id'],
{'status': constants.STATUS_ERROR})
self.driver.deallocate_network(context, share_server['id'])
if need_new_network_allocation:
self.driver.deallocate_network(context, share_server['id'])
def _validate_segmentation_id(self, network_info):
"""Raises exception if the segmentation type is incorrect."""
@ -4964,7 +5029,8 @@ class ShareManager(manager.SchedulerDependentManager):
def _share_server_migration_start_driver(
self, context, source_share_server, dest_host, writable,
nondisruptive, preserve_snapshots, new_share_network_id):
nondisruptive, preserve_snapshots, new_share_network_id,
cleanup_source_server):
share_instances = self.db.share_instances_get_all_by_share_server(
context, source_share_server['id'], with_share_data=True)
@ -5036,9 +5102,15 @@ class ShareManager(manager.SchedulerDependentManager):
{'task_state': (
constants.TASK_STATE_MIGRATION_DRIVER_STARTING)})
self.driver.share_server_migration_start(
server_info = self.driver.share_server_migration_start(
context, source_share_server, dest_share_server,
share_instances, snapshot_instances)
share_instances, snapshot_instances, cleanup_source_server)
backend_details = (
server_info.get('backend_details') if server_info else None)
if backend_details:
self.db.share_server_backend_details_set(
context, dest_share_server['id'], backend_details)
self.db.share_server_update(
context, source_share_server['id'],
@ -5147,7 +5219,8 @@ class ShareManager(manager.SchedulerDependentManager):
@utils.require_driver_initialized
def share_server_migration_start(
self, context, share_server_id, dest_host, writable,
nondisruptive, preserve_snapshots, new_share_network_id=None):
nondisruptive, preserve_snapshots, new_share_network_id=None,
cleanup_source_server=False):
"""Migrates a share server from current host to another host."""
LOG.debug("Entered share_server_migration_start method for share "
"server %s.", share_server_id)
@ -5166,7 +5239,8 @@ class ShareManager(manager.SchedulerDependentManager):
self._share_server_migration_start_driver(
context, share_server, dest_host, writable, nondisruptive,
preserve_snapshots, new_share_network_id)
preserve_snapshots, new_share_network_id, cleanup_source_server
)
except Exception:
LOG.exception(
("The driver could not migrate the share server "
@ -5374,13 +5448,42 @@ class ShareManager(manager.SchedulerDependentManager):
dest_sn = self.db.share_network_get(context, dest_sn_id)
dest_sns = self.db.share_network_subnet_get(context, dest_sns_id)
need_new_network_allocations, __ = (
self._get_setup_server_info_for_share_server_migration(
dest_share_server, source_share_server))
server_to_get_allocations = (
dest_share_server
if need_new_network_allocations else source_share_server)
new_network_allocations = self._form_server_setup_info(
context, dest_share_server, dest_sn, dest_sns)
context, server_to_get_allocations, dest_sn, dest_sns)
model_update = self.driver.share_server_migration_complete(
context, source_share_server, dest_share_server, share_instances,
snapshot_instances, new_network_allocations)
if not need_new_network_allocations:
all_allocations = [
new_network_allocations['network_allocations'],
new_network_allocations['admin_network_allocations']
]
for allocations in all_allocations:
for allocation in allocations:
allocation_id = allocation['id']
values = {
'share_server_id': dest_share_server['id']
}
self.db.network_allocation_update(
context, allocation_id, values)
if (self.driver.server_migration_mechanism_can_reuse_share_server(
source_share_server['host'], dest_share_server['host'])):
source_backend_details = source_share_server.get("backend_details")
if source_backend_details:
for k, v in source_backend_details.items():
self.db.share_server_backend_details_set(
context, dest_share_server['id'], {k: v})
host_value = share_utils.extract_host(dest_share_server['host'])
service = self.db.service_get_by_args(
context, host_value, 'manila-share')

View File

@ -186,7 +186,8 @@ class ShareAPI(object):
def share_server_migration_start(self, context, share_server, dest_host,
writable, nondisruptive,
preserve_snapshots, new_share_network_id):
preserve_snapshots, new_share_network_id,
cleanup_source_server):
host = utils.extract_host(dest_host)
call_context = self.client.prepare(server=host, version='1.21')
call_context.cast(
@ -197,7 +198,8 @@ class ShareAPI(object):
writable=writable,
nondisruptive=nondisruptive,
preserve_snapshots=preserve_snapshots,
new_share_network_id=new_share_network_id)
new_share_network_id=new_share_network_id,
cleanup_source_server=cleanup_source_server)
def share_server_migration_check(self, context, share_server_id, dest_host,
writable, nondisruptive,

View File

@ -19,6 +19,7 @@ import ddt
import webob
from manila.api import common
from manila.api.openstack import api_version_request
from manila.api.v2 import share_servers
from manila.common import constants
from manila import context as ctx_api
@ -532,20 +533,52 @@ class ShareServerControllerTest(test.TestCase):
context, network_subnet['share_network_id'])
is_active_mock.assert_called_once_with(share_network)
def _get_server_migration_request(self, server_id):
def _get_server_migration_request(self, server_id, version='2.57'):
req = fakes.HTTPRequest.blank(
'/share-servers/%s/action' % server_id,
use_admin_context=True, version='2.57')
use_admin_context=True, version=version)
req.method = 'POST'
req.headers['content-type'] = 'application/json'
req.api_version_request.experimental = True
return req
def test_share_server_migration_start(self):
@ddt.data('2.57', '2.63', '2.64')
def test_share_server_migration_start(self, version):
server = db_utils.create_share_server(id='fake_server_id',
status=constants.STATUS_ACTIVE)
req = self._get_server_migration_request(server['id'], version=version)
mock__share_server_migration_start = self.mock_object(
self.controller, '_share_server_migration_start')
body = {
'migration_start': {
'host': 'fake_host',
'preserve_snapshots': True,
'writable': True,
'nondisruptive': True,
'new_share_network_id': 'fake_net_id'
}
}
self.controller.share_server_migration_start(req, server['id'], body)
if (req.api_version_request ==
api_version_request.APIVersionRequest('2.64')):
mock__share_server_migration_start.assert_called_once_with(
req, server['id'], body,
cleanup_source_server=False)
else:
mock__share_server_migration_start.assert_called_once_with(
req, server['id'], body)
@ddt.data(True, False)
def test__share_server_migration_start(self, cleanup_source_server):
server = db_utils.create_share_server(id='fake_server_id',
status=constants.STATUS_ACTIVE)
share_network = db_utils.create_share_network()
req = self._get_server_migration_request(server['id'])
version = '2.64' if cleanup_source_server else '2.57'
req = self._get_server_migration_request(server['id'], version=version)
context = req.environ['manila.context']
self.mock_object(db_api, 'share_network_get', mock.Mock(
@ -563,16 +596,20 @@ class ShareServerControllerTest(test.TestCase):
'writable': True,
'nondisruptive': True,
'new_share_network_id': 'fake_net_id',
'cleanup_source_server': cleanup_source_server
}
}
self.controller.share_server_migration_start(req, server['id'], body)
self.controller._share_server_migration_start(
req, server['id'], body,
cleanup_source_server=cleanup_source_server)
db_api.share_server_get.assert_called_once_with(
context, server['id'])
share_api.API.share_server_migration_start.assert_called_once_with(
context, server, 'fake_host', True, True, True,
new_share_network=share_network)
new_share_network=share_network,
cleanup_source_server=cleanup_source_server)
db_api.share_network_get.assert_called_once_with(
context, 'fake_net_id')
common.check_share_network_is_active.assert_called_once_with(
@ -583,8 +620,8 @@ class ShareServerControllerTest(test.TestCase):
{'api_exception': exception.InvalidShareServer(reason=""),
'expected_exception': webob.exc.HTTPConflict})
@ddt.unpack
def test_share_server_migration_start_conflict(self, api_exception,
expected_exception):
def test__share_server_migration_start_conflict(self, api_exception,
expected_exception):
share_network = db_utils.create_share_network()
share_network_subnet = db_utils.create_share_network_subnet(
share_network_id=share_network['id'])
@ -598,7 +635,7 @@ class ShareServerControllerTest(test.TestCase):
'host': 'fake_host',
'preserve_snapshots': True,
'writable': True,
'nondisruptive': True,
'nondisruptive': True
}
}
self.mock_object(share_api.API, 'share_server_migration_start',
@ -611,7 +648,7 @@ class ShareServerControllerTest(test.TestCase):
mock.Mock(return_value=share_network))
self.assertRaises(expected_exception,
self.controller.share_server_migration_start,
self.controller._share_server_migration_start,
req, server['id'], body)
db_api.share_server_get.assert_called_once_with(context,
@ -626,10 +663,11 @@ class ShareServerControllerTest(test.TestCase):
migration_start_params['writable'],
migration_start_params['nondisruptive'],
migration_start_params['preserve_snapshots'],
cleanup_source_server=False,
new_share_network=None)
@ddt.data('host', 'body')
def test_share_server_migration_start_missing_mandatory(self, param):
def test__share_server_migration_start_missing_mandatory(self, param):
server = db_utils.create_share_server(
id='fake_server_id', status=constants.STATUS_ACTIVE)
req = self._get_server_migration_request(server['id'])
@ -641,7 +679,7 @@ class ShareServerControllerTest(test.TestCase):
'preserve_metadata': True,
'preserve_snapshots': True,
'writable': True,
'nondisruptive': True,
'nondisruptive': True
}
}
@ -650,20 +688,20 @@ class ShareServerControllerTest(test.TestCase):
else:
body['migration_start'].pop(param)
method = 'share_server_migration_start'
self.mock_object(share_api.API, method)
self.mock_object(share_api.API, 'share_server_migration_start')
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
self.assertRaises(webob.exc.HTTPBadRequest,
getattr(self.controller, method),
req, server['id'], body)
self.assertRaises(
webob.exc.HTTPBadRequest,
getattr(self.controller, '_share_server_migration_start'),
req, server['id'], body)
db_api.share_server_get.assert_called_once_with(context,
server['id'])
@ddt.data('nondisruptive', 'writable', 'preserve_snapshots')
def test_share_server_migration_start_non_boolean(self, param):
def test__share_server_migration_start_non_boolean(self, param):
server = db_utils.create_share_server(
id='fake_server_id', status=constants.STATUS_ACTIVE)
req = self._get_server_migration_request(server['id'])
@ -674,25 +712,25 @@ class ShareServerControllerTest(test.TestCase):
'host': 'fake_host',
'preserve_snapshots': True,
'writable': True,
'nondisruptive': True,
'nondisruptive': True
}
}
body['migration_start'][param] = None
method = 'share_server_migration_start'
self.mock_object(share_api.API, method)
self.mock_object(share_api.API, 'share_server_migration_start')
self.mock_object(db_api, 'share_server_get',
mock.Mock(return_value=server))
self.assertRaises(webob.exc.HTTPBadRequest,
getattr(self.controller, method),
req, server['id'], body)
self.assertRaises(
webob.exc.HTTPBadRequest,
getattr(self.controller, '_share_server_migration_start'),
req, server['id'], body)
db_api.share_server_get.assert_called_once_with(context,
server['id'])
def test_share_server_migration_start_share_server_not_found(self):
def test__share_server_migration_start_share_server_not_found(self):
fake_id = 'fake_server_id'
req = self._get_server_migration_request(fake_id)
context = req.environ['manila.context']
@ -704,12 +742,12 @@ class ShareServerControllerTest(test.TestCase):
share_server_id=fake_id)))
self.assertRaises(webob.exc.HTTPNotFound,
self.controller.share_server_migration_start,
self.controller._share_server_migration_start,
req, fake_id, body)
db_api.share_server_get.assert_called_once_with(context,
fake_id)
def test_share_server_migration_start_new_share_network_not_found(self):
def test__share_server_migration_start_new_share_network_not_found(self):
server = db_utils.create_share_server(
id='fake_server_id', status=constants.STATUS_ACTIVE)
req = self._get_server_migration_request(server['id'])
@ -730,14 +768,14 @@ class ShareServerControllerTest(test.TestCase):
mock.Mock(return_value=server))
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.share_server_migration_start,
self.controller._share_server_migration_start,
req, server['id'], body)
db_api.share_network_get.assert_called_once_with(context,
'nonexistent')
db_api.share_server_get.assert_called_once_with(context,
server['id'])
def test_share_server_migration_start_host_with_pool(self):
def test__share_server_migration_start_host_with_pool(self):
server = db_utils.create_share_server(id='fake_server_id',
status=constants.STATUS_ACTIVE)
req = self._get_server_migration_request(server['id'])
@ -753,7 +791,7 @@ class ShareServerControllerTest(test.TestCase):
}
self.assertRaises(webob.exc.HTTPBadRequest,
self.controller.share_server_migration_start,
self.controller._share_server_migration_start,
req, server['id'], body)
def test_share_server_migration_check_host_with_pool(self):

View File

@ -716,7 +716,8 @@ class DummyDriver(driver.ShareDriver):
@slow_me_down
def share_server_migration_start(self, context, src_share_server,
dest_share_server, shares, snapshots):
dest_share_server, shares, snapshots,
cleanup_source_server=False):
"""Is called to perform 1st phase of migration of a share server."""
LOG.debug(
"Migration of dummy share server with ID '%s' has been started.",

View File

@ -5263,6 +5263,7 @@ class ShareAPITestCase(test.TestCase):
writable = True
nondisruptive = True
preserve_snapshots = True
cleanup_source_server = False
fake_share_network = db_api.share_network_get(
self.context, fake_share_network['id'])
fake_host = 'test@fake'
@ -5300,7 +5301,8 @@ class ShareAPITestCase(test.TestCase):
self.context, fake_share_server, fake_host, fake_share_network)
mock_migration_start.assert_called_once_with(
self.context, fake_share_server, fake_host, writable,
nondisruptive, preserve_snapshots, fake_share_network['id']
nondisruptive, preserve_snapshots, fake_share_network['id'],
cleanup_source_server
)
mock_server_update.assert_called_once_with(
self.context, fake_share_server['id'], server_expected_update)

View File

@ -587,7 +587,7 @@ class ShareDriverTestCase(test.TestCase):
self.assertRaises(NotImplementedError,
share_driver.share_server_migration_start,
None, None, None, None, None)
None, None, None, None, None, None)
def test_share_server_migration_continue(self):
driver.CONF.set_default('driver_handles_share_servers', True)
@ -1343,3 +1343,21 @@ class ShareDriverTestCase(test.TestCase):
self.assertEqual(expected['ipv4'], result['ipv4_support'])
self.assertIsNotNone(result['ipv6_support'])
self.assertEqual(expected['ipv6'], result['ipv6_support'])
def test_is_ip_reusage_supported_on_server_migration(self):
share_driver = self._instantiate_share_driver(None, False)
result = share_driver.is_ip_reusage_supported_on_server_migration(
'source_host', 'dest_host'
)
self.assertEqual(result, False)
def test_server_migration_mechanism_can_reuse_share_server(self):
share_driver = self._instantiate_share_driver(None, False)
result = (
share_driver.server_migration_mechanism_can_reuse_share_server(
'source_host', 'dest_host'
)
)
self.assertEqual(result, False)

View File

@ -2832,7 +2832,8 @@ class ShareManagerTestCase(test.TestCase):
}
fake_metadata = {
'migration_destination': True,
'request_host': fake_data['fake_dest_host']
'request_host': fake_data['fake_dest_host'],
'source_share_server': fake_data['source_share_server']
}
mock_subnet_get = self.mock_object(
@ -3431,9 +3432,129 @@ class ShareManagerTestCase(test.TestCase):
delete_all_rules=True, share_server=share_srv)
self.assertTrue(manager.LOG.warning.called)
def test_setup_server(self):
@ddt.data(
{
'same_subnet': False,
'can_reuse_ip': False,
'can_reuse_share_server': False
},
{
'same_subnet': False,
'can_reuse_ip': False,
'can_reuse_share_server': True
},
{
'same_subnet': False,
'can_reuse_ip': True,
'can_reuse_share_server': False
},
{
'same_subnet': False,
'can_reuse_ip': True,
'can_reuse_share_server': True
},
{
'same_subnet': True,
'can_reuse_ip': False,
'can_reuse_share_server': False
},
{
'same_subnet': True,
'can_reuse_ip': False,
'can_reuse_share_server': True
},
{
'same_subnet': True,
'can_reuse_ip': True,
'can_reuse_share_server': False
},
{
'same_subnet': True,
'can_reuse_ip': True,
'can_reuse_share_server': True
}
)
@ddt.unpack
def test__get_setup_server_info_for_share_server_migration(
self, same_subnet, can_reuse_ip, can_reuse_share_server):
share_server = {
'host': 'fake_host',
'share_network_subnet': {
'id': 'fake_id' if same_subnet else 'different_fake_id'
}
}
source_share_server = {
'host': 'fake_source_host',
'share_network_subnet_id': 'fake_id'
}
can_reuse_network_allocations = can_reuse_ip and same_subnet
network_allocations_result = can_reuse_network_allocations is False
self.share_manager.driver = mock.Mock()
mock_ip_reusage = self.mock_object(
self.share_manager.driver,
'is_ip_reusage_supported_on_server_migration',
mock.Mock(return_value=can_reuse_ip))
mock_share_server_reusage = self.mock_object(
self.share_manager.driver,
'server_migration_mechanism_can_reuse_share_server',
mock.Mock(return_value=can_reuse_share_server))
(need_new_network_allocations, migration_can_reuse_server) = (
self.share_manager.
_get_setup_server_info_for_share_server_migration(
share_server, source_share_server)
)
mock_ip_reusage.assert_called_once_with(share_server['host'],
source_share_server['host'])
mock_share_server_reusage.assert_called_once_with(
source_share_server['host'], share_server['host'])
self.assertEqual(need_new_network_allocations,
network_allocations_result)
self.assertEqual(migration_can_reuse_server, can_reuse_share_server)
@ddt.data(
{
'migration_destination': None,
'need_new_network_allocation': True,
'setup_server_in_backend': True
},
{
'migration_destination': 'fake-destination',
'need_new_network_allocation': False,
'setup_server_in_backend': False
},
{
'migration_destination': 'fake-destination',
'need_new_network_allocation': False,
'setup_server_in_backend': True
},
{
'migration_destination': 'fake-destination',
'need_new_network_allocation': True,
'setup_server_in_backend': False
},
{
'migration_destination': 'fake-destination',
'need_new_network_allocation': True,
'setup_server_in_backend': True
})
@ddt.unpack
def test__setup_server(self, migration_destination,
need_new_network_allocation,
setup_server_in_backend):
# Setup required test data
metadata = {'fake_metadata_key': 'fake_metadata_value'}
source_share_server = {
'host': 'fake_source_host',
'share_network_subnet_id': 'fake_id'
}
metadata = {
'fake_metadata_key': 'fake_metadata_value',
'migration_destination': migration_destination,
'source_share_server': source_share_server
}
share_network = db_utils.create_share_network(id='fake_sn_id')
share_net_subnet = db_utils.create_share_network_subnet(
id='fake_sns_id', share_network_id=share_network['id']
@ -3457,18 +3578,27 @@ class ShareManagerTestCase(test.TestCase):
network_info['network_type'] = 'fake_network_type'
# mock required stuff
self.mock_object(self.share_manager.db, 'share_network_subnet_get',
mock.Mock(return_value=share_net_subnet))
self.mock_object(self.share_manager.db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(self.share_manager,
'_get_setup_server_info_for_share_server_migration',
mock.Mock(return_value=(need_new_network_allocation,
not setup_server_in_backend)))
self.mock_object(self.share_manager.driver, 'allocate_network')
self.mock_object(self.share_manager.driver, 'allocate_admin_network')
self.mock_object(self.share_manager.db, 'share_network_subnet_get',
mock.Mock(return_value=share_net_subnet))
self.mock_object(self.share_manager, '_form_server_setup_info',
mock.Mock(return_value=network_info))
self.mock_object(self.share_manager, '_validate_segmentation_id')
self.mock_object(self.share_manager.driver, 'setup_server',
mock.Mock(return_value=server_info))
self.mock_object(self.share_manager.db,
'share_server_backend_details_set')
self.mock_object(self.share_manager.driver, 'setup_server',
mock.Mock(return_value=server_info))
self.mock_object(self.share_manager.driver,
'update_network_allocation')
self.mock_object(self.share_manager.driver,
'update_admin_network_allocation')
self.mock_object(self.share_manager.db, 'share_server_update',
mock.Mock(return_value=share_server))
@ -3478,45 +3608,92 @@ class ShareManagerTestCase(test.TestCase):
# verify results
self.assertEqual(share_server, result)
if migration_destination:
(self.share_manager
._get_setup_server_info_for_share_server_migration
.assert_called_once_with(share_server, source_share_server))
if need_new_network_allocation:
self.share_manager.driver.allocate_network.assert_called_once_with(
self.context, share_server, share_network,
share_server['share_network_subnet'])
(self.share_manager.driver.allocate_admin_network
.assert_called_once_with(self.context, share_server))
(self.share_manager.driver.update_network_allocation
.assert_called_once_with(self.context, share_server))
(self.share_manager.driver.update_admin_network_allocation
.assert_called_once_with(self.context, share_server))
if setup_server_in_backend:
self.share_manager.driver.setup_server.assert_called_once_with(
network_info, metadata=metadata)
(self.share_manager.db.share_server_backend_details_set
.assert_has_calls([
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[0]['type']:
jsonutils.dumps(sec_services[0])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[1]['type']:
jsonutils.dumps(sec_services[1])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[2]['type']:
jsonutils.dumps(sec_services[2])}),
mock.call(self.context, share_server['id'], server_info)
]))
else:
(self.share_manager.db.share_server_backend_details_set
.assert_has_calls([
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[0]['type']:
jsonutils.dumps(sec_services[0])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[1]['type']:
jsonutils.dumps(sec_services[1])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[2]['type']:
jsonutils.dumps(sec_services[2])})
]))
if migration_destination and not need_new_network_allocation:
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, source_share_server, share_network,
share_net_subnet)
else:
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
self.share_manager.db.share_network_get.assert_called_once_with(
self.context, share_net_subnet['share_network_id'])
self.share_manager.db.share_network_subnet_get.assert_called_once_with(
self.context, share_server['share_network_subnet']['id'])
self.share_manager.driver.allocate_network.assert_called_once_with(
self.context, share_server, share_network,
share_server['share_network_subnet'])
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
self.share_manager._validate_segmentation_id.assert_called_once_with(
network_info)
self.share_manager.driver.setup_server.assert_called_once_with(
network_info, metadata=metadata)
(self.share_manager.db.share_server_backend_details_set.
assert_has_calls([
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[0]['type']:
jsonutils.dumps(sec_services[0])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[1]['type']:
jsonutils.dumps(sec_services[1])}),
mock.call(self.context, share_server['id'],
{'security_service_' + sec_services[2]['type']:
jsonutils.dumps(sec_services[2])}),
mock.call(self.context, share_server['id'], server_info),
]))
self.share_manager.db.share_server_update.assert_called_once_with(
self.context, share_server['id'],
{'status': constants.STATUS_ACTIVE,
'identifier': share_server['id']})
def test_setup_server_server_info_not_present(self):
@ddt.data(True, False)
def test__setup_server_server_info_not_present(self,
need_network_allocation):
# Setup required test data
metadata = {'fake_metadata_key': 'fake_metadata_value'}
source_share_server = {
'host': 'fake_source_host',
'share_network_subnet_id': 'fake_id'
}
metadata = {
'fake_metadata_key': 'fake_metadata_value',
'migration_destination': 'fake_detination',
'source_share_server': source_share_server
}
share_network = {'id': 'fake_sn_id'}
share_net_subnet = {'id': 'fake_sns_id',
'share_network_id': share_network['id']}
share_server = {
'id': 'fake_id',
'host': 'fake_host',
'share_network_subnet': share_net_subnet,
}
network_info = {
@ -3527,6 +3704,10 @@ class ShareManagerTestCase(test.TestCase):
server_info = {}
# mock required stuff
self.mock_object(self.share_manager,
'_get_setup_server_info_for_share_server_migration',
mock.Mock(
return_value=(need_network_allocation, False)))
self.mock_object(self.share_manager.db, 'share_network_subnet_get',
mock.Mock(return_value=share_net_subnet))
self.mock_object(self.share_manager.db, 'share_network_get',
@ -3538,6 +3719,7 @@ class ShareManagerTestCase(test.TestCase):
self.mock_object(self.share_manager.db, 'share_server_update',
mock.Mock(return_value=share_server))
self.mock_object(self.share_manager.driver, 'allocate_network')
self.mock_object(self.share_manager.driver, 'allocate_admin_network')
# execute method _setup_server
result = self.share_manager._setup_server(
@ -3549,20 +3731,37 @@ class ShareManagerTestCase(test.TestCase):
self.context, share_net_subnet['share_network_id'])
self.share_manager.db.share_network_subnet_get.assert_called_once_with(
self.context, share_server['share_network_subnet']['id'])
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
self.share_manager.driver.setup_server.assert_called_once_with(
network_info, metadata=metadata)
self.share_manager.db.share_server_update.assert_called_once_with(
self.context, share_server['id'],
{'status': constants.STATUS_ACTIVE,
'identifier': share_server['id']})
self.share_manager.driver.allocate_network.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
if need_network_allocation:
self.share_manager.driver.allocate_network.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
(self.share_manager.driver.allocate_admin_network
.assert_called_once_with(
self.context, share_server))
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
else:
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, source_share_server, share_network,
share_net_subnet)
def setup_server_raise_exception(self, detail_data_proper):
def setup_server_raise_exception(self, detail_data_proper,
need_network_allocation):
# Setup required test data
metadata = {'fake_metadata_key': 'fake_metadata_value'}
source_share_server = {
'host': 'fake_source_host',
'share_network_subnet_id': 'fake_id'
}
metadata = {
'fake_metadata_key': 'fake_metadata_value',
'migration_destination': 'fake_detination',
'source_share_server': source_share_server
}
server_info = {'details_key': 'value'}
share_network = {'id': 'fake_sn_id'}
share_net_subnet = {'id': 'fake_sns_id',
@ -3584,12 +3783,18 @@ class ShareManagerTestCase(test.TestCase):
detail_data = 'not dictionary detail data'
# Mock required parameters
self.mock_object(self.share_manager,
'_get_setup_server_info_for_share_server_migration',
mock.Mock(
return_value=(need_network_allocation, False)))
self.mock_object(self.share_manager.db, 'share_network_get',
mock.Mock(return_value=share_network))
self.mock_object(self.share_manager.db, 'share_network_subnet_get',
mock.Mock(return_value=share_net_subnet))
self.mock_object(self.share_manager.db, 'share_server_update')
for m in ['deallocate_network', 'allocate_network']:
methods = ['deallocate_network', 'allocate_network',
'allocate_admin_network']
for m in methods:
self.mock_object(self.share_manager.driver, m)
self.mock_object(self.share_manager, '_form_server_setup_info',
mock.Mock(return_value=network_info))
@ -3613,8 +3818,6 @@ class ShareManagerTestCase(test.TestCase):
(self.share_manager.db.share_server_backend_details_set.
assert_called_once_with(
self.context, share_server['id'], server_info))
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
self.share_manager.db.share_server_update.assert_called_once_with(
self.context, share_server['id'],
{'status': constants.STATUS_ERROR})
@ -3622,17 +3825,33 @@ class ShareManagerTestCase(test.TestCase):
self.context, share_net_subnet['share_network_id'])
self.share_manager.db.share_network_subnet_get.assert_called_once_with(
self.context, share_server['share_network_subnet']['id'])
self.share_manager.driver.allocate_network.assert_has_calls([
mock.call(self.context, share_server, share_network,
share_net_subnet)])
self.share_manager.driver.deallocate_network.assert_has_calls([
mock.call(self.context, share_server['id'])])
if need_network_allocation:
self.share_manager.driver.allocate_network.assert_has_calls([
mock.call(self.context, share_server, share_network,
share_net_subnet)])
self.share_manager.driver.allocate_admin_network.assert_has_calls([
mock.call(self.context, share_server)])
self.share_manager.driver.deallocate_network.assert_has_calls([
mock.call(self.context, share_server['id'])])
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, share_server, share_network, share_net_subnet)
else:
self.share_manager._form_server_setup_info.assert_called_once_with(
self.context, source_share_server, share_network,
share_net_subnet)
def test_setup_server_incorrect_detail_data(self):
self.setup_server_raise_exception(detail_data_proper=False)
@ddt.data(True, False)
def test__setup_server_incorrect_detail_data(self,
need_network_allocation):
self.setup_server_raise_exception(
detail_data_proper=False,
need_network_allocation=need_network_allocation)
def test_setup_server_exception_in_driver(self):
self.setup_server_raise_exception(detail_data_proper=True)
@ddt.data(True, False)
def test__setup_server_exception_in_driver(self, need_network_allocation):
self.setup_server_raise_exception(
detail_data_proper=True,
need_network_allocation=need_network_allocation)
@ddt.data({},
{'detail_data': 'fake'},
@ -3640,7 +3859,7 @@ class ShareManagerTestCase(test.TestCase):
{'detail_data': {'server_details': {'fake': 'fake'}}},
{'detail_data': {
'server_details': {'fake': 'fake', 'fake2': 'fake2'}}},)
def test_setup_server_exception_in_cleanup_after_error(self, data):
def test__setup_server_exception_in_cleanup_after_error(self, data):
def get_server_details_from_data(data):
d = data.get('detail_data')
@ -8080,7 +8299,7 @@ class ShareManagerTestCase(test.TestCase):
def _setup_server_migration_start_mocks(
self, fake_share_instances, fake_snap_instances, fake_old_network,
fake_new_network, fake_service, fake_request_spec,
fake_driver_result, fake_new_share_server):
fake_driver_result, fake_new_share_server, server_info):
self.mock_object(db, 'share_instances_get_all_by_share_server',
mock.Mock(return_value=fake_share_instances))
self.mock_object(db, 'share_snapshot_instance_get_all_with_filters',
@ -8106,7 +8325,8 @@ class ShareManagerTestCase(test.TestCase):
'_cast_access_rules_to_readonly_for_server')
self.mock_object(db, 'share_server_update')
self.mock_object(self.share_manager.driver,
'share_server_migration_start')
'share_server_migration_start',
mock.Mock(return_value=server_info))
@ddt.data(True, False)
def test__share_server_migration_start_driver(self, writable):
@ -8127,6 +8347,7 @@ class ShareManagerTestCase(test.TestCase):
fake_dest_host = 'fakehost@fakebackend'
nondisruptive = False
preserve_snapshots = True
cleanup_source_server = True
fake_driver_result = {
'compatible': True,
'writable': writable,
@ -8136,15 +8357,20 @@ class ShareManagerTestCase(test.TestCase):
'migration_cancel': False,
'migration_get_progress': False
}
server_info = {
'fake_server_info_key': 'fake_server_info_value',
'backend_details': {'fake': 'fake'}
}
self._setup_server_migration_start_mocks(
fake_share_instances, fake_snap_instances, fake_old_network,
fake_new_network, fake_service, fake_request_spec,
fake_driver_result, fake_new_share_server)
fake_driver_result, fake_new_share_server, server_info)
result = self.share_manager._share_server_migration_start_driver(
self.context, fake_old_share_server, fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_new_network['id']
nondisruptive, preserve_snapshots, fake_new_network['id'],
cleanup_source_server
)
self.assertTrue(result)
@ -8183,7 +8409,8 @@ class ShareManagerTestCase(test.TestCase):
(self.share_manager.driver.share_server_migration_start.
assert_called_once_with(
self.context, fake_old_share_server, fake_new_share_server,
fake_share_instances, fake_snap_instances))
fake_share_instances, fake_snap_instances,
cleanup_source_server))
if not writable:
(self.share_manager._cast_access_rules_to_readonly_for_server.
assert_called_once_with(
@ -8213,6 +8440,7 @@ class ShareManagerTestCase(test.TestCase):
nondisruptive = False
preserve_snapshots = True
writable = True
cleanup_source_server = True
fake_driver_result = {
'compatible': True,
'writable': writable,
@ -8222,11 +8450,15 @@ class ShareManagerTestCase(test.TestCase):
'migration_cancel': False,
'migration_get_progress': False
}
server_info = {
'fake_server_info_key': 'fake_server_info_value',
'backend_details': {'fake': 'fake'}
}
self._setup_server_migration_start_mocks(
fake_share_instances, fake_snap_instances, fake_old_network,
fake_new_network, fake_service, fake_request_spec,
fake_driver_result, fake_new_share_server)
fake_driver_result, fake_new_share_server, server_info)
mock__reset_read_only = self.mock_object(
self.share_manager, '_reset_read_only_access_rules_for_server')
@ -8238,7 +8470,8 @@ class ShareManagerTestCase(test.TestCase):
exception.ShareServerMigrationFailed,
self.share_manager._share_server_migration_start_driver,
self.context, fake_old_share_server, fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_new_network['id']
nondisruptive, preserve_snapshots, fake_new_network['id'],
cleanup_source_server
)
db.share_instances_get_all_by_share_server.assert_called_once_with(
@ -8282,7 +8515,8 @@ class ShareManagerTestCase(test.TestCase):
(self.share_manager.driver.share_server_migration_start.
assert_called_once_with(
self.context, fake_old_share_server, fake_new_share_server,
fake_share_instances, fake_snap_instances))
fake_share_instances, fake_snap_instances,
cleanup_source_server))
mock__reset_read_only.assert_called_once_with(
self.context, fake_share_instances, fake_old_share_server,
dest_host=fake_old_share_server['host']
@ -8416,6 +8650,7 @@ class ShareManagerTestCase(test.TestCase):
writable = True
nondisruptive = True
preserve_snapshots = True
cleanup_source_server = True
mock_server_update = self.mock_object(db, 'share_server_update')
mock_server_get = self.mock_object(
@ -8425,7 +8660,8 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.share_server_migration_start(
self.context, fake_share_server['id'], fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_share_network['id']
nondisruptive, preserve_snapshots, fake_share_network['id'],
cleanup_source_server=cleanup_source_server
)
mock_server_update.assert_called_once_with(
@ -8437,7 +8673,8 @@ class ShareManagerTestCase(test.TestCase):
)
mock__server_migration_start_driver.assert_called_once_with(
self.context, fake_share_server, fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_share_network['id']
nondisruptive, preserve_snapshots, fake_share_network['id'],
cleanup_source_server
)
@ddt.data(True, False)
@ -8448,6 +8685,7 @@ class ShareManagerTestCase(test.TestCase):
writable = True
nondisruptive = True
preserve_snapshots = True
cleanup_source_server = True
self.mock_object(self.share_manager, 'driver')
self.share_manager.driver.driver_handles_share_servers = dhss
@ -8460,7 +8698,8 @@ class ShareManagerTestCase(test.TestCase):
self.share_manager.share_server_migration_start(
self.context, fake_share_server['id'], fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_share_network['id']
nondisruptive, preserve_snapshots, fake_share_network['id'],
cleanup_source_server=cleanup_source_server
)
mock_server_update.assert_has_calls([
@ -8479,7 +8718,8 @@ class ShareManagerTestCase(test.TestCase):
if dhss:
mock__server_migration_start_driver.assert_called_once_with(
self.context, fake_share_server, fake_dest_host, writable,
nondisruptive, preserve_snapshots, fake_share_network['id']
nondisruptive, preserve_snapshots, fake_share_network['id'],
cleanup_source_server
)
def _setup_migration_continue_mocks(
@ -8764,14 +9004,35 @@ class ShareManagerTestCase(test.TestCase):
'status': constants.STATUS_ACTIVE})
@ddt.data(
{'unmanage_source_server': False,
'snapshot_updates': {},
'share_updates': {}},
{'unmanage_source_server': True,
'snapshot_updates': {},
'share_updates': {}},
{'model_update': {
'unmanage_source_server': False,
'snapshot_updates': {},
'share_updates': {}},
'need_network_allocation': False,
'can_reuse_server': False},
{'model_update': {
'unmanage_source_server': True,
'snapshot_updates': {},
'share_updates': {}},
'need_network_allocation': False,
'can_reuse_server': True},
{'model_update': {
'unmanage_source_server': False,
'snapshot_updates': {},
'share_updates': {}},
'need_network_allocation': True,
'can_reuse_server': False},
{'model_update': {
'unmanage_source_server': True,
'snapshot_updates': {},
'share_updates': {}},
'need_network_allocation': True,
'can_reuse_server': True}
)
def test__server_migration_complete_driver(self, model_update):
@ddt.unpack
def test__server_migration_complete_driver(self, model_update,
need_network_allocation,
can_reuse_server):
fake_share_network = db_utils.create_share_network()
fake_share_network_subnet = db_utils.create_share_network_subnet(
share_network_id=fake_share_network['id'])
@ -8785,7 +9046,10 @@ class ShareManagerTestCase(test.TestCase):
fake_share_instances = [fake_share['instance']]
fake_snapshot_instances = [fake_snapshot['instance']]
fake_share_instance_id = fake_share['instance']['id']
fake_allocation_data = {}
fake_allocation_data = {
'network_allocations': [{'id': 'fake_id'}],
'admin_network_allocations': [{'id': 'fake_admin_id'}],
}
model_update['share_updates'][fake_share['instance']['id']] = {
'export_locations': {
"path": "10.10.10.31:/fake_mount_point",
@ -8809,19 +9073,40 @@ class ShareManagerTestCase(test.TestCase):
'share_network_id': fake_share_network['id'],
'availability_zone_id': fake_service['availability_zone_id'],
}
backend_details = fake_source_share_server.get("backend_details")
mock_backend_details_set_calls = []
if backend_details:
for k, v in backend_details.items():
mock_backend_details_set_calls.append(
mock.call(
self.context, fake_dest_share_server['id'],
{k: v})
)
mock_server_update = self.mock_object(db, 'share_server_update')
mock_network_get = self.mock_object(
db, 'share_network_get', mock.Mock(return_vale=fake_share_network))
db, 'share_network_get',
mock.Mock(return_value=fake_share_network))
mock_subnet_get = self.mock_object(
db, 'share_network_subnet_get',
mock.Mock(return_value=fake_share_network_subnet))
self.mock_object(
mock_server_info_share_migration = self.mock_object(
self.share_manager,
'_get_setup_server_info_for_share_server_migration',
mock.Mock(return_value=(need_network_allocation, True)))
mock_form_server_setup_info = self.mock_object(
self.share_manager, '_form_server_setup_info',
mock.Mock(return_value=fake_allocation_data))
mock_server_migration_complete = self.mock_object(
self.share_manager.driver, 'share_server_migration_complete',
mock.Mock(return_value=model_update))
mock_network_allocation_update = self.mock_object(
db, 'network_allocation_update')
self.mock_object(self.share_manager.driver,
'server_migration_mechanism_can_reuse_share_server',
mock.Mock(return_value=can_reuse_server))
mock_share_server_backend_details_set = self.mock_object(
db, 'share_server_backend_details_set')
mock_service_get_by_args = self.mock_object(
db, 'service_get_by_args', mock.Mock(return_value=fake_service))
mock_instance_update = self.mock_object(db, 'share_instance_update')
@ -8854,6 +9139,30 @@ class ShareManagerTestCase(test.TestCase):
self.context, fake_share_network['id'])
mock_subnet_get.assert_called_once_with(
self.context, fake_share_network_subnet['id'])
mock_server_info_share_migration.assert_called_once_with(
fake_dest_share_server, fake_source_share_server)
if need_network_allocation:
mock_form_server_setup_info.assert_called_once_with(
self.context, fake_dest_share_server, fake_share_network,
fake_share_network_subnet)
elif need_network_allocation and can_reuse_server:
mock_share_server_backend_details_set.assert_has_calls(
mock_backend_details_set_calls)
else:
mock_form_server_setup_info.assert_called_once_with(
self.context, fake_source_share_server, fake_share_network,
fake_share_network_subnet)
mock_network_allocation_update.assert_has_calls(
[mock.call(
self.context,
fake_allocation_data['network_allocations'][0]['id'],
{'share_server_id': fake_dest_share_server['id']}),
mock.call(
self.context,
fake_allocation_data['admin_network_allocations'][0]['id'],
{'share_server_id': fake_dest_share_server['id']})])
mock_server_migration_complete.assert_called_once_with(
self.context, fake_source_share_server, fake_dest_share_server,
fake_share_instances, fake_snapshot_instances, fake_allocation_data

View File

@ -412,7 +412,8 @@ class ShareRpcAPITestCase(test.TestCase):
writable=True,
nondisruptive=False,
preserve_snapshots=True,
new_share_network_id='fake_share_network_id')
new_share_network_id='fake_share_network_id',
cleanup_source_server=True)
def test_share_server_migration_check(self):
self._test_share_api('share_server_migration_check',

View File

@ -0,0 +1,6 @@
---
features:
- |
Added share server migration enhancements. Now is possible to migrate share
servers nondisruptively and also allows the user to specify if the source
share server will be deleted after the migration ends.