Add commands for share server migration
Implements the commands for performing the share server migration. They are: share-server-migration-reset-task-state, share-server-migration-complete, share-server-migration-cancel, share-server-migration-start, share-server-migration-check and share-server-migration-get-progress. Co-Authored-By: Daniel Tapia <danielarthurt@gmail.com> Co-Authored-By: Andre Beltrami <debeltrami@gmail.com> Co-Authored-By: Douglas Viroel <viroel@gmail.com> Change-Id: Id829a375d828a4808306fd1a42bc7548721feb19 Partially-implements: bp share-server-migration Depends-On: Ic0751027d2c3f1ef7ab0f7836baff3070a230cfd Depends-On: I46a0cee0a4625d950f02fa7a5bf612de926451b5
This commit is contained in:
parent
399c0c4fb5
commit
edf064a335
@ -27,7 +27,7 @@ from manilaclient import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MAX_VERSION = '2.56'
|
||||
MAX_VERSION = '2.57'
|
||||
MIN_VERSION = '2.0'
|
||||
DEPRECATED_VERSION = '1.0'
|
||||
_VERSIONED_METHOD_MAP = {}
|
||||
|
@ -97,6 +97,7 @@ STATUS_MANAGE_ERROR = 'manage_error'
|
||||
STATUS_UNMANAGE_ERROR = 'unmanage_error'
|
||||
STATUS_DELETING = 'deleting'
|
||||
STATUS_CREATING = 'creating'
|
||||
STATUS_SERVER_MIGRATING = 'server_migrating'
|
||||
|
||||
SNAPSHOT_SUPPORT = 'snapshot_support'
|
||||
CREATE_SHARE_FROM_SNAPSHOT_SUPPORT = 'create_share_from_snapshot_support'
|
||||
|
@ -186,6 +186,11 @@ share_opts = [
|
||||
help="Defines whether to run manage/unmanage tests or "
|
||||
"not. Disable this feature if used driver does not "
|
||||
"support it."),
|
||||
cfg.BoolOpt("run_share_servers_migration_tests",
|
||||
default=False,
|
||||
help="Defines whether to run share server migration tests or "
|
||||
"not. Disable this feature if used driver does not "
|
||||
"support it."),
|
||||
|
||||
]
|
||||
|
||||
|
@ -1613,6 +1613,83 @@ class ManilaCLIClient(base.CLIClient):
|
||||
managed_share = output_parser.details(managed_share_raw)
|
||||
return managed_share['id']
|
||||
|
||||
def share_server_migration_check(self, server_id, dest_host, writable,
|
||||
nondisruptive, preserve_snapshots,
|
||||
new_share_network=None):
|
||||
cmd = ('share-server-migration-check %(server_id)s %(host)s '
|
||||
'--writable %(writable)s --nondisruptive %(nondisruptive)s '
|
||||
'--preserve-snapshots %(preserve_snapshots)s') % {
|
||||
'server_id': server_id,
|
||||
'host': dest_host,
|
||||
'writable': writable,
|
||||
'nondisruptive': nondisruptive,
|
||||
'preserve_snapshots': preserve_snapshots,
|
||||
}
|
||||
if new_share_network:
|
||||
cmd += ' --new-share-network %s' % new_share_network
|
||||
result = self.manila(cmd)
|
||||
return output_parser.details(result)
|
||||
|
||||
def share_server_migration_start(self, server_id, dest_host,
|
||||
writable=False, nondisruptive=False,
|
||||
preserve_snapshots=False,
|
||||
new_share_network=None):
|
||||
cmd = ('share-server-migration-start %(server_id)s %(host)s '
|
||||
'--writable %(writable)s --nondisruptive %(nondisruptive)s '
|
||||
'--preserve-snapshots %(preserve_snapshots)s') % {
|
||||
'server_id': server_id,
|
||||
'host': dest_host,
|
||||
'writable': writable,
|
||||
'nondisruptive': nondisruptive,
|
||||
'preserve_snapshots': preserve_snapshots,
|
||||
}
|
||||
if new_share_network:
|
||||
cmd += ' --new-share-network %s' % new_share_network
|
||||
return self.manila(cmd)
|
||||
|
||||
def share_server_migration_complete(self, server_id):
|
||||
return self.manila('share-server-migration-complete %s' % server_id)
|
||||
|
||||
def share_server_migration_cancel(self, server_id):
|
||||
return self.manila('share-server-migration-cancel %s' % server_id)
|
||||
|
||||
def share_server_migration_get_progress(self, server_id):
|
||||
result = self.manila('share-server-migration-get-progress %s'
|
||||
% server_id)
|
||||
return output_parser.details(result)
|
||||
|
||||
def wait_for_server_migration_task_state(self, share_server_id, dest_host,
|
||||
task_state_to_wait,
|
||||
microversion=None):
|
||||
"""Waits for a certain server task state. """
|
||||
statuses = ((task_state_to_wait,)
|
||||
if not isinstance(task_state_to_wait, (tuple, list, set))
|
||||
else task_state_to_wait)
|
||||
server = self.get_share_server(share_server=share_server_id,
|
||||
microversion=microversion)
|
||||
start = int(time.time())
|
||||
while server['task_state'] not in statuses:
|
||||
time.sleep(self.build_interval)
|
||||
server = self.get_share_server(share_server=share_server_id,
|
||||
microversion=microversion)
|
||||
if server['task_state'] in statuses:
|
||||
return server
|
||||
elif server['task_state'] == constants.TASK_STATE_MIGRATION_ERROR:
|
||||
raise exceptions.ShareServerMigrationException(
|
||||
server_id=server['id'])
|
||||
elif int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Server %(share_server_id)s failed to reach the '
|
||||
'status in %(status)s while migrating from host '
|
||||
'%(src)s to host %(dest)s within the required time '
|
||||
'%(timeout)s.' % {
|
||||
'src': server['host'],
|
||||
'dest': dest_host,
|
||||
'share_server_id': server['id'],
|
||||
'timeout': self.build_timeout,
|
||||
'status': six.text_type(statuses),
|
||||
})
|
||||
raise tempest_lib_exc.TimeoutException(message)
|
||||
|
||||
# user messages
|
||||
|
||||
def wait_for_message(self, resource_id):
|
||||
|
@ -64,3 +64,7 @@ class AccessRuleDeleteErrorException(exceptions.TempestException):
|
||||
class ShareMigrationException(exceptions.TempestException):
|
||||
message = ("Share %(share_id)s failed to migrate from "
|
||||
"host %(src)s to host %(dest)s.")
|
||||
|
||||
|
||||
class ShareServerMigrationException(exceptions.TempestException):
|
||||
message = ("Share server %(server_id)s failed to migrate.")
|
||||
|
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
import ddt
|
||||
import testtools
|
||||
|
||||
@ -70,12 +71,11 @@ class ShareServersReadWriteBase(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
super(ShareServersReadWriteBase, self).setUp()
|
||||
if not CONF.run_share_servers_tests:
|
||||
message = "share-servers tests are disabled."
|
||||
message = "share-server tests are disabled."
|
||||
raise self.skipException(message)
|
||||
if self.protocol not in CONF.enable_protocols:
|
||||
message = "%s tests are disabled." % self.protocol
|
||||
raise self.skipException(message)
|
||||
|
||||
self.client = self.get_admin_client()
|
||||
if not self.client.share_network:
|
||||
message = "Can run only with DHSS=True mode"
|
||||
@ -204,10 +204,152 @@ class ShareServersReadWriteCIFSTest(ShareServersReadWriteBase):
|
||||
protocol = 'cifs'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@utils.skip_if_microversion_not_supported('2.57')
|
||||
class ShareServersMigrationBase(base.BaseTestCase):
|
||||
|
||||
protocol = None
|
||||
|
||||
def setUp(self):
|
||||
super(ShareServersMigrationBase, self).setUp()
|
||||
if not CONF.run_share_servers_tests:
|
||||
message = "Share-server tests are disabled."
|
||||
raise self.skipException(message)
|
||||
if self.protocol not in CONF.enable_protocols:
|
||||
message = "%s tests are disabled." % self.protocol
|
||||
raise self.skipException(message)
|
||||
self.client = self.get_admin_client()
|
||||
if not self.client.share_network:
|
||||
message = "Can run only with DHSS=True mode"
|
||||
raise self.skipException(message)
|
||||
if not CONF.run_share_servers_migration_tests:
|
||||
message = "Share server migration tests are disabled."
|
||||
raise self.skipException(message)
|
||||
|
||||
def _create_share_and_share_network(self):
|
||||
name = data_utils.rand_name('autotest_share_name')
|
||||
description = data_utils.rand_name('autotest_share_description')
|
||||
|
||||
common_share_network = self.client.get_share_network(
|
||||
self.client.share_network)
|
||||
share_net_info = utils.get_default_subnet(self.client,
|
||||
common_share_network['id'])
|
||||
|
||||
neutron_net_id = (
|
||||
share_net_info['neutron_net_id']
|
||||
if 'none' not in share_net_info['neutron_net_id'].lower()
|
||||
else None)
|
||||
neutron_subnet_id = (
|
||||
share_net_info['neutron_subnet_id']
|
||||
if 'none' not in share_net_info['neutron_subnet_id'].lower()
|
||||
else None)
|
||||
share_network = self.client.create_share_network(
|
||||
neutron_net_id=neutron_net_id,
|
||||
neutron_subnet_id=neutron_subnet_id,
|
||||
)
|
||||
share_type = self.create_share_type(
|
||||
data_utils.rand_name('test_share_type'),
|
||||
driver_handles_share_servers=True)
|
||||
|
||||
share = self.create_share(
|
||||
share_protocol=self.protocol,
|
||||
size=1,
|
||||
name=name,
|
||||
description=description,
|
||||
share_type=share_type['ID'],
|
||||
share_network=share_network['id'],
|
||||
client=self.client,
|
||||
wait_for_creation=True
|
||||
)
|
||||
share = self.client.get_share(share['id'])
|
||||
return share, share_network
|
||||
|
||||
@ddt.data('cancel', 'complete')
|
||||
def test_share_server_migration(self, operation):
|
||||
|
||||
# Create a share and share network to be used in the tests.
|
||||
share, share_network = self._create_share_and_share_network()
|
||||
share_server_id = share['share_server_id']
|
||||
src_host = share['host'].split('#')[0]
|
||||
pools = self.admin_client.pool_list(detail=True)
|
||||
|
||||
host_list = list()
|
||||
# Filter the backends DHSS True and different
|
||||
# than the source host.
|
||||
for hosts in pools:
|
||||
host_name = hosts['Name'].split('#')[0]
|
||||
if (ast.literal_eval(hosts['Capabilities']).get(
|
||||
'driver_handles_share_servers') and
|
||||
host_name != src_host):
|
||||
host_list.append(host_name)
|
||||
|
||||
host_list = list(set(host_list))
|
||||
# If not found any host we need skip the test.
|
||||
if len(host_list) == 0:
|
||||
raise self.skipException("No hosts available for "
|
||||
"share server migration.")
|
||||
|
||||
dest_backend = None
|
||||
# If found at least one host, we still need to verify the
|
||||
# share server migration compatibility with the destination host.
|
||||
for host in host_list:
|
||||
compatibility = self.admin_client.share_server_migration_check(
|
||||
server_id=share_server_id, dest_host=host,
|
||||
writable=False, nondisruptive=False, preserve_snapshots=False,
|
||||
new_share_network=None)
|
||||
# If found at least one compatible host, we will use it.
|
||||
if compatibility['compatible']:
|
||||
dest_host = host
|
||||
# If not found, we need skip the test.
|
||||
if dest_backend is not None:
|
||||
raise self.skipException("No hosts compatible to perform a "
|
||||
"share server migration.")
|
||||
|
||||
# Start the share server migration
|
||||
self.admin_client.share_server_migration_start(
|
||||
share_server_id, dest_host)
|
||||
|
||||
server = self.admin_client.get_share_server(share_server_id)
|
||||
share = self.admin_client.get_share(share['id'])
|
||||
self.assertEqual(constants.STATUS_SERVER_MIGRATING, share['status'])
|
||||
|
||||
# Wait for the share server migration driver phase 1 done.
|
||||
task_state = constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE
|
||||
server = self.admin_client.wait_for_server_migration_task_state(
|
||||
share_server_id, dest_host, task_state)
|
||||
|
||||
# Call share server migration complete or cancel operations
|
||||
# according the ddt.
|
||||
if operation == 'complete':
|
||||
self.admin_client.share_server_migration_complete(
|
||||
share_server_id)
|
||||
task_state = constants.TASK_STATE_MIGRATION_SUCCESS
|
||||
else:
|
||||
self.admin_client.share_server_migration_cancel(server['id'])
|
||||
task_state = constants.TASK_STATE_MIGRATION_CANCELLED
|
||||
|
||||
# Wait for the respectives task state for each operation above.
|
||||
server = self.admin_client.wait_for_server_migration_task_state(
|
||||
server['id'], dest_host, task_state)
|
||||
# Check if the share is available again.
|
||||
share = self.admin_client.get_share(share['id'])
|
||||
self.assertEqual('available', share['status'])
|
||||
|
||||
|
||||
class ShareServersMigrationNFSTest(ShareServersMigrationBase):
|
||||
protocol = 'nfs'
|
||||
|
||||
|
||||
class ShareServersMigrationCIFSTest(ShareServersMigrationBase):
|
||||
protocol = 'cifs'
|
||||
|
||||
|
||||
def load_tests(loader, tests, _):
|
||||
result = []
|
||||
for test_case in tests:
|
||||
if type(test_case._tests[0]) is ShareServersReadWriteBase:
|
||||
continue
|
||||
if type(test_case._tests[0]) is ShareServersMigrationBase:
|
||||
continue
|
||||
result.append(test_case)
|
||||
return loader.suiteClass(result)
|
||||
|
@ -454,6 +454,33 @@ class FakeHTTPClient(fakes.FakeHTTPClient):
|
||||
}
|
||||
elif action in ('unmanage', ):
|
||||
assert 'force' in body[action]
|
||||
elif action in (
|
||||
'migration_cancel', 'migration_complete',
|
||||
'migration_get_progress'):
|
||||
assert body[action] is None
|
||||
if 'migration_get_progress' == action:
|
||||
_body = {'total_progress': 50,
|
||||
'task_state': 'fake_task_state',
|
||||
'destination_share_server_id': 'fake_dest_id'}
|
||||
return 200, {}, _body
|
||||
elif 'migration_complete' == action:
|
||||
_body = {'destination_share_server_id': 'fake_dest_id'}
|
||||
return 200, {}, _body
|
||||
elif action in (
|
||||
'migration_start', 'migration_check'):
|
||||
assert 'host' in body[action]
|
||||
if 'migration-check':
|
||||
_body = {
|
||||
'compatible': True,
|
||||
'capacity': True,
|
||||
'capability': True,
|
||||
'writable': True,
|
||||
'nondisruptive': True,
|
||||
'preserve_snapshots': True,
|
||||
}
|
||||
return 200, {}, _body
|
||||
elif action == 'reset_task_state':
|
||||
assert 'task_state' in body[action]
|
||||
|
||||
resp = 202
|
||||
result = (resp, {}, _body)
|
||||
|
@ -201,3 +201,101 @@ class ShareServerManagerTest(utils.TestCase):
|
||||
self.manager._get.assert_called_once_with(
|
||||
"%s/%s/details" % (share_servers.RESOURCES_PATH,
|
||||
share_server_id), 'details')
|
||||
|
||||
def test_migration_check(self):
|
||||
share_server = "fake_share_server"
|
||||
host = "fake_host"
|
||||
returned = {
|
||||
'compatible': True,
|
||||
'capacity': True,
|
||||
'capability': True,
|
||||
'writable': True,
|
||||
'nondisruptive': True,
|
||||
'preserve_snapshots': True,
|
||||
}
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=['200', returned])):
|
||||
result = self.manager.migration_check(
|
||||
share_server, host, writable=True, nondisruptive=True,
|
||||
preserve_snapshots=True)
|
||||
self.manager._action.assert_called_once_with(
|
||||
'migration_check', share_server, {
|
||||
"host": host,
|
||||
"writable": True,
|
||||
"nondisruptive": True,
|
||||
"preserve_snapshots": True,
|
||||
"new_share_network_id": None,
|
||||
})
|
||||
|
||||
self.assertEqual(returned, result)
|
||||
|
||||
def test_migration_start(self):
|
||||
share_server = "fake_share_server"
|
||||
host = "fake_host"
|
||||
returned = "fake"
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=returned)):
|
||||
result = self.manager.migration_start(
|
||||
share_server, host, writable=True, nondisruptive=True,
|
||||
preserve_snapshots=True)
|
||||
self.manager._action.assert_called_once_with(
|
||||
'migration_start', share_server, {
|
||||
"host": host,
|
||||
"writable": True,
|
||||
"nondisruptive": True,
|
||||
"preserve_snapshots": True,
|
||||
"new_share_network_id": None,
|
||||
})
|
||||
|
||||
self.assertEqual(returned, result)
|
||||
|
||||
def test_migration_complete(self):
|
||||
share_server = "fake_share_server"
|
||||
returned = "fake"
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=['200', returned])):
|
||||
result = self.manager.migration_complete(share_server)
|
||||
|
||||
self.manager._action.assert_called_once_with(
|
||||
"migration_complete", share_server)
|
||||
self.assertEqual(returned, result)
|
||||
|
||||
def test_migration_get_progress(self):
|
||||
share_server = "fake_share_server"
|
||||
returned = "fake"
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=['200', returned])):
|
||||
result = self.manager.migration_get_progress(share_server)
|
||||
|
||||
self.manager._action.assert_called_once_with(
|
||||
"migration_get_progress", share_server)
|
||||
self.assertEqual(returned, result)
|
||||
|
||||
def test_reset_task_state(self):
|
||||
share_server = "fake_share_server"
|
||||
state = "fake_state"
|
||||
returned = "fake"
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=returned)):
|
||||
result = self.manager.reset_task_state(share_server, state)
|
||||
|
||||
self.manager._action.assert_called_once_with(
|
||||
"reset_task_state", share_server, {'task_state': state})
|
||||
self.assertEqual(returned, result)
|
||||
|
||||
def test_migration_cancel(self):
|
||||
share_server = "fake_share_server"
|
||||
returned = "fake"
|
||||
|
||||
with mock.patch.object(self.manager, "_action",
|
||||
mock.Mock(return_value=returned)):
|
||||
result = self.manager.migration_cancel(share_server)
|
||||
|
||||
self.manager._action.assert_called_once_with(
|
||||
"migration_cancel", share_server)
|
||||
self.assertEqual(returned, result)
|
||||
|
@ -3464,3 +3464,38 @@ class ShellTest(test_utils.TestCase):
|
||||
expected = {'unmanage': {'force': False}}
|
||||
self.assert_called('POST', '/share-servers/1234/action',
|
||||
body=expected)
|
||||
|
||||
@ddt.data('migration-start', 'migration-check')
|
||||
def test_share_server_migration_start_and_check(self, method):
|
||||
command = ("share-server-%s "
|
||||
"1234 host@backend --new-share-network 1111 "
|
||||
"--writable False --nondisruptive True "
|
||||
"--preserve-snapshots True" %
|
||||
method)
|
||||
self.run_command(command)
|
||||
method = method.replace('-', '_')
|
||||
expected = {method: {
|
||||
'host': 'host@backend',
|
||||
'writable': 'False',
|
||||
'nondisruptive': 'True',
|
||||
'preserve_snapshots': 'True',
|
||||
'new_share_network_id': 1111
|
||||
}}
|
||||
self.assert_called('POST', '/share-servers/1234/action', body=expected)
|
||||
|
||||
@ddt.data('migration-complete', 'migration-get-progress',
|
||||
'migration-cancel')
|
||||
def test_share_server_migration_others(self, method):
|
||||
command = 'share-server-' + ' '.join((method, '1234'))
|
||||
self.run_command(command)
|
||||
expected = {method.replace('-', '_'): None}
|
||||
self.assert_called('POST', '/share-servers/1234/action', body=expected)
|
||||
|
||||
@ddt.data('migration_error', 'migration_success', None)
|
||||
def test_share_server_reset_task_state(self, param):
|
||||
command = ' '.join(('share-server-reset-task-state --state',
|
||||
six.text_type(param),
|
||||
'1234'))
|
||||
self.run_command(command)
|
||||
expected = {'reset_task_state': {'task_state': param}}
|
||||
self.assert_called('POST', '/share-servers/1234/action', body=expected)
|
||||
|
@ -46,6 +46,36 @@ class ShareServer(common_base.Resource):
|
||||
"""Update the share server with the provided state."""
|
||||
self.manager.reset_state(self, state)
|
||||
|
||||
def migration_check(self, host, writable, nondisruptive,
|
||||
preserve_snapshots, new_share_network_id=None):
|
||||
"""Check if the new host is suitable for migration."""
|
||||
return self.manager.migration_check(
|
||||
self, host, writable, nondisruptive,
|
||||
preserve_snapshots, new_share_network_id=new_share_network_id)
|
||||
|
||||
def migration_start(self, host, writable, nondisruptive,
|
||||
preserve_snapshots, new_share_network_id=None):
|
||||
"""Migrate the share server to a new host."""
|
||||
self.manager.migration_start(
|
||||
self, host, writable, nondisruptive,
|
||||
preserve_snapshots, new_share_network_id=new_share_network_id)
|
||||
|
||||
def migration_complete(self):
|
||||
"""Complete migration of a share server."""
|
||||
return self.manager.migration_complete(self)
|
||||
|
||||
def migration_cancel(self):
|
||||
"""Attempts to cancel migration of a share server."""
|
||||
self.manager.migration_cancel(self)
|
||||
|
||||
def migration_get_progress(self):
|
||||
"""Obtain progress of migration of a share server."""
|
||||
return self.manager.migration_get_progress(self)
|
||||
|
||||
def reset_task_state(self, task_state):
|
||||
"""Reset the task state of a given share server."""
|
||||
self.manager.reset_task_state(self, task_state)
|
||||
|
||||
|
||||
class ShareServerManager(base.ManagerWithFind):
|
||||
"""Manage :class:`ShareServer` resources."""
|
||||
@ -148,9 +178,97 @@ class ShareServerManager(base.ManagerWithFind):
|
||||
:param action: text with action name.
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
:param info: dict with data for specified 'action'.
|
||||
:param kwargs: dict with data to be provided for action hooks.
|
||||
"""
|
||||
body = {action: info}
|
||||
self.run_hooks('modify_body_for_action', body)
|
||||
url = ACTION_PATH % common_base.getid(share_server)
|
||||
return self.api.client.post(url, body=body)
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def migration_check(self, share_server, host, writable, nondisruptive,
|
||||
preserve_snapshots, new_share_network_id=None):
|
||||
"""Check the share server migration to a new host
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
:param host: Destination host where share server will be migrated.
|
||||
:param writable: Enforces migration to keep the shares writable.
|
||||
:param nondisruptive: Enforces migration to be nondisruptive.
|
||||
:param preserve_snapshots: Enforces migration to preserve snapshots.
|
||||
:param new_share_network_id: Specify the new share network id.
|
||||
"""
|
||||
result = self._action(
|
||||
"migration_check", share_server, {
|
||||
"host": host,
|
||||
"preserve_snapshots": preserve_snapshots,
|
||||
"writable": writable,
|
||||
"nondisruptive": nondisruptive,
|
||||
"new_share_network_id": new_share_network_id,
|
||||
})
|
||||
return result[1]
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def migration_start(self, share_server, host, writable,
|
||||
nondisruptive, preserve_snapshots,
|
||||
new_share_network_id=None):
|
||||
"""Migrates share server to a new host
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
:param host: Destination host where share server will be migrated.
|
||||
:param writable: Enforces migration to keep the shares writable.
|
||||
:param nondisruptive: Enforces migration to be nondisruptive.
|
||||
:param preserve_snapshots: Enforces migration to preserve snapshots.
|
||||
:param new_share_network_id: Specify the new share network id.
|
||||
"""
|
||||
return self._action(
|
||||
"migration_start", share_server, {
|
||||
"host": host,
|
||||
"writable": writable,
|
||||
"nondisruptive": nondisruptive,
|
||||
"preserve_snapshots": preserve_snapshots,
|
||||
"new_share_network_id": new_share_network_id,
|
||||
})
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def reset_task_state(self, share_server, task_state):
|
||||
"""Update the provided share server with the provided task state.
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
:param task_state: text with new task state to set for share.
|
||||
"""
|
||||
return self._action('reset_task_state', share_server,
|
||||
{"task_state": task_state})
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def migration_complete(self, share_server):
|
||||
"""Completes migration for a given share server.
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
"""
|
||||
result = self._action('migration_complete', share_server)
|
||||
# NOTE(dviroel): result[0] is response code, result[1] is dict body
|
||||
return result[1]
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def migration_cancel(self, share_server):
|
||||
"""Attempts to cancel migration for a given share server.
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
"""
|
||||
return self._action('migration_cancel',
|
||||
share_server)
|
||||
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def migration_get_progress(self, share_server):
|
||||
"""Obtains progress of share migration for a given share server.
|
||||
|
||||
:param share_server: either share_server object or text with its ID.
|
||||
"""
|
||||
result = self._action('migration_get_progress', share_server)
|
||||
# NOTE(felipefutty): result[0] is response code, result[1] is dict body
|
||||
return result[1]
|
||||
|
@ -1001,6 +1001,200 @@ def do_migration_get_progress(cs, args):
|
||||
cliutils.print_dict(result[1])
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of the share server to check if the migration is possible.')
|
||||
@cliutils.arg(
|
||||
'host',
|
||||
metavar='<host@backend>',
|
||||
help="Destination to migrate the share server to. Use the format "
|
||||
"'<node_hostname>@<backend_name>'.")
|
||||
@cliutils.arg(
|
||||
'--preserve-snapshots',
|
||||
'--preserve_snapshots',
|
||||
action='single_alias',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Set to True if snapshots must be preserved at the migration "
|
||||
"destination.")
|
||||
@cliutils.arg(
|
||||
'--writable',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Set to True if shares associated with the share server must be "
|
||||
"writable through the first phase of the migration.")
|
||||
@cliutils.arg(
|
||||
'--nondisruptive',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Set to True if migration must be non disruptive to clients that are "
|
||||
"using the shares associated with the share server through both "
|
||||
"phases of the migration.")
|
||||
@cliutils.arg(
|
||||
'--new_share_network',
|
||||
'--new-share-network',
|
||||
metavar='<new_share_network>',
|
||||
action='single_alias',
|
||||
required=False,
|
||||
help="New share network to migrate to. Optional, default=None.",
|
||||
default=None)
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_migration_check(cs, args):
|
||||
"""Check migration compatibility for a share server with desired properties
|
||||
|
||||
(Admin only, Experimental).
|
||||
"""
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
|
||||
new_share_net_id = None
|
||||
if args.new_share_network:
|
||||
share_net = _find_share_network(cs, args.new_share_network)
|
||||
new_share_net_id = share_net.id
|
||||
result = share_server.migration_check(
|
||||
args.host, args.writable, args.nondisruptive, args.preserve_snapshots,
|
||||
new_share_net_id)
|
||||
cliutils.print_dict(result)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of the share server to migrate.')
|
||||
@cliutils.arg(
|
||||
'host',
|
||||
metavar='<host@backend>',
|
||||
help="Destination to migrate the share server to. Use the format "
|
||||
"'<node_hostname>@<backend_name>'.")
|
||||
@cliutils.arg(
|
||||
'--preserve-snapshots',
|
||||
'--preserve_snapshots',
|
||||
action='single_alias',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Set to True if snapshots must be preserved at the migration "
|
||||
"destination.")
|
||||
@cliutils.arg(
|
||||
'--writable',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Enforces migration to keep all its shares writable while contents "
|
||||
"are being moved.")
|
||||
@cliutils.arg(
|
||||
'--nondisruptive',
|
||||
metavar='<True|False>',
|
||||
choices=['True', 'False'],
|
||||
required=True,
|
||||
help="Enforces migration to be nondisruptive.")
|
||||
@cliutils.arg(
|
||||
'--new_share_network',
|
||||
'--new-share-network',
|
||||
metavar='<new_share_network>',
|
||||
action='single_alias',
|
||||
required=False,
|
||||
help='Specify a new share network for the share server. Do not '
|
||||
'specify this parameter if the migrating share server has '
|
||||
'to be retained within its current share network.',
|
||||
default=None)
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_migration_start(cs, args):
|
||||
"""Migrates share server to a new host (Admin only, Experimental)."""
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
|
||||
new_share_net_id = None
|
||||
if args.new_share_network:
|
||||
share_net = _find_share_network(cs, args.new_share_network)
|
||||
new_share_net_id = share_net.id
|
||||
share_server.migration_start(args.host, args.writable, args.nondisruptive,
|
||||
args.preserve_snapshots, new_share_net_id)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of share server to complete migration.')
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_migration_complete(cs, args):
|
||||
"""Completes migration for a given share server
|
||||
|
||||
(Admin only,Experimental).
|
||||
"""
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
result = share_server.migration_complete()
|
||||
cliutils.print_dict(result)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of share server to complete migration.')
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_migration_cancel(cs, args):
|
||||
"""Cancels migration of a given share server when copying
|
||||
|
||||
(Admin only, Experimental).
|
||||
"""
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
share_server.migration_cancel()
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of share server to complete migration.')
|
||||
@cliutils.arg(
|
||||
'--task-state',
|
||||
'--task_state',
|
||||
'--state',
|
||||
metavar='<task_state>',
|
||||
default='None',
|
||||
action='single_alias',
|
||||
required=False,
|
||||
help=('Indicate which task state to assign the share server. Options: '
|
||||
'migration_starting, migration_in_progress, migration_completing, '
|
||||
'migration_success, migration_error, migration_cancel_in_progress, '
|
||||
'migration_cancelled, migration_driver_in_progress, '
|
||||
'migration_driver_phase1_done. If no value is provided, None will '
|
||||
'be used.'))
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_reset_task_state(cs, args):
|
||||
"""Explicitly update the task state of a share
|
||||
|
||||
(Admin only, Experimental).
|
||||
"""
|
||||
state = args.task_state
|
||||
if args.task_state == 'None':
|
||||
state = None
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
share_server.reset_task_state(state)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share_server_id',
|
||||
metavar='<share_server_id>',
|
||||
help='ID of share server to complete migration.')
|
||||
@api_versions.wraps("2.57")
|
||||
@api_versions.experimental_api
|
||||
def do_share_server_migration_get_progress(cs, args):
|
||||
"""Gets migration progress of a given share server when copying
|
||||
|
||||
(Admin only, Experimental).
|
||||
"""
|
||||
share_server = _find_share_server(cs, args.share_server_id)
|
||||
result = share_server.migration_get_progress()
|
||||
cliutils.print_dict(result)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'share',
|
||||
metavar='<share>',
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added support for performing the share server migration. The new commands
|
||||
are: `share-server-migration-start`, `share-server-migration-complete`,
|
||||
`share-server-migration-cancel`, `share-server-migration-get-progress`,
|
||||
`share-server-migration-check` and `share-server-reset-task-state`.
|
@ -30,6 +30,7 @@
|
||||
iniset {{ manilaclient_config }} DEFAULT run_migration_tests true
|
||||
iniset {{ manilaclient_config }} DEFAULT run_manage_tests true
|
||||
iniset {{ manilaclient_config }} DEFAULT run_mount_snapshot_tests true
|
||||
iniset {{ manilaclient_config }} DEFAULT run_share_servers_migration_tests true
|
||||
args:
|
||||
executable: "/bin/bash"
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
MANILA_INSTALL_TEMPEST_PLUGIN_SYSTEMWIDE: false
|
||||
MANILA_SERVICE_IMAGE_ENABLED: false
|
||||
MANILA_SHARE_MIGRATION_PERIOD_TASK_INTERVAL: 1
|
||||
MANILA_SERVER_MIGRATION_PERIOD_TASK_INTERVAL: 10
|
||||
SHARE_DRIVER: manila.tests.share.drivers.dummy.DummyDriver
|
||||
MANILA_REPLICA_STATE_UPDATE_INTERVAL: 10
|
||||
MANILA_ENABLED_BACKENDS: alpha,beta,gamma,delta
|
||||
|
Loading…
Reference in New Issue
Block a user