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
|
||||
|
||||
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
|
||||
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 = {
|
||||
'config': ConfigCommands,
|
||||
'db': DbCommands,
|
||||
'host': HostCommands,
|
||||
'logs': GetLogCommands,
|
||||
'service': ServiceCommands,
|
||||
'share': ShareCommands,
|
||||
'shell': ShellCommands,
|
||||
'version': VersionCommands
|
||||
}
|
||||
|
@ -325,6 +325,11 @@ def share_instance_update(context, instance_id, values, with_share_data=False):
|
||||
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):
|
||||
"""Returns all share instances."""
|
||||
return IMPL.share_instances_get_all(context, filters=filters)
|
||||
|
@ -1339,6 +1339,20 @@ def _share_instance_create(context, share_id, values, 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
|
||||
def share_instance_update(context, share_instance_id, values,
|
||||
with_share_data=False):
|
||||
|
@ -45,6 +45,7 @@ class ManilaCmdManageTestCase(test.TestCase):
|
||||
self.config_commands = manila_manage.ConfigCommands()
|
||||
self.get_log_cmds = manila_manage.GetLogCommands()
|
||||
self.service_cmds = manila_manage.ServiceCommands()
|
||||
self.share_cmds = manila_manage.ShareCommands()
|
||||
|
||||
def test_param2id_is_uuid_like(self):
|
||||
obj_id = '12345678123456781234567812345678'
|
||||
@ -376,3 +377,44 @@ class ManilaCmdManageTestCase(test.TestCase):
|
||||
def test_get_arg_string(self, arg):
|
||||
parsed_arg = manila_manage.get_arg_string(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.assertEqual(value_2, actual_data['info_hash'])
|
||||
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