Add command to update share instance hosts
usage: manila-manage share update_host --currenthost CURRENTHOST --newhost NEWHOST [--force FORCE] --currenthost CURRENTHOST Current share host name. --newhost NEWHOST New share host name. --force FORCE Ignore validations. A fully qualified host string is of the format 'HostA@BackendB#PoolC'. The command allows making substring modifications to update the host or the host and backend. To avoid making gratuitous changes, the complete host string must be specified. Change-Id: I0f30ab6135c8c438860341cc68b512311e9c4711
This commit is contained in:
parent
94db9c7efa
commit
dbf97098e1
@ -75,6 +75,16 @@ from manila import version
|
|||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
HOST_UPDATE_HELP_MSG = ("A fully qualified host string is of the format "
|
||||||
|
"'HostA@BackendB#PoolC'. Provide only the host name "
|
||||||
|
"(ex: 'HostA') to update the hostname part of "
|
||||||
|
"the host string. Provide only the "
|
||||||
|
"host name and backend name (ex: 'HostA@BackendB') to "
|
||||||
|
"update the host and backend names.")
|
||||||
|
HOST_UPDATE_CURRENT_HOST_HELP = ("Current share host name. %s" %
|
||||||
|
HOST_UPDATE_HELP_MSG)
|
||||||
|
HOST_UPDATE_NEW_HOST_HELP = "New share host name. %s" % HOST_UPDATE_HELP_MSG
|
||||||
|
|
||||||
|
|
||||||
# Decorators for actions
|
# Decorators for actions
|
||||||
def args(*args, **kwargs):
|
def args(*args, **kwargs):
|
||||||
@ -355,12 +365,47 @@ class ServiceCommands(object):
|
|||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
class ShareCommands(object):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _validate_hosts(current_host, new_host):
|
||||||
|
err = None
|
||||||
|
if '@' in current_host:
|
||||||
|
if '#' in current_host and '#' not in new_host:
|
||||||
|
err = "%(chost)s specifies a pool but %(nhost)s does not."
|
||||||
|
elif '@' not in new_host:
|
||||||
|
err = "%(chost)s specifies a backend but %(nhost)s does not."
|
||||||
|
if err:
|
||||||
|
print(err % {'chost': current_host, 'nhost': new_host})
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
@args('--currenthost', required=True, help=HOST_UPDATE_CURRENT_HOST_HELP)
|
||||||
|
@args('--newhost', required=True, help=HOST_UPDATE_NEW_HOST_HELP)
|
||||||
|
@args('--force', required=False, type=bool, default=False,
|
||||||
|
help="Ignore validations.")
|
||||||
|
def update_host(self, current_host, new_host, force=False):
|
||||||
|
"""Modify the host name associated with a share.
|
||||||
|
|
||||||
|
Particularly to recover from cases where one has moved
|
||||||
|
their Manila Share node, or modified their 'host' opt
|
||||||
|
or their backend section name in the manila configuration file.
|
||||||
|
"""
|
||||||
|
if not force:
|
||||||
|
self._validate_hosts(current_host, new_host)
|
||||||
|
ctxt = context.get_admin_context()
|
||||||
|
updated = db.share_instances_host_update(ctxt, current_host, new_host)
|
||||||
|
print("Updated host of %(count)s share instances on %(chost)s "
|
||||||
|
"to %(nhost)s." % {'count': updated, 'chost': current_host,
|
||||||
|
'nhost': new_host})
|
||||||
|
|
||||||
|
|
||||||
CATEGORIES = {
|
CATEGORIES = {
|
||||||
'config': ConfigCommands,
|
'config': ConfigCommands,
|
||||||
'db': DbCommands,
|
'db': DbCommands,
|
||||||
'host': HostCommands,
|
'host': HostCommands,
|
||||||
'logs': GetLogCommands,
|
'logs': GetLogCommands,
|
||||||
'service': ServiceCommands,
|
'service': ServiceCommands,
|
||||||
|
'share': ShareCommands,
|
||||||
'shell': ShellCommands,
|
'shell': ShellCommands,
|
||||||
'version': VersionCommands
|
'version': VersionCommands
|
||||||
}
|
}
|
||||||
|
@ -325,6 +325,11 @@ def share_instance_update(context, instance_id, values, with_share_data=False):
|
|||||||
with_share_data=with_share_data)
|
with_share_data=with_share_data)
|
||||||
|
|
||||||
|
|
||||||
|
def share_instances_host_update(context, current_host, new_host):
|
||||||
|
"""Update the host attr of all share instances that are on current_host."""
|
||||||
|
return IMPL.share_instances_host_update(context, current_host, new_host)
|
||||||
|
|
||||||
|
|
||||||
def share_instances_get_all(context, filters=None):
|
def share_instances_get_all(context, filters=None):
|
||||||
"""Returns all share instances."""
|
"""Returns all share instances."""
|
||||||
return IMPL.share_instances_get_all(context, filters=filters)
|
return IMPL.share_instances_get_all(context, filters=filters)
|
||||||
|
@ -1339,6 +1339,20 @@ def _share_instance_create(context, share_id, values, session):
|
|||||||
session=session)
|
session=session)
|
||||||
|
|
||||||
|
|
||||||
|
@require_admin_context
|
||||||
|
def share_instances_host_update(context, current_host, new_host):
|
||||||
|
session = get_session()
|
||||||
|
host_field = models.ShareInstance.host
|
||||||
|
with session.begin():
|
||||||
|
query = model_query(
|
||||||
|
context, models.ShareInstance, session=session, read_deleted="no",
|
||||||
|
).filter(host_field.like('{}%'.format(current_host)))
|
||||||
|
result = query.update(
|
||||||
|
{host_field: func.replace(host_field, current_host, new_host)},
|
||||||
|
synchronize_session=False)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def share_instance_update(context, share_instance_id, values,
|
def share_instance_update(context, share_instance_id, values,
|
||||||
with_share_data=False):
|
with_share_data=False):
|
||||||
|
@ -45,6 +45,7 @@ class ManilaCmdManageTestCase(test.TestCase):
|
|||||||
self.config_commands = manila_manage.ConfigCommands()
|
self.config_commands = manila_manage.ConfigCommands()
|
||||||
self.get_log_cmds = manila_manage.GetLogCommands()
|
self.get_log_cmds = manila_manage.GetLogCommands()
|
||||||
self.service_cmds = manila_manage.ServiceCommands()
|
self.service_cmds = manila_manage.ServiceCommands()
|
||||||
|
self.share_cmds = manila_manage.ShareCommands()
|
||||||
|
|
||||||
def test_param2id_is_uuid_like(self):
|
def test_param2id_is_uuid_like(self):
|
||||||
obj_id = '12345678123456781234567812345678'
|
obj_id = '12345678123456781234567812345678'
|
||||||
@ -376,3 +377,44 @@ class ManilaCmdManageTestCase(test.TestCase):
|
|||||||
def test_get_arg_string(self, arg):
|
def test_get_arg_string(self, arg):
|
||||||
parsed_arg = manila_manage.get_arg_string(arg)
|
parsed_arg = manila_manage.get_arg_string(arg)
|
||||||
self.assertEqual('bar', parsed_arg)
|
self.assertEqual('bar', parsed_arg)
|
||||||
|
|
||||||
|
@ddt.data({'current_host': 'controller-0@fancystore01#pool100',
|
||||||
|
'new_host': 'controller-0@fancystore01'},
|
||||||
|
{'current_host': 'controller-0@fancystore01',
|
||||||
|
'new_host': 'controller-0'})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_share_update_host_fail_validation(self, current_host, new_host):
|
||||||
|
self.mock_object(context, 'get_admin_context',
|
||||||
|
mock.Mock(return_value='admin_ctxt'))
|
||||||
|
self.mock_object(db, 'share_instances_host_update')
|
||||||
|
|
||||||
|
self.assertRaises(SystemExit,
|
||||||
|
self.share_cmds.update_host,
|
||||||
|
current_host, new_host)
|
||||||
|
|
||||||
|
self.assertFalse(db.share_instances_host_update.called)
|
||||||
|
|
||||||
|
@ddt.data({'current_host': 'controller-0@fancystore01#pool100',
|
||||||
|
'new_host': 'controller-0@fancystore02#pool0'},
|
||||||
|
{'current_host': 'controller-0@fancystore01',
|
||||||
|
'new_host': 'controller-1@fancystore01'},
|
||||||
|
{'current_host': 'controller-0',
|
||||||
|
'new_host': 'controller-1'},
|
||||||
|
{'current_host': 'controller-0@fancystore01#pool100',
|
||||||
|
'new_host': 'controller-1@fancystore02', 'force': True})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_share_update_host(self, current_host, new_host, force=False):
|
||||||
|
self.mock_object(context, 'get_admin_context',
|
||||||
|
mock.Mock(return_value='admin_ctxt'))
|
||||||
|
self.mock_object(db, 'share_instances_host_update',
|
||||||
|
mock.Mock(return_value=20))
|
||||||
|
|
||||||
|
with mock.patch('sys.stdout', new=six.StringIO()) as intercepted_op:
|
||||||
|
self.share_cmds.update_host(current_host, new_host, force)
|
||||||
|
|
||||||
|
expected_op = ("Updated host of 20 share instances on "
|
||||||
|
"%(chost)s to %(nhost)s." %
|
||||||
|
{'chost': current_host, 'nhost': new_host})
|
||||||
|
self.assertEqual(expected_op, intercepted_op.getvalue().strip())
|
||||||
|
db.share_instances_host_update.assert_called_once_with(
|
||||||
|
'admin_ctxt', current_host, new_host)
|
||||||
|
@ -3133,3 +3133,91 @@ class BackendInfoDatabaseAPITestCase(test.TestCase):
|
|||||||
self.assertIsNone(initial_data)
|
self.assertIsNone(initial_data)
|
||||||
self.assertEqual(value_2, actual_data['info_hash'])
|
self.assertEqual(value_2, actual_data['info_hash'])
|
||||||
self.assertEqual(host, actual_data['host'])
|
self.assertEqual(host, actual_data['host'])
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class ShareInstancesTestCase(test.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ShareInstancesTestCase, self).setUp()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
|
@ddt.data('controller-100', 'controller-0@otherstore03',
|
||||||
|
'controller-0@otherstore01#pool200')
|
||||||
|
def test_share_instances_host_update_no_matches(self, current_host):
|
||||||
|
share_id = uuidutils.generate_uuid()
|
||||||
|
if '@' in current_host:
|
||||||
|
if '#' in current_host:
|
||||||
|
new_host = 'new-controller-X@backendX#poolX'
|
||||||
|
else:
|
||||||
|
new_host = 'new-controller-X@backendX'
|
||||||
|
else:
|
||||||
|
new_host = 'new-controller-X'
|
||||||
|
instances = [
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-0@fancystore01#pool100',
|
||||||
|
status=constants.STATUS_AVAILABLE),
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-0@otherstore02#pool100',
|
||||||
|
status=constants.STATUS_ERROR),
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-2@beststore07#pool200',
|
||||||
|
status=constants.STATUS_DELETING),
|
||||||
|
]
|
||||||
|
db_utils.create_share(id=share_id, instances=instances)
|
||||||
|
|
||||||
|
updates = db_api.share_instances_host_update(self.context,
|
||||||
|
current_host,
|
||||||
|
new_host)
|
||||||
|
|
||||||
|
share_instances = db_api.share_instances_get_all(
|
||||||
|
self.context, filters={'share_id': share_id})
|
||||||
|
self.assertEqual(0, updates)
|
||||||
|
for share_instance in share_instances:
|
||||||
|
self.assertTrue(not share_instance['host'].startswith(new_host))
|
||||||
|
|
||||||
|
@ddt.data({'current_host': 'controller-2', 'expected_updates': 1},
|
||||||
|
{'current_host': 'controller-0@fancystore01',
|
||||||
|
'expected_updates': 2},
|
||||||
|
{'current_host': 'controller-0@fancystore01#pool100',
|
||||||
|
'expected_updates': 1})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_share_instance_host_update_partial_matches(self, current_host,
|
||||||
|
expected_updates):
|
||||||
|
share_id = uuidutils.generate_uuid()
|
||||||
|
if '@' in current_host:
|
||||||
|
if '#' in current_host:
|
||||||
|
new_host = 'new-controller-X@backendX#poolX'
|
||||||
|
else:
|
||||||
|
new_host = 'new-controller-X@backendX'
|
||||||
|
else:
|
||||||
|
new_host = 'new-controller-X'
|
||||||
|
instances = [
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-0@fancystore01#pool100',
|
||||||
|
status=constants.STATUS_AVAILABLE),
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-0@fancystore01#pool200',
|
||||||
|
status=constants.STATUS_ERROR),
|
||||||
|
db_utils.create_share_instance(
|
||||||
|
share_id=share_id,
|
||||||
|
host='controller-2@beststore07#pool200',
|
||||||
|
status=constants.STATUS_DELETING),
|
||||||
|
]
|
||||||
|
db_utils.create_share(id=share_id, instances=instances)
|
||||||
|
|
||||||
|
actual_updates = db_api.share_instances_host_update(
|
||||||
|
self.context, current_host, new_host)
|
||||||
|
|
||||||
|
share_instances = db_api.share_instances_get_all(
|
||||||
|
self.context, filters={'share_id': share_id})
|
||||||
|
|
||||||
|
host_updates = [si for si in share_instances if
|
||||||
|
si['host'].startswith(new_host)]
|
||||||
|
self.assertEqual(actual_updates, expected_updates)
|
||||||
|
self.assertEqual(expected_updates, len(host_updates))
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- The ``manila-manage`` utility now has a new command to update the host
|
||||||
|
attribute of shares. This is useful when the share manager process
|
||||||
|
has been migrated to a different host, or if changes are made to the
|
||||||
|
``host`` config option or the backend section name in ``manila.conf``.
|
||||||
|
Execute ``manila-manage share update_host -h`` to see usage instructions.
|
Loading…
Reference in New Issue
Block a user