From 69f04939687174bbd38ca40d9ceb411aedb6abee Mon Sep 17 00:00:00 2001 From: licanwei Date: Tue, 3 Apr 2018 00:31:59 -0700 Subject: [PATCH] add start and end time for continuous audit Add new start_time and end_time fields when audit create Partially Implements: blueprint add-start-end-time-for-continuous-audit Change-Id: I37d1bd13649dfb92ce7526f04b25054ed088c4f2 --- watcherclient/__init__.py | 9 + watcherclient/common/api_versioning.py | 12 +- watcherclient/common/httpclient.py | 27 +-- watcherclient/osc/plugin.py | 36 +++- watcherclient/tests/unit/v1/base.py | 4 +- .../tests/unit/v1/test_audit_shell.py | 187 ++++++++++-------- watcherclient/v1/audit.py | 2 +- watcherclient/v1/audit_shell.py | 48 ++++- watcherclient/v1/resource_fields.py | 4 +- 9 files changed, 226 insertions(+), 103 deletions(-) diff --git a/watcherclient/__init__.py b/watcherclient/__init__.py index aa107bb..5492c60 100644 --- a/watcherclient/__init__.py +++ b/watcherclient/__init__.py @@ -17,6 +17,7 @@ import pbr.version from watcherclient import client +from watcherclient.common import api_versioning from watcherclient import exceptions @@ -24,3 +25,11 @@ __version__ = pbr.version.VersionInfo( 'python-watcherclient').version_string() __all__ = ['client', 'exceptions', ] + +API_MIN_VERSION = api_versioning.APIVersion("1.0") +# The max version should be the latest version that is supported in the client, +# not necessarily the latest that the server can provide. This is only bumped +# when client supported the max version, and bumped sequentially, otherwise +# the client may break due to server side new version may include some +# backward incompatible change. +API_MAX_VERSION = api_versioning.APIVersion("1.1") diff --git a/watcherclient/common/api_versioning.py b/watcherclient/common/api_versioning.py index 40c546c..2ac4296 100644 --- a/watcherclient/common/api_versioning.py +++ b/watcherclient/common/api_versioning.py @@ -19,6 +19,7 @@ import re from oslo_utils import strutils from watcherclient._i18n import _ +from watcherclient.common import httpclient from watcherclient import exceptions LOG = logging.getLogger(__name__) @@ -27,13 +28,22 @@ if not LOG.handlers: HEADER_NAME = "OpenStack-API-Version" -SERVICE_TYPE = "infra-optim" # key is a deprecated version and value is an alternative version. DEPRECATED_VERSIONS = {} _type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'") +def allow_start_end_audit_time(requested_version): + """Check if we should support optional start/end attributes for Audit. + + Version 1.1 of the API added support for start and end time of continuous + audits. + """ + return (APIVersion(requested_version) >= + APIVersion(httpclient.MINOR_1_START_END_TIMING)) + + class APIVersion(object): """This class represents an API Version Request. diff --git a/watcherclient/common/httpclient.py b/watcherclient/common/httpclient.py index a4e2f1b..d72ba19 100644 --- a/watcherclient/common/httpclient.py +++ b/watcherclient/common/httpclient.py @@ -43,7 +43,9 @@ from watcherclient import exceptions # http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa # for full details. DEFAULT_VER = 'latest' - +MINOR_1_START_END_TIMING = '1.1' +LAST_KNOWN_API_VERSION = 1 +LATEST_VERSION = '1.{}'.format(LAST_KNOWN_API_VERSION) LOG = logging.getLogger(__name__) USER_AGENT = 'python-watcherclient' @@ -138,9 +140,9 @@ class VersionNegotiationMixin(object): return negotiated_ver def _generic_parse_version_headers(self, accessor_func): - min_ver = accessor_func('X-OpenStack-Watcher-API-Minimum-Version', + min_ver = accessor_func('OpenStack-API-Minimum-Version', None) - max_ver = accessor_func('X-OpenStack-Watcher-API-Maximum-Version', + max_ver = accessor_func('OpenStack-API-Maximum-Version', None) return min_ver, max_ver @@ -296,8 +298,9 @@ class HTTPClient(VersionNegotiationMixin): kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'].setdefault('User-Agent', USER_AGENT) if self.os_watcher_api_version: - kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version', - self.os_watcher_api_version) + kwargs['headers'].setdefault( + 'OpenStack-API-Version', + ' '.join(['infra-optim', self.os_watcher_api_version])) if self.auth_token: kwargs['headers'].setdefault('X-Auth-Token', self.auth_token) @@ -322,8 +325,8 @@ class HTTPClient(VersionNegotiationMixin): if resp.status_code == http_client.NOT_ACCEPTABLE: negotiated_ver = self.negotiate_version(self.session, resp) - kwargs['headers']['X-OpenStack-Watcher-API-Version'] = ( - negotiated_ver) + kwargs['headers']['OpenStack-API-Version'] = ( + ' '.join(['infra-optim', negotiated_ver])) return self._http_request(url, method, **kwargs) except requests.exceptions.RequestException as e: @@ -505,8 +508,10 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter): ) if getattr(self, 'os_watcher_api_version', None): - kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version', - self.os_watcher_api_version) + kwargs['headers'].setdefault( + 'OpenStack-API-Version', + ' '.join(['infra-optim', + self.os_watcher_api_version])) endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter.setdefault('interface', self.interface) @@ -517,8 +522,8 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter): raise_exc=False, **kwargs) if resp.status_code == http_client.NOT_ACCEPTABLE: negotiated_ver = self.negotiate_version(self.session, resp) - kwargs['headers']['X-OpenStack-Watcher-API-Version'] = ( - negotiated_ver) + kwargs['headers']['OpenStack-API-Version'] = ( + ' '.join(['infra-optim', negotiated_ver])) return self._http_request(url, method, **kwargs) if resp.status_code >= http_client.BAD_REQUEST: error_json = _extract_error_json(resp.content) diff --git a/watcherclient/osc/plugin.py b/watcherclient/osc/plugin.py index 5885de3..90be8b2 100644 --- a/watcherclient/osc/plugin.py +++ b/watcherclient/osc/plugin.py @@ -15,6 +15,10 @@ import logging from osc_lib import utils +import watcherclient +from watcherclient.common import api_versioning +from watcherclient import exceptions + LOG = logging.getLogger(__name__) DEFAULT_API_VERSION = '1' @@ -27,9 +31,12 @@ API_VERSIONS = { def make_client(instance): """Returns an infra-optim service client.""" + + version = api_versioning.APIVersion(instance._api_version[API_NAME]) + infraoptim_client_class = utils.get_client_class( API_NAME, - instance._api_version[API_NAME], + version.ver_major, API_VERSIONS) LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class) @@ -53,3 +60,30 @@ def build_option_parser(parser): DEFAULT_API_VERSION + ' (Env: OS_INFRA_OPTIM_API_VERSION)')) return parser + + +def check_api_version(check_version): + """Validate version supplied by user + + Returns: + * True if version is OK + * False if the version has not been checked and the previous plugin + check should be performed + * throws an exception if the version is no good + """ + + infra_api_version = api_versioning.get_api_version(check_version) + + # Bypass X.latest format microversion + if not infra_api_version.is_latest(): + if infra_api_version > api_versioning.APIVersion("2.0"): + if not infra_api_version.matches( + watcherclient.API_MIN_VERSION, + watcherclient.API_MAX_VERSION, + ): + msg = "versions supported by client: %(min)s - %(max)s" % { + "min": watcherclient.API_MIN_VERSION.get_string(), + "max": watcherclient.API_MAX_VERSION.get_string(), + } + raise exceptions.CommandError(msg) + return True diff --git a/watcherclient/tests/unit/v1/base.py b/watcherclient/tests/unit/v1/base.py index e50eebf..ea04e5f 100644 --- a/watcherclient/tests/unit/v1/base.py +++ b/watcherclient/tests/unit/v1/base.py @@ -25,7 +25,7 @@ from watcherclient.tests.unit import utils class CommandTestCase(utils.BaseTestCase): - def setUp(self): + def setUp(self, os_watcher_api_version='1.0'): super(CommandTestCase, self).setUp() self.fake_env = { @@ -38,7 +38,7 @@ class CommandTestCase(utils.BaseTestCase): 'os_username': 'test', 'os_password': 'test', 'timeout': 600, - 'os_watcher_api_version': '1'} + 'os_watcher_api_version': os_watcher_api_version} self.m_env = mock.Mock( name='m_env', side_effect=lambda x, *args, **kwargs: self.fake_env.get( diff --git a/watcherclient/tests/unit/v1/test_audit_shell.py b/watcherclient/tests/unit/v1/test_audit_shell.py index a835d36..28cb778 100755 --- a/watcherclient/tests/unit/v1/test_audit_shell.py +++ b/watcherclient/tests/unit/v1/test_audit_shell.py @@ -20,7 +20,6 @@ import six from watcherclient import shell from watcherclient.tests.unit.v1 import base from watcherclient import v1 as resource -from watcherclient.v1 import resource_fields AUDIT_TEMPLATE_1 = { 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', @@ -52,76 +51,86 @@ STRATEGY_1 = { 'deleted_at': None, } -AUDIT_1 = { - 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9', - 'audit_type': 'ONESHOT', - 'state': 'PENDING', - 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', - 'audit_template_name': 'at1', - 'goal_name': 'SERVER_CONSOLIDATION', - 'strategy_name': 'basic', - 'created_at': datetime.datetime.now().isoformat(), - 'updated_at': None, - 'deleted_at': None, - 'parameters': None, - 'interval': None, - 'scope': '', - 'auto_trigger': False, - 'next_run_time': None, - 'name': 'my_audit1', - 'hostname': '', -} - -AUDIT_2 = { - 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', - 'audit_type': 'ONESHOT', - 'state': 'PENDING', - 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', - 'audit_template_name': 'at1', - 'goal_name': 'fc087747-61be-4aad-8126-b701731ae836', - 'strategy_name': 'auto', - 'created_at': datetime.datetime.now().isoformat(), - 'updated_at': None, - 'deleted_at': None, - 'parameters': None, - 'interval': None, - 'scope': '', - 'auto_trigger': False, - 'next_run_time': None, - 'name': 'my_audit2', - 'hostname': '', -} - -AUDIT_3 = { - 'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte', - 'audit_type': 'ONESHOT', - 'state': 'PENDING', - 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', - 'audit_template_name': 'at1', - 'goal_name': None, - 'strategy_name': 'auto', - 'created_at': datetime.datetime.now().isoformat(), - 'updated_at': None, - 'deleted_at': None, - 'parameters': None, - 'interval': 3600, - 'scope': '', - 'auto_trigger': True, - 'next_run_time': None, - 'name': 'my_audit3', - 'hostname': '', -} - class AuditShellTest(base.CommandTestCase): - SHORT_LIST_FIELDS = resource_fields.AUDIT_SHORT_LIST_FIELDS - SHORT_LIST_FIELD_LABELS = resource_fields.AUDIT_SHORT_LIST_FIELD_LABELS - FIELDS = resource_fields.AUDIT_FIELDS - FIELD_LABELS = resource_fields.AUDIT_FIELD_LABELS + AUDIT_1 = { + 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9', + 'audit_type': 'ONESHOT', + 'state': 'PENDING', + 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', + 'audit_template_name': 'at1', + 'goal_name': 'SERVER_CONSOLIDATION', + 'strategy_name': 'basic', + 'created_at': datetime.datetime.now().isoformat(), + 'updated_at': None, + 'deleted_at': None, + 'parameters': None, + 'interval': None, + 'scope': '', + 'auto_trigger': False, + 'next_run_time': None, + 'name': 'my_audit1', + 'hostname': '', + } - def setUp(self): - super(self.__class__, self).setUp() + AUDIT_2 = { + 'uuid': 'a5199d0e-0702-4613-9234-5ae2af8dafea', + 'audit_type': 'ONESHOT', + 'state': 'PENDING', + 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', + 'audit_template_name': 'at1', + 'goal_name': 'fc087747-61be-4aad-8126-b701731ae836', + 'strategy_name': 'auto', + 'created_at': datetime.datetime.now().isoformat(), + 'updated_at': None, + 'deleted_at': None, + 'parameters': None, + 'interval': None, + 'scope': '', + 'auto_trigger': False, + 'next_run_time': None, + 'name': 'my_audit2', + 'hostname': '', + } + + AUDIT_3 = { + 'uuid': '43199d0e-0712-1213-9674-5ae2af8dhgte', + 'audit_type': 'ONESHOT', + 'state': 'PENDING', + 'audit_template_uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', + 'audit_template_name': 'at1', + 'goal_name': None, + 'strategy_name': 'auto', + 'created_at': datetime.datetime.now().isoformat(), + 'updated_at': None, + 'deleted_at': None, + 'parameters': None, + 'interval': 3600, + 'scope': '', + 'auto_trigger': True, + 'next_run_time': None, + 'name': 'my_audit3', + 'hostname': '', + } + + SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type', + 'state', 'goal_name', 'strategy_name', + 'auto_trigger'] + SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Audit Type', 'State', 'Goal', + 'Strategy', 'Auto Trigger'] + 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'] + 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'] + + def setUp(self, os_watcher_api_version='1.0'): + super(AuditShellTest, self).setUp( + os_watcher_api_version=os_watcher_api_version) # goal mock p_goal_manager = mock.patch.object(resource, 'GoalManager') @@ -158,8 +167,8 @@ class AuditShellTest(base.CommandTestCase): self.cmd = shell.WatcherShell(stdout=self.stdout) def test_do_audit_list(self): - audit1 = resource.Audit(mock.Mock(), AUDIT_1) - audit2 = resource.Audit(mock.Mock(), AUDIT_2) + audit1 = resource.Audit(mock.Mock(), self.AUDIT_1) + audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [ audit1, audit2] @@ -176,7 +185,7 @@ class AuditShellTest(base.CommandTestCase): self.m_audit_mgr.list.assert_called_once_with(detail=False) def test_do_audit_list_marker(self): - audit2 = resource.Audit(mock.Mock(), AUDIT_2) + audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [audit2] exit_code, results = self.run_cmd( @@ -193,8 +202,8 @@ class AuditShellTest(base.CommandTestCase): marker='5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_audit_list_detail(self): - audit1 = resource.Audit(mock.Mock(), AUDIT_1) - audit2 = resource.Audit(mock.Mock(), AUDIT_2) + audit1 = resource.Audit(mock.Mock(), self.AUDIT_1) + audit2 = resource.Audit(mock.Mock(), self.AUDIT_2) self.m_audit_mgr.list.return_value = [ audit1, audit2] @@ -211,7 +220,7 @@ class AuditShellTest(base.CommandTestCase): self.m_audit_mgr.list.assert_called_once_with(detail=True) def test_do_audit_show_by_uuid(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.get.return_value = audit exit_code, result = self.run_cmd( @@ -225,7 +234,7 @@ class AuditShellTest(base.CommandTestCase): '5869da81-4876-4687-a1ed-12cd64cf53d9') def test_do_audit_show_by_name(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.get.return_value = audit exit_code, result = self.run_cmd( @@ -278,7 +287,7 @@ class AuditShellTest(base.CommandTestCase): '5b157edd-5a7e-4aaa-b511-f7b33ec86e9f') def test_do_audit_update(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.update.return_value = audit exit_code, result = self.run_cmd( @@ -294,7 +303,7 @@ class AuditShellTest(base.CommandTestCase): [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) def test_do_audit_update_by_name(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.update.return_value = audit exit_code, result = self.run_cmd( @@ -309,7 +318,7 @@ class AuditShellTest(base.CommandTestCase): [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) def test_do_audit_create_with_audit_template_uuid(self): - audit = resource.Audit(mock.Mock(), AUDIT_3) + 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 @@ -328,7 +337,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_audit_template_name(self): - audit = resource.Audit(mock.Mock(), AUDIT_3) + 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 @@ -346,7 +355,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_goal(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -363,7 +372,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_goal_and_strategy(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -382,7 +391,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_type(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -399,7 +408,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_parameter(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -418,7 +427,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_type_continuous(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -437,7 +446,7 @@ class AuditShellTest(base.CommandTestCase): ) def test_do_audit_create_with_name(self): - audit = resource.Audit(mock.Mock(), AUDIT_1) + audit = resource.Audit(mock.Mock(), self.AUDIT_1) self.m_audit_mgr.create.return_value = audit exit_code, result = self.run_cmd( @@ -455,3 +464,13 @@ class AuditShellTest(base.CommandTestCase): interval='3600', name='my_audit' ) + + +class AuditShellTestv11(AuditShellTest): + def setUp(self): + super(AuditShellTestv11, self).setUp(os_watcher_api_version='1.1') + v11 = dict(start_time=None, end_time=None) + for audit in (self.AUDIT_1, self.AUDIT_2, self.AUDIT_3): + audit.update(v11) + self.FIELDS.extend(['start_time', 'end_time']) + self.FIELD_LABELS.extend(['Start Time', 'End Time']) diff --git a/watcherclient/v1/audit.py b/watcherclient/v1/audit.py index d2b9098..6f9a3f1 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'] + 'name', 'start_time', 'end_time'] class Audit(base.Resource): diff --git a/watcherclient/v1/audit_shell.py b/watcherclient/v1/audit_shell.py index 4da64a9..4a54f6d 100644 --- a/watcherclient/v1/audit_shell.py +++ b/watcherclient/v1/audit_shell.py @@ -13,16 +13,31 @@ # See the License for the specific language governing permissions and # limitations under the License. +import copy + from osc_lib import utils from oslo_utils import uuidutils from watcherclient._i18n import _ +from watcherclient.common import api_versioning from watcherclient.common import command from watcherclient.common import utils as common_utils from watcherclient import exceptions from watcherclient.v1 import resource_fields as res_fields +def drop_start_end_field(app_args, fields, field_labels): + fields = copy.copy(fields) + field_labels = copy.copy(field_labels) + api_ver = app_args.os_watcher_api_version + if not api_versioning.allow_start_end_audit_time(api_ver): + for field, label in zip(('start_time', 'end_time'), + ('Start Time', 'End Time')): + fields.remove(field) + field_labels.remove(label) + return fields, field_labels + + class ShowAudit(command.ShowOne): """Show detailed information about a given audit.""" @@ -47,6 +62,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) return column_headers, utils.get_item_properties(audit, columns) @@ -118,6 +135,10 @@ class ListAudit(command.Lister): fields = res_fields.AUDIT_SHORT_LIST_FIELDS 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) + params.update(common_utils.common_params_for_list( parsed_args, fields, field_labels)) @@ -187,6 +208,18 @@ class CreateAudit(command.ShowOne): dest='name', metavar='', help=_('Name for this audit.')) + parser.add_argument( + '--start-time', + dest='start_time', + metavar='', + help=_('CONTINUOUS audit start time. ' + 'Format: YYYY-MM-DD hh:mm:ss')) + parser.add_argument( + '--end-time', + dest='end_time', + metavar='', + help=_('CONTINUOUS audit end time. ' + 'Format: YYYY-MM-DD hh:mm:ss')) return parser @@ -194,7 +227,15 @@ class CreateAudit(command.ShowOne): client = getattr(self.app.client_manager, "infra-optim") field_list = ['audit_template_uuid', 'audit_type', 'parameters', - 'interval', 'goal', 'strategy', 'auto_trigger', 'name'] + 'interval', 'goal', 'strategy', 'auto_trigger', + 'name'] + + api_ver = self.app_args.os_watcher_api_version + if api_versioning.allow_start_end_audit_time(api_ver): + if parsed_args.start_time is not None: + field_list.append('start_time') + if parsed_args.end_time is not None: + field_list.append('end_time') fields = dict((k, v) for (k, v) in vars(parsed_args).items() if k in field_list and v is not None) @@ -211,6 +252,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) return column_headers, utils.get_item_properties(audit, columns) @@ -252,6 +295,9 @@ 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) + return column_headers, utils.get_item_properties(audit, columns) diff --git a/watcherclient/v1/resource_fields.py b/watcherclient/v1/resource_fields.py index 5682bf5..54969f1 100755 --- a/watcherclient/v1/resource_fields.py +++ b/watcherclient/v1/resource_fields.py @@ -33,12 +33,12 @@ 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'] + 'hostname', 'start_time', 'end_time'] 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'] + 'Next Run Time', 'Hostname', 'Start Time', 'End Time'] AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type', 'state', 'goal_name', 'strategy_name',