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',