From 6fbcfc794ac0fbea852fe15ffb4716bfd78fa6b5 Mon Sep 17 00:00:00 2001 From: Prashanth Hari Date: Wed, 22 Jun 2016 15:15:19 -0400 Subject: [PATCH] Add goal_id, strategy_id and host_aggregate CLI options to audit Add goal_id, strategy_id and host_aggregate CLI options to audit so that an audit doesn't lose important information when an audit template is deleted. Also, user can input a specific strategy to accomplish a goal for an audit. Partially-Implements: blueprint persistent-audit-parameters Depends-On: I7b3eae4d0752a11208f5f92ee13ab1018d8521ad Change-Id: Ied1da980f358343316ee726b0781188107bfba8d --- watcherclient/tests/v1/test_audit.py | 31 ++--- watcherclient/tests/v1/test_audit_shell.py | 131 ++++++++++++++++----- watcherclient/v1/audit.py | 5 +- watcherclient/v1/audit_shell.py | 70 ++++++++--- watcherclient/v1/resource_fields.py | 18 +-- 5 files changed, 176 insertions(+), 79 deletions(-) diff --git a/watcherclient/tests/v1/test_audit.py b/watcherclient/tests/v1/test_audit.py index 2701815..aad3d2f 100644 --- a/watcherclient/tests/v1/test_audit.py +++ b/watcherclient/tests/v1/test_audit.py @@ -23,12 +23,14 @@ from testtools.matchers import HasLength from watcherclient.tests import utils import watcherclient.v1.audit + AUDIT1 = { 'id': 1, - 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', + 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9', 'deadline': None, 'audit_type': 'ONE_SHOT', - 'audit_template_uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588' + 'goal': 'fc087747-61be-4aad-8126-b701731ae836', + 'strategy': '2cf86250-d309-4b81-818e-1537f3dba6e5', } AUDIT2 = { @@ -36,9 +38,11 @@ AUDIT2 = { 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'deadline': None, 'audit_type': 'ONE_SHOT', - 'audit_template_uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588' + 'goal': 'fc087747-61be-4aad-8126-b701731ae836', + 'strategy': None, } + CREATE_AUDIT = copy.deepcopy(AUDIT1) del CREATE_AUDIT['id'] del CREATE_AUDIT['uuid'] @@ -125,19 +129,8 @@ fake_responses_sorting = { }, } -fake_responses_filters = { - '/v1/audits/?audit_template=%s' % AUDIT2['audit_template_uuid']: - { - 'GET': ( - {}, - {"audits": [AUDIT2]} - ), - }, -} - class AuditManagerTest(testtools.TestCase): - def setUp(self): super(AuditManagerTest, self).setUp() self.api = utils.FakeAPI(fake_responses) @@ -200,16 +193,6 @@ class AuditManagerTest(testtools.TestCase): self.assertEqual(expect, self.api.calls) self.assertEqual(2, len(audits)) - def test_audits_list_filter_by_audit_template(self): - self.api = utils.FakeAPI(fake_responses_filters) - self.mgr = watcherclient.v1.audit.AuditManager(self.api) - self.mgr.list(audit_template=AUDIT2['audit_template_uuid']) - expect = [ - ('GET', '/v1/audits/?audit_template=%s' % - AUDIT2['audit_template_uuid'], {}, None), - ] - self.assertEqual(expect, self.api.calls) - def test_audits_show(self): audit = self.mgr.get(AUDIT1['uuid']) expect = [ diff --git a/watcherclient/tests/v1/test_audit_shell.py b/watcherclient/tests/v1/test_audit_shell.py index 4692da3..83965b4 100644 --- a/watcherclient/tests/v1/test_audit_shell.py +++ b/watcherclient/tests/v1/test_audit_shell.py @@ -31,8 +31,27 @@ AUDIT_TEMPLATE_1 = { 'description': 'Audit Template 1 description', 'host_aggregate': 5, 'extra': {'automatic': False}, - 'goal_uuid': '7568667b-51fe-4087-9eb1-29b26891036f', - 'strategy_uuid': 'bbe6b966-f98e-439b-a01a-17b9b3b8478b', + 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', + 'strategy_uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', + 'created_at': datetime.datetime.now().isoformat(), + 'updated_at': None, + 'deleted_at': None, +} + +GOAL_1 = { + 'uuid': "fc087747-61be-4aad-8126-b701731ae836", + 'name': "SERVER_CONSOLIDATION", + 'display_name': 'Server Consolidation', + 'created_at': datetime.datetime.now().isoformat(), + 'updated_at': None, + 'deleted_at': None, +} + +STRATEGY_1 = { + 'uuid': '2cf86250-d309-4b81-818e-1537f3dba6e5', + 'name': 'basic', + 'display_name': 'Basic consolidation', + 'goal_uuid': 'fc087747-61be-4aad-8126-b701731ae836', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, @@ -45,6 +64,9 @@ AUDIT_1 = { 'state': 'PENDING', 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit_template_name': 'at1', + 'host_aggregate': 5, + 'goal_name': 'SERVER_CONSOLIDATION', + 'strategy_name': 'basic', 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, @@ -56,9 +78,12 @@ AUDIT_2 = { 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', 'deadline': None, 'audit_type': 'ONESHOT', - 'audit_template_uuid': '770ef053-ecb3-48b0-85b5-d55a2dbc6588', - 'audit_template_name': 'at2', 'state': 'PENDING', + 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', + 'audit_template_name': 'at1', + 'host_aggregate': None, + 'goal_name': 'fc087747-61be-4aad-8126-b701731ae836', + 'strategy_name': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, @@ -69,10 +94,13 @@ AUDIT_2 = { AUDIT_3 = { 'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte', 'deadline': None, - 'audit_type': 'CONTINUOUS', + 'audit_type': 'ONESHOT', + 'state': 'PENDING', 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'audit_template_name': 'at1', - 'state': 'PENDING', + 'host_aggregate': 3, + 'goal_name': None, + 'strategy_name': None, 'created_at': datetime.datetime.now().isoformat(), 'updated_at': None, 'deleted_at': None, @@ -91,6 +119,22 @@ class AuditShellTest(base.CommandTestCase): def setUp(self): super(self.__class__, self).setUp() + # goal mock + p_goal_manager = mock.patch.object(resource, 'GoalManager') + self.m_goal_mgr_cls = p_goal_manager.start() + self.addCleanup(p_goal_manager.stop) + + self.m_goal_mgr = mock.Mock() + self.m_goal_mgr_cls.return_value = self.m_goal_mgr + + # strategy mock + p_strategy_manager = mock.patch.object(resource, 'StrategyManager') + self.m_strategy_mgr_cls = p_strategy_manager.start() + self.addCleanup(p_strategy_manager.stop) + + self.m_strategy_mgr = mock.Mock() + self.m_strategy_mgr_cls.return_value = self.m_strategy_mgr + p_audit_manager = mock.patch.object(resource, 'AuditManager') p_audit_template_manager = mock.patch.object( resource, 'AuditTemplateManager') @@ -105,6 +149,7 @@ class AuditShellTest(base.CommandTestCase): self.m_audit_mgr_cls.return_value = self.m_audit_mgr self.m_audit_template_mgr_cls.return_value = self.m_audit_template_mgr + # stdout mock self.stdout = six.StringIO() self.cmd = shell.WatcherShell(stdout=self.stdout) @@ -147,7 +192,6 @@ class AuditShellTest(base.CommandTestCase): def test_do_audit_show_by_uuid(self): audit = resource.Audit(mock.Mock(), AUDIT_1) self.m_audit_mgr.get.return_value = audit - self.m_audit_template_mgr.get.return_value = audit exit_code, result = self.run_cmd( 'audit show 5869da81-4876-4687-a1ed-12cd64cf53d9') @@ -231,7 +275,9 @@ class AuditShellTest(base.CommandTestCase): self.assertEqual('', result) def test_do_audit_create_with_audit_template_uuid(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), AUDIT_3) + audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) + self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -246,7 +292,7 @@ class AuditShellTest(base.CommandTestCase): audit_type='ONESHOT') def test_do_audit_create_with_audit_template_name(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), AUDIT_3) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit @@ -261,73 +307,102 @@ class AuditShellTest(base.CommandTestCase): audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', audit_type='ONESHOT') - def test_do_audit_create_with_deadline(self): + def test_do_audit_create_with_goal(self): audit = resource.Audit(mock.Mock(), AUDIT_1) - audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) - self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( - 'audit create -a at1 -d 2016-04-28T10:48:32.064802') + 'audit create -g fc087747-61be-4aad-8126-b701731ae836') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( - audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', + goal='fc087747-61be-4aad-8126-b701731ae836', + audit_type='ONESHOT' + ) + + def test_do_audit_create_with_goal_and_strategy(self): + audit = resource.Audit(mock.Mock(), AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -s ' + '2cf86250-d309-4b81-818e-1537f3dba6e5') + + self.assertEqual(0, exit_code) + self.assertEqual( + self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), + result) + self.m_audit_mgr.create.assert_called_once_with( + goal='fc087747-61be-4aad-8126-b701731ae836', + strategy='2cf86250-d309-4b81-818e-1537f3dba6e5', + audit_type='ONESHOT' + ) + + def test_do_audit_create_with_deadline(self): + audit = resource.Audit(mock.Mock(), AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -d ' + '2016-04-28T10:48:32.064802') + + self.assertEqual(0, exit_code) + self.assertEqual( + self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), + result) + self.m_audit_mgr.create.assert_called_once_with( + goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='ONESHOT', deadline='2016-04-28T10:48:32.064802') def test_do_audit_create_with_type(self): audit = resource.Audit(mock.Mock(), AUDIT_1) - audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) - self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( - 'audit create -a at1 -t ONESHOT') + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -t ONESHOT') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( - audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', + goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='ONESHOT') def test_do_audit_create_with_parameter(self): audit = resource.Audit(mock.Mock(), AUDIT_1) - audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) - self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( - 'audit create -a at1 -p para1=10 -p para2=20') + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 -p para1=10 ' + '-p para2=20') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( - audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', + goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='ONESHOT', - parameters={'para1': 10, 'para2': 20}) + parameters=['para1=10', 'para2=20']) def test_do_audit_create_with_type_continuous(self): - audit = resource.Audit(mock.Mock(), AUDIT_3) - audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1) - self.m_audit_template_mgr.get.return_value = audit_template + audit = resource.Audit(mock.Mock(), AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( - 'audit create -a at1 -t CONTINUOUS -i 3600') + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' + '-t CONTINUOUS -i 3600') self.assertEqual(0, exit_code) self.assertEqual( self.resource_as_dict(audit, self.FIELDS, self.FIELD_LABELS), result) self.m_audit_mgr.create.assert_called_once_with( - audit_template_uuid='f8e47706-efcf-49a4-a5c4-af604eb492f2', + goal='fc087747-61be-4aad-8126-b701731ae836', audit_type='CONTINUOUS', interval='3600') diff --git a/watcherclient/v1/audit.py b/watcherclient/v1/audit.py index 2d467ae..502f0e7 100644 --- a/watcherclient/v1/audit.py +++ b/watcherclient/v1/audit.py @@ -19,8 +19,9 @@ from watcherclient.common import utils from watcherclient import exceptions as exc -CREATION_ATTRIBUTES = ['audit_template_uuid', 'deadline', - 'audit_type', 'parameters', 'interval'] +CREATION_ATTRIBUTES = ['audit_template_uuid', 'host_aggregate', + 'deadline', 'audit_type', 'interval', + 'goal', 'strategy'] class Audit(base.Resource): diff --git a/watcherclient/v1/audit_shell.py b/watcherclient/v1/audit_shell.py index 546430b..a8887bf 100644 --- a/watcherclient/v1/audit_shell.py +++ b/watcherclient/v1/audit_shell.py @@ -55,17 +55,22 @@ class ListAudit(command.Lister): def get_parser(self, prog_name): parser = super(ListAudit, self).get_parser(prog_name) - parser.add_argument( - '--audit-template', - metavar='', - dest='audit_template', - help=_('Name or UUID of an audit template used for filtering.')) parser.add_argument( '--detail', dest='detail', action='store_true', default=False, help=_("Show detailed information about audits.")) + parser.add_argument( + '--goal', + dest='goal', + metavar='', + help=_('UUID or name of the goal used for filtering.')) + parser.add_argument( + '--strategy', + dest='strategy', + metavar='', + help=_('UUID or name of the strategy used for filtering.')) parser.add_argument( '--limit', metavar='', @@ -89,8 +94,15 @@ class ListAudit(command.Lister): client = getattr(self.app.client_manager, "infra-optim") params = {} - if parsed_args.audit_template is not None: - params['audit_template'] = parsed_args.audit_template + + # Optional + if parsed_args.goal: + params['goal'] = parsed_args.goal + + # Optional + if parsed_args.strategy: + params['strategy'] = parsed_args.strategy + if parsed_args.detail: fields = res_fields.AUDIT_FIELDS field_labels = res_fields.AUDIT_FIELD_LABELS @@ -115,12 +127,6 @@ class CreateAudit(command.ShowOne): def get_parser(self, prog_name): parser = super(CreateAudit, self).get_parser(prog_name) - parser.add_argument( - '-a', '--audit-template', - required=True, - dest='audit_template_uuid', - metavar='', - help=_('Audit template used for this audit (name or uuid).')) parser.add_argument( '-d', '--deadline', dest='deadline', @@ -144,22 +150,52 @@ class CreateAudit(command.ShowOne): dest='interval', metavar='', help=_("Audit interval.")) - + parser.add_argument( + '-g', '--goal', + dest='goal', + metavar='', + help=_('Goal UUID or name associated to this audit.')) + parser.add_argument( + '-s', '--strategy', + dest='strategy', + metavar='', + help=_('Strategy UUID or name associated to this audit.')) + parser.add_argument( + '-r', '--host-aggregate', + dest='host_aggregate', + metavar='', + help=_('Name or UUID of the host aggregate targeted ' + 'by this audit.')) + parser.add_argument( + '-a', '--audit-template', + dest='audit_template_uuid', + metavar='', + help=_('Audit template used for this audit (name or uuid).')) return parser def take_action(self, parsed_args): client = getattr(self.app.client_manager, "infra-optim") - field_list = ['audit_template_uuid', 'audit_type', - 'deadline', 'parameters', 'interval'] + field_list = ['audit_template_uuid', 'host_aggregate', + 'audit_type', 'deadline', 'parameters', 'interval', + 'goal', 'strategy'] fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) - fields = common_utils.args_array_to_dict(fields, 'parameters') + + if fields.get('goal'): + if not uuidutils.is_uuid_like(fields['goal']): + fields['goal'] = client.goal.get(fields['goal']).uuid + if fields.get('audit_template_uuid'): if not uuidutils.is_uuid_like(fields['audit_template_uuid']): fields['audit_template_uuid'] = client.audit_template.get( fields['audit_template_uuid']).uuid + # optional + if fields.get('strategy'): + if not uuidutils.is_uuid_like(fields['strategy']): + fields['strategy'] = client.strategy.get( + fields['strategy']).uuid audit = client.audit.create(**fields) diff --git a/watcherclient/v1/resource_fields.py b/watcherclient/v1/resource_fields.py index 30236e7..d89c4f7 100644 --- a/watcherclient/v1/resource_fields.py +++ b/watcherclient/v1/resource_fields.py @@ -34,18 +34,20 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy'] # Audit AUDIT_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at', - 'deadline', 'state', 'audit_type', 'audit_template_uuid', - 'audit_template_name', 'parameters', 'interval'] + 'deadline', 'state', 'audit_type', + 'parameters', 'interval', + 'host_aggregate', 'goal_name', 'strategy_name'] AUDIT_FIELD_LABELS = ['UUID', 'Created At', 'Updated At', 'Deleted At', - 'Deadline', 'State', 'Audit Type', 'Audit Template uuid', - 'Audit Template Name', 'Parameters', 'Interval'] + 'Deadline', 'State', 'Audit Type', + 'Parameters', 'Interval', 'Host Aggregate ID or Name', + 'Goal', 'Strategy'] -AUDIT_SHORT_LIST_FIELDS = [ - 'uuid', 'audit_type', 'audit_template_name', 'state'] +AUDIT_SHORT_LIST_FIELDS = ['uuid', 'audit_type', + 'state', 'goal_name', 'strategy_name'] -AUDIT_SHORT_LIST_FIELD_LABELS = [ - 'UUID', 'Audit Type', 'Audit Template', 'State'] +AUDIT_SHORT_LIST_FIELD_LABELS = ['UUID', 'Audit Type', 'State', 'Goal', + 'Strategy'] # Action Plan ACTION_PLAN_FIELDS = ['uuid', 'created_at', 'updated_at', 'deleted_at',