From cde518be3156cb1156b54c895ddebbdce2f37c39 Mon Sep 17 00:00:00 2001 From: licanwei Date: Mon, 6 May 2019 19:35:49 +0800 Subject: [PATCH] Add force option Partially Implements: blueprint add-force-field-to-audit Depends-on: Ib2d221ea9c994dea396c54cc8d2d32237025a1d4 Change-Id: I235bbd298b7fa8ad9d8b01a94d68376c8ac27e8d --- watcherclient/common/api_versioning.py | 10 + watcherclient/common/httpclient.py | 9 +- .../tests/unit/v1/test_audit_shell.py | 188 ++++++++++++++++++ watcherclient/v1/audit.py | 2 +- watcherclient/v1/audit_shell.py | 30 ++- watcherclient/v1/resource_fields.py | 5 +- 6 files changed, 226 insertions(+), 18 deletions(-) diff --git a/watcherclient/common/api_versioning.py b/watcherclient/common/api_versioning.py index 2bff088..9edcff9 100644 --- a/watcherclient/common/api_versioning.py +++ b/watcherclient/common/api_versioning.py @@ -27,6 +27,7 @@ if not LOG.handlers: MINOR_1_START_END_TIMING = '1.1' +MINOR_2_FORCE_AUDIT = '1.2' HEADER_NAME = "OpenStack-API-Version" # key is a deprecated version and value is an alternative version. DEPRECATED_VERSIONS = {} @@ -44,6 +45,15 @@ def allow_start_end_audit_time(requested_version): APIVersion(MINOR_1_START_END_TIMING)) +def launch_audit_forced(requested_version): + """Check if we should support force option for Audit. + + Version 1.2 of the API added support for force option. + """ + return (APIVersion(requested_version) >= + APIVersion(MINOR_2_FORCE_AUDIT)) + + class APIVersion(object): """This class represents an API Version Request. diff --git a/watcherclient/common/httpclient.py b/watcherclient/common/httpclient.py index 50f3beb..6c70be9 100644 --- a/watcherclient/common/httpclient.py +++ b/watcherclient/common/httpclient.py @@ -38,13 +38,10 @@ from watcherclient.common import api_versioning from watcherclient import exceptions -# NOTE(deva): Record the latest version that this client was tested with. -# We still have a lot of work to do in the client to implement -# microversion support in the client properly! See -# http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa -# for full details. +# Record the latest version that this client was tested with. DEFAULT_VER = '1.latest' -LAST_KNOWN_API_VERSION = 1 +# Minor version 2 for adding force option to audit +LAST_KNOWN_API_VERSION = 2 LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION) LOG = logging.getLogger(__name__) diff --git a/watcherclient/tests/unit/v1/test_audit_shell.py b/watcherclient/tests/unit/v1/test_audit_shell.py index 8cc3073..76176a7 100755 --- a/watcherclient/tests/unit/v1/test_audit_shell.py +++ b/watcherclient/tests/unit/v1/test_audit_shell.py @@ -474,3 +474,191 @@ class AuditShellTestv11(AuditShellTest): audit.update(v11) self.FIELDS.extend(['start_time', 'end_time']) self.FIELD_LABELS.extend(['Start Time', 'End Time']) + + +class AuditShellTestv12(AuditShellTest): + def setUp(self): + super(AuditShellTestv12, self).setUp(os_infra_optim_api_version='1.2') + v11 = dict(start_time=None, end_time=None) + v12 = dict(force=False) + for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3): + audit.update(v11) + audit.update(v12) + self.FIELDS.extend(['start_time', 'end_time', 'force']) + self.FIELD_LABELS.extend(['Start Time', 'End Time', 'Force']) + + def test_do_audit_create_with_force(self): + audit = resource.Audit(mock.Mock(), self.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( + 'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2 --force') + + 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', + audit_type='ONESHOT', + auto_trigger=False, + force=True + ) + + def test_do_audit_create_with_audit_template_uuid(self): + audit = resource.Audit(mock.Mock(), self.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( + 'audit create -a f8e47706-efcf-49a4-a5c4-af604eb492f2') + + 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', + audit_type='ONESHOT', + auto_trigger=False, + force=False + ) + + def test_do_audit_create_with_audit_template_name(self): + audit = resource.Audit(mock.Mock(), self.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('audit create -a at1') + + 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', + auto_trigger=False, + audit_type='ONESHOT', + force=False + ) + + def test_do_audit_create_with_goal(self): + audit = resource.Audit(mock.Mock(), self.AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + '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( + goal='fc087747-61be-4aad-8126-b701731ae836', + auto_trigger=False, + audit_type='ONESHOT', + force=False + ) + + def test_do_audit_create_with_goal_and_strategy(self): + audit = resource.Audit(mock.Mock(), self.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', + auto_trigger=False, + audit_type='ONESHOT', + force=False + ) + + def test_do_audit_create_with_type(self): + audit = resource.Audit(mock.Mock(), self.AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + '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( + goal='fc087747-61be-4aad-8126-b701731ae836', + auto_trigger=False, + audit_type='ONESHOT', + force=False + ) + + def test_do_audit_create_with_parameter(self): + audit = resource.Audit(mock.Mock(), self.AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + '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( + goal='fc087747-61be-4aad-8126-b701731ae836', + audit_type='ONESHOT', + auto_trigger=False, + parameters={'para1': 10, 'para2': 20}, + force=False + ) + + def test_do_audit_create_with_type_continuous(self): + audit = resource.Audit(mock.Mock(), self.AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + '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( + goal='fc087747-61be-4aad-8126-b701731ae836', + audit_type='CONTINUOUS', + auto_trigger=False, + interval='3600', + force=False + ) + + def test_do_audit_create_with_name(self): + audit = resource.Audit(mock.Mock(), self.AUDIT_1) + self.m_audit_mgr.create.return_value = audit + + exit_code, result = self.run_cmd( + 'audit create -g fc087747-61be-4aad-8126-b701731ae836 ' + '-t CONTINUOUS -i 3600 --name my_audit') + + 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='CONTINUOUS', + auto_trigger=False, + interval='3600', + name='my_audit', + force=False + ) diff --git a/watcherclient/v1/audit.py b/watcherclient/v1/audit.py index 6f9a3f1..803342c 100644 --- a/watcherclient/v1/audit.py +++ b/watcherclient/v1/audit.py @@ -20,7 +20,7 @@ from watcherclient import exceptions as exc CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval', 'parameters', 'goal', 'strategy', 'auto_trigger', - 'name', 'start_time', 'end_time'] + 'name', 'start_time', 'end_time', 'force'] class Audit(base.Resource): diff --git a/watcherclient/v1/audit_shell.py b/watcherclient/v1/audit_shell.py index d7d2a6b..20e7f62 100644 --- a/watcherclient/v1/audit_shell.py +++ b/watcherclient/v1/audit_shell.py @@ -26,7 +26,7 @@ from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields -def drop_start_end_field(app_args, fields, field_labels): +def drop_unsupported_field(app_args, fields, field_labels): fields = copy.copy(fields) field_labels = copy.copy(field_labels) api_ver = app_args.os_infra_optim_api_version @@ -35,6 +35,9 @@ def drop_start_end_field(app_args, fields, field_labels): ('Start Time', 'End Time')): fields.remove(field) field_labels.remove(label) + if not api_versioning.launch_audit_forced(api_ver): + fields.remove('force') + field_labels.remove('Force') return fields, field_labels @@ -62,8 +65,8 @@ class ShowAudit(command.ShowOne): columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS - columns, column_headers = drop_start_end_field(self.app_args, columns, - column_headers) + columns, column_headers = drop_unsupported_field( + self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) @@ -136,8 +139,8 @@ class ListAudit(command.Lister): field_labels = res_fields.AUDIT_SHORT_LIST_FIELD_LABELS if parsed_args.detail: - fields, field_labels = drop_start_end_field(self.app_args, fields, - field_labels) + fields, field_labels = drop_unsupported_field( + self.app_args, fields, field_labels) params.update(common_utils.common_params_for_list( parsed_args, fields, field_labels)) @@ -220,6 +223,12 @@ class CreateAudit(command.ShowOne): metavar='', help=_('CONTINUOUS audit local end time. ' 'Format: YYYY-MM-DD hh:mm:ss')) + parser.add_argument( + '--force', + dest='force', + action='store_true', + help=_('Launch audit even if action plan ' + 'is ongoing. default is False')) return parser @@ -236,6 +245,9 @@ class CreateAudit(command.ShowOne): field_list.append('start_time') if parsed_args.end_time is not None: field_list.append('end_time') + if api_versioning.launch_audit_forced(api_ver): + if parsed_args.force is not None: + field_list.append('force') fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) @@ -252,8 +264,8 @@ class CreateAudit(command.ShowOne): columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS - columns, column_headers = drop_start_end_field(self.app_args, columns, - column_headers) + columns, column_headers = drop_unsupported_field( + self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) @@ -295,8 +307,8 @@ class UpdateAudit(command.ShowOne): columns = res_fields.AUDIT_FIELDS column_headers = res_fields.AUDIT_FIELD_LABELS - columns, column_headers = drop_start_end_field(self.app_args, columns, - column_headers) + columns, column_headers = drop_unsupported_field( + self.app_args, columns, column_headers) return column_headers, utils.get_item_properties(audit, columns) diff --git a/watcherclient/v1/resource_fields.py b/watcherclient/v1/resource_fields.py index 54969f1..317413d 100755 --- a/watcherclient/v1/resource_fields.py +++ b/watcherclient/v1/resource_fields.py @@ -33,12 +33,13 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy'] AUDIT_FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at', 'state', 'audit_type', 'parameters', 'interval', 'goal_name', 'strategy_name', 'scope', 'auto_trigger', 'next_run_time', - 'hostname', 'start_time', 'end_time'] + 'hostname', 'start_time', 'end_time', 'force'] AUDIT_FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At', 'State', 'Audit Type', 'Parameters', 'Interval', 'Goal', 'Strategy', 'Audit Scope', 'Auto Trigger', - 'Next Run Time', 'Hostname', 'Start Time', 'End Time'] + 'Next Run Time', 'Hostname', 'Start Time', 'End Time', + 'Force'] AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type', 'state', 'goal_name', 'strategy_name',