Add module-instance-count command

Added a --count_only flag to the call for module instances
to return a summary of the applied instances
based on the MD5 of the module (this is most useful
for live_update modules, to see which ones haven't been
updated).

Added a new module-instance-count command.  This was done
to facilitate getting the summary, since it returns
a different result set.  It basically calls the
same python interface as module-instances, but adds
the --count_only flag.

Also added some missing tests.

Change-Id: Iea661166bf3a4f3520a590da5954aedcd0036243
Partial-Bug: #1554900
Depends-On: I4caf4a57226dd711575cde766076fa25d16792e2
This commit is contained in:
Peter Stachowski 2016-11-04 10:45:27 -04:00
parent d273969fa3
commit 714c6e781c
7 changed files with 137 additions and 6 deletions

View File

@ -0,0 +1,6 @@
---
fixes:
- Add module-instance-count support to list
a count of all instances having a specific
module applied. Bug 1554900

View File

@ -178,6 +178,17 @@ class FakeHTTPClient(base_client.HTTPClient):
"region": "regionOne",
"datastore": {"version": "5.6", "type": "mysql"}}]})
def get_instance_counts(self, **kw):
return (200, {}, {"instances": [
{
"module_id": "4321",
"module_name": "mod1",
"min_date": "2015-05-02T11:06:16",
"max_date": "2015-05-02T11:06:19",
"module_md5": "9db783b92a9355f70c41806659fcb77d",
"current": True,
"count": 1}]})
def get_instances_1234(self, **kw):
r = {'instance': self.get_instances()[2]['instances'][0]}
return (200, {}, r)
@ -479,6 +490,8 @@ class FakeHTTPClient(base_client.HTTPClient):
return (200, {}, {"modules": [{"module": {}}]})
def get_modules_4321_instances(self, **kw):
if kw.get('count_only', False):
return self.get_instance_counts()
return self.get_instances()
def get_instances_modules(self, **kw):

View File

@ -233,6 +233,56 @@ class InstancesTest(testtools.TestCase):
resp.status_code = 500
self.assertRaises(Exception, self.instances.edit, 'instance1')
def test_module_apply(self):
resp = mock.Mock()
resp.status_code = 200
body = {'modules': []}
self.instances.api.client.post = mock.Mock(return_value=(resp, body))
self.instances.module_apply(self.instance_with_id, "mod_id")
resp.status_code = 500
self.assertRaises(Exception, self.instances.module_apply,
'instance1', 'mod1')
def test_module_remove(self):
resp = mock.Mock()
resp.status_code = 200
body = {'modules': []}
self.instances.api.client.delete = mock.Mock(return_value=(resp, body))
self.instances.module_remove(self.instance_with_id, "mod_id")
resp.status_code = 500
self.assertRaises(Exception, self.instances.module_remove,
'instance1', 'mod1')
def test_module_query(self):
resp = mock.Mock()
resp.status_code = 200
body = {'modules': []}
self.instances.api.client.get = mock.Mock(return_value=(resp, body))
self.instances.module_query(self.instance_with_id)
resp.status_code = 500
self.assertRaises(Exception, self.instances.module_query,
'instance1')
def test_module_retrieve(self):
resp = mock.Mock()
resp.status_code = 200
body = {'modules': []}
self.instances.api.client.get = mock.Mock(return_value=(resp, body))
self.instances.module_retrieve(self.instance_with_id)
resp.status_code = 500
self.assertRaises(Exception, self.instances.module_retrieve,
'instance1')
def test_module_list_instance(self):
resp = mock.Mock()
resp.status_code = 200
body = {'modules': []}
self.instances.api.client.get = mock.Mock(return_value=(resp, body))
self.instances.modules(self.instance_with_id)
resp.status_code = 500
self.assertRaises(Exception, self.instances.modules,
'instance1')
def test_upgrade(self):
resp = mock.Mock()
resp.status_code = 200

View File

@ -123,3 +123,25 @@ class TestModules(testtools.TestCase):
self.modules.delete(self.module)
resp.status_code = 500
self.assertRaises(Exception, self.modules.delete, self.module_name)
def _test_instances(self, expected_query=None):
page_mock = mock.Mock()
self.modules._paginated = page_mock
limit = "test-limit"
marker = "test-marker"
if not expected_query:
expected_query = {}
self.modules.instances(self.module_name, limit, marker,
**expected_query)
page_mock.assert_called_with("/modules/mod_1/instances",
"instances", limit, marker,
query_strings=expected_query)
def test_instance_count(self):
expected_query = {'include_clustered': True,
'count_only': True}
self._test_instances(expected_query)
def test_instances(self):
expected_query = {'include_clustered': True}
self._test_instances(expected_query)

View File

@ -722,6 +722,21 @@ class ShellTest(utils.TestCase):
self.assert_called_anytime(
'GET', '/modules/4321/instances?include_clustered=True')
def test_module_instance_count(self):
with mock.patch.object(troveclient.v1.modules.Module, '__repr__',
mock.Mock(return_value='4321')):
self.run_command('module-instance-count 4321')
self.assert_called(
'GET', '/modules/4321/instances?count_only=True')
def test_module_instance_count_clustered(self):
with mock.patch.object(troveclient.v1.modules.Module, '__repr__',
mock.Mock(return_value='4321')):
self.run_command('module-instance-count 4321 --include_clustered')
self.assert_called(
'GET', '/modules/4321/instances?count_only=True&'
'include_clustered=True')
def test_cluster_modules(self):
self.run_command('cluster-modules cls-1234')
self.assert_called_anytime('GET', '/clusters/cls-1234')

View File

@ -157,11 +157,13 @@ class Modules(base.ManagerWithFind):
common.check_for_exceptions(resp, body, url)
def instances(self, module, limit=None, marker=None,
include_clustered=False):
include_clustered=False, count_only=False):
"""Get a list of all instances this module has been applied to."""
url = "/modules/%s/instances" % base.getid(module)
query_strings = {}
if include_clustered:
query_strings['include_clustered'] = include_clustered
if count_only:
query_strings['count_only'] = count_only
return self._paginated(url, "instances", limit, marker,
query_strings=query_strings)

View File

@ -260,7 +260,7 @@ def do_list(cs, args):
_print_instances(instances)
def _print_instances(instances):
def _print_instances(instances, is_admin=False):
for instance in instances:
setattr(instance, 'flavor_id', instance.flavor['id'])
if hasattr(instance, 'volume'):
@ -272,9 +272,12 @@ def _print_instances(instances):
setattr(instance, 'datastore_version',
instance.datastore['version'])
setattr(instance, 'datastore', instance.datastore['type'])
utils.print_list(instances, ['id', 'name', 'datastore',
'datastore_version', 'status',
'flavor_id', 'size', 'region'])
fields = ['id', 'name', 'datastore',
'datastore_version', 'status',
'flavor_id', 'size', 'region']
if is_admin:
fields.append('tenant_id')
utils.print_list(instances, fields)
@utils.arg('--limit', metavar='<limit>', type=int, default=None,
@ -1964,7 +1967,27 @@ def do_module_instances(cs, args):
while not args.limit and items.next:
items = cs.modules.instances(module, marker=items.next)
instance_list += items
_print_instances(instance_list)
_print_instances(instance_list, utils.is_admin(cs))
@utils.arg('module', metavar='<module>', type=str,
help=_('ID or name of the module.'))
@utils.arg('--include_clustered', action="store_true", default=False,
help=_("Include instances that are part of a cluster "
"(default %(default)s)."))
@utils.service_type('database')
def do_module_instance_count(cs, args):
"""Lists a count of the instances for each module md5."""
module = _find_module(cs, args.module)
count_list = cs.modules.instances(
module, include_clustered=args.include_clustered,
count_only=True)
field_list = ['module_name', 'min_updated_date', 'max_updated_date',
'module_md5', 'current', 'instance_count']
utils.print_list(count_list, field_list,
labels={'module_md5': 'Module MD5',
'instance_count': 'Count',
'module_id': 'Module ID'})
@utils.arg('cluster', metavar='<cluster>',