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
This commit is contained in:
licanwei 2018-04-03 00:31:59 -07:00
parent 8c89b3327b
commit 69f0493968
9 changed files with 226 additions and 103 deletions

@ -17,6 +17,7 @@
import pbr.version import pbr.version
from watcherclient import client from watcherclient import client
from watcherclient.common import api_versioning
from watcherclient import exceptions from watcherclient import exceptions
@ -24,3 +25,11 @@ __version__ = pbr.version.VersionInfo(
'python-watcherclient').version_string() 'python-watcherclient').version_string()
__all__ = ['client', 'exceptions', ] __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")

@ -19,6 +19,7 @@ import re
from oslo_utils import strutils from oslo_utils import strutils
from watcherclient._i18n import _ from watcherclient._i18n import _
from watcherclient.common import httpclient
from watcherclient import exceptions from watcherclient import exceptions
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -27,13 +28,22 @@ if not LOG.handlers:
HEADER_NAME = "OpenStack-API-Version" HEADER_NAME = "OpenStack-API-Version"
SERVICE_TYPE = "infra-optim"
# key is a deprecated version and value is an alternative version. # key is a deprecated version and value is an alternative version.
DEPRECATED_VERSIONS = {} DEPRECATED_VERSIONS = {}
_type_error_msg = _("'%(other)s' should be an instance of '%(cls)s'") _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): class APIVersion(object):
"""This class represents an API Version Request. """This class represents an API Version Request.

@ -43,7 +43,9 @@ from watcherclient import exceptions
# http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa # http://specs.openstack.org/openstack/watcher-specs/specs/kilo/api-microversions.html # noqa
# for full details. # for full details.
DEFAULT_VER = 'latest' 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__) LOG = logging.getLogger(__name__)
USER_AGENT = 'python-watcherclient' USER_AGENT = 'python-watcherclient'
@ -138,9 +140,9 @@ class VersionNegotiationMixin(object):
return negotiated_ver return negotiated_ver
def _generic_parse_version_headers(self, accessor_func): 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) None)
max_ver = accessor_func('X-OpenStack-Watcher-API-Maximum-Version', max_ver = accessor_func('OpenStack-API-Maximum-Version',
None) None)
return min_ver, max_ver return min_ver, max_ver
@ -296,8 +298,9 @@ class HTTPClient(VersionNegotiationMixin):
kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {})) kwargs['headers'] = copy.deepcopy(kwargs.get('headers', {}))
kwargs['headers'].setdefault('User-Agent', USER_AGENT) kwargs['headers'].setdefault('User-Agent', USER_AGENT)
if self.os_watcher_api_version: if self.os_watcher_api_version:
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version', kwargs['headers'].setdefault(
self.os_watcher_api_version) 'OpenStack-API-Version',
' '.join(['infra-optim', self.os_watcher_api_version]))
if self.auth_token: if self.auth_token:
kwargs['headers'].setdefault('X-Auth-Token', 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: if resp.status_code == http_client.NOT_ACCEPTABLE:
negotiated_ver = self.negotiate_version(self.session, resp) negotiated_ver = self.negotiate_version(self.session, resp)
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = ( kwargs['headers']['OpenStack-API-Version'] = (
negotiated_ver) ' '.join(['infra-optim', negotiated_ver]))
return self._http_request(url, method, **kwargs) return self._http_request(url, method, **kwargs)
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
@ -505,8 +508,10 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
) )
if getattr(self, 'os_watcher_api_version', None): if getattr(self, 'os_watcher_api_version', None):
kwargs['headers'].setdefault('X-OpenStack-Watcher-API-Version', kwargs['headers'].setdefault(
self.os_watcher_api_version) 'OpenStack-API-Version',
' '.join(['infra-optim',
self.os_watcher_api_version]))
endpoint_filter = kwargs.setdefault('endpoint_filter', {}) endpoint_filter = kwargs.setdefault('endpoint_filter', {})
endpoint_filter.setdefault('interface', self.interface) endpoint_filter.setdefault('interface', self.interface)
@ -517,8 +522,8 @@ class SessionClient(VersionNegotiationMixin, adapter.LegacyJsonAdapter):
raise_exc=False, **kwargs) raise_exc=False, **kwargs)
if resp.status_code == http_client.NOT_ACCEPTABLE: if resp.status_code == http_client.NOT_ACCEPTABLE:
negotiated_ver = self.negotiate_version(self.session, resp) negotiated_ver = self.negotiate_version(self.session, resp)
kwargs['headers']['X-OpenStack-Watcher-API-Version'] = ( kwargs['headers']['OpenStack-API-Version'] = (
negotiated_ver) ' '.join(['infra-optim', negotiated_ver]))
return self._http_request(url, method, **kwargs) return self._http_request(url, method, **kwargs)
if resp.status_code >= http_client.BAD_REQUEST: if resp.status_code >= http_client.BAD_REQUEST:
error_json = _extract_error_json(resp.content) error_json = _extract_error_json(resp.content)

@ -15,6 +15,10 @@ import logging
from osc_lib import utils from osc_lib import utils
import watcherclient
from watcherclient.common import api_versioning
from watcherclient import exceptions
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
DEFAULT_API_VERSION = '1' DEFAULT_API_VERSION = '1'
@ -27,9 +31,12 @@ API_VERSIONS = {
def make_client(instance): def make_client(instance):
"""Returns an infra-optim service client.""" """Returns an infra-optim service client."""
version = api_versioning.APIVersion(instance._api_version[API_NAME])
infraoptim_client_class = utils.get_client_class( infraoptim_client_class = utils.get_client_class(
API_NAME, API_NAME,
instance._api_version[API_NAME], version.ver_major,
API_VERSIONS) API_VERSIONS)
LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class) LOG.debug('Instantiating infraoptim client: %s', infraoptim_client_class)
@ -53,3 +60,30 @@ def build_option_parser(parser):
DEFAULT_API_VERSION + DEFAULT_API_VERSION +
' (Env: OS_INFRA_OPTIM_API_VERSION)')) ' (Env: OS_INFRA_OPTIM_API_VERSION)'))
return parser 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

@ -25,7 +25,7 @@ from watcherclient.tests.unit import utils
class CommandTestCase(utils.BaseTestCase): class CommandTestCase(utils.BaseTestCase):
def setUp(self): def setUp(self, os_watcher_api_version='1.0'):
super(CommandTestCase, self).setUp() super(CommandTestCase, self).setUp()
self.fake_env = { self.fake_env = {
@ -38,7 +38,7 @@ class CommandTestCase(utils.BaseTestCase):
'os_username': 'test', 'os_username': 'test',
'os_password': 'test', 'os_password': 'test',
'timeout': 600, 'timeout': 600,
'os_watcher_api_version': '1'} 'os_watcher_api_version': os_watcher_api_version}
self.m_env = mock.Mock( self.m_env = mock.Mock(
name='m_env', name='m_env',
side_effect=lambda x, *args, **kwargs: self.fake_env.get( side_effect=lambda x, *args, **kwargs: self.fake_env.get(

@ -20,7 +20,6 @@ import six
from watcherclient import shell from watcherclient import shell
from watcherclient.tests.unit.v1 import base from watcherclient.tests.unit.v1 import base
from watcherclient import v1 as resource from watcherclient import v1 as resource
from watcherclient.v1 import resource_fields
AUDIT_TEMPLATE_1 = { AUDIT_TEMPLATE_1 = {
'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2', 'uuid': 'f8e47706-efcf-49a4-a5c4-af604eb492f2',
@ -52,76 +51,86 @@ STRATEGY_1 = {
'deleted_at': None, '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): class AuditShellTest(base.CommandTestCase):
SHORT_LIST_FIELDS = resource_fields.AUDIT_SHORT_LIST_FIELDS AUDIT_1 = {
SHORT_LIST_FIELD_LABELS = resource_fields.AUDIT_SHORT_LIST_FIELD_LABELS 'uuid': '5869da81-4876-4687-a1ed-12cd64cf53d9',
FIELDS = resource_fields.AUDIT_FIELDS 'audit_type': 'ONESHOT',
FIELD_LABELS = resource_fields.AUDIT_FIELD_LABELS '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): AUDIT_2 = {
super(self.__class__, self).setUp() '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 # goal mock
p_goal_manager = mock.patch.object(resource, 'GoalManager') p_goal_manager = mock.patch.object(resource, 'GoalManager')
@ -158,8 +167,8 @@ class AuditShellTest(base.CommandTestCase):
self.cmd = shell.WatcherShell(stdout=self.stdout) self.cmd = shell.WatcherShell(stdout=self.stdout)
def test_do_audit_list(self): def test_do_audit_list(self):
audit1 = resource.Audit(mock.Mock(), AUDIT_1) audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
audit2 = resource.Audit(mock.Mock(), AUDIT_2) audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
self.m_audit_mgr.list.return_value = [ self.m_audit_mgr.list.return_value = [
audit1, audit2] audit1, audit2]
@ -176,7 +185,7 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.list.assert_called_once_with(detail=False) self.m_audit_mgr.list.assert_called_once_with(detail=False)
def test_do_audit_list_marker(self): 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] self.m_audit_mgr.list.return_value = [audit2]
exit_code, results = self.run_cmd( exit_code, results = self.run_cmd(
@ -193,8 +202,8 @@ class AuditShellTest(base.CommandTestCase):
marker='5869da81-4876-4687-a1ed-12cd64cf53d9') marker='5869da81-4876-4687-a1ed-12cd64cf53d9')
def test_do_audit_list_detail(self): def test_do_audit_list_detail(self):
audit1 = resource.Audit(mock.Mock(), AUDIT_1) audit1 = resource.Audit(mock.Mock(), self.AUDIT_1)
audit2 = resource.Audit(mock.Mock(), AUDIT_2) audit2 = resource.Audit(mock.Mock(), self.AUDIT_2)
self.m_audit_mgr.list.return_value = [ self.m_audit_mgr.list.return_value = [
audit1, audit2] audit1, audit2]
@ -211,7 +220,7 @@ class AuditShellTest(base.CommandTestCase):
self.m_audit_mgr.list.assert_called_once_with(detail=True) self.m_audit_mgr.list.assert_called_once_with(detail=True)
def test_do_audit_show_by_uuid(self): 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 self.m_audit_mgr.get.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -225,7 +234,7 @@ class AuditShellTest(base.CommandTestCase):
'5869da81-4876-4687-a1ed-12cd64cf53d9') '5869da81-4876-4687-a1ed-12cd64cf53d9')
def test_do_audit_show_by_name(self): 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 self.m_audit_mgr.get.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -278,7 +287,7 @@ class AuditShellTest(base.CommandTestCase):
'5b157edd-5a7e-4aaa-b511-f7b33ec86e9f') '5b157edd-5a7e-4aaa-b511-f7b33ec86e9f')
def test_do_audit_update(self): 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 self.m_audit_mgr.update.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -294,7 +303,7 @@ class AuditShellTest(base.CommandTestCase):
[{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}])
def test_do_audit_update_by_name(self): 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 self.m_audit_mgr.update.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -309,7 +318,7 @@ class AuditShellTest(base.CommandTestCase):
[{'op': 'replace', 'path': '/state', 'value': 'PENDING'}]) [{'op': 'replace', 'path': '/state', 'value': 'PENDING'}])
def test_do_audit_create_with_audit_template_uuid(self): 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) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_template_mgr.get.return_value = audit_template
self.m_audit_mgr.create.return_value = audit 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): 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) audit_template = resource.AuditTemplate(mock.Mock(), AUDIT_TEMPLATE_1)
self.m_audit_template_mgr.get.return_value = audit_template self.m_audit_template_mgr.get.return_value = audit_template
self.m_audit_mgr.create.return_value = audit self.m_audit_mgr.create.return_value = audit
@ -346,7 +355,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_goal(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -363,7 +372,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_goal_and_strategy(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -382,7 +391,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_type(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -399,7 +408,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_parameter(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -418,7 +427,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_type_continuous(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -437,7 +446,7 @@ class AuditShellTest(base.CommandTestCase):
) )
def test_do_audit_create_with_name(self): 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 self.m_audit_mgr.create.return_value = audit
exit_code, result = self.run_cmd( exit_code, result = self.run_cmd(
@ -455,3 +464,13 @@ class AuditShellTest(base.CommandTestCase):
interval='3600', interval='3600',
name='my_audit' 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'])

@ -20,7 +20,7 @@ from watcherclient import exceptions as exc
CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval', CREATION_ATTRIBUTES = ['audit_template_uuid', 'audit_type', 'interval',
'parameters', 'goal', 'strategy', 'auto_trigger', 'parameters', 'goal', 'strategy', 'auto_trigger',
'name'] 'name', 'start_time', 'end_time']
class Audit(base.Resource): class Audit(base.Resource):

@ -13,16 +13,31 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import copy
from osc_lib import utils from osc_lib import utils
from oslo_utils import uuidutils from oslo_utils import uuidutils
from watcherclient._i18n import _ from watcherclient._i18n import _
from watcherclient.common import api_versioning
from watcherclient.common import command from watcherclient.common import command
from watcherclient.common import utils as common_utils from watcherclient.common import utils as common_utils
from watcherclient import exceptions from watcherclient import exceptions
from watcherclient.v1 import resource_fields as res_fields 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): class ShowAudit(command.ShowOne):
"""Show detailed information about a given audit.""" """Show detailed information about a given audit."""
@ -47,6 +62,8 @@ class ShowAudit(command.ShowOne):
columns = res_fields.AUDIT_FIELDS columns = res_fields.AUDIT_FIELDS
column_headers = res_fields.AUDIT_FIELD_LABELS 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) return column_headers, utils.get_item_properties(audit, columns)
@ -118,6 +135,10 @@ class ListAudit(command.Lister):
fields = res_fields.AUDIT_SHORT_LIST_FIELDS fields = res_fields.AUDIT_SHORT_LIST_FIELDS
field_labels = res_fields.AUDIT_SHORT_LIST_FIELD_LABELS 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( params.update(common_utils.common_params_for_list(
parsed_args, fields, field_labels)) parsed_args, fields, field_labels))
@ -187,6 +208,18 @@ class CreateAudit(command.ShowOne):
dest='name', dest='name',
metavar='<name>', metavar='<name>',
help=_('Name for this audit.')) help=_('Name for this audit.'))
parser.add_argument(
'--start-time',
dest='start_time',
metavar='<start_time>',
help=_('CONTINUOUS audit start time. '
'Format: YYYY-MM-DD hh:mm:ss'))
parser.add_argument(
'--end-time',
dest='end_time',
metavar='<end_time>',
help=_('CONTINUOUS audit end time. '
'Format: YYYY-MM-DD hh:mm:ss'))
return parser return parser
@ -194,7 +227,15 @@ class CreateAudit(command.ShowOne):
client = getattr(self.app.client_manager, "infra-optim") client = getattr(self.app.client_manager, "infra-optim")
field_list = ['audit_template_uuid', 'audit_type', 'parameters', 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() fields = dict((k, v) for (k, v) in vars(parsed_args).items()
if k in field_list and v is not None) if k in field_list and v is not None)
@ -211,6 +252,8 @@ class CreateAudit(command.ShowOne):
columns = res_fields.AUDIT_FIELDS columns = res_fields.AUDIT_FIELDS
column_headers = res_fields.AUDIT_FIELD_LABELS 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) return column_headers, utils.get_item_properties(audit, columns)
@ -252,6 +295,9 @@ class UpdateAudit(command.ShowOne):
columns = res_fields.AUDIT_FIELDS columns = res_fields.AUDIT_FIELDS
column_headers = res_fields.AUDIT_FIELD_LABELS 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) return column_headers, utils.get_item_properties(audit, columns)

@ -33,12 +33,12 @@ AUDIT_TEMPLATE_SHORT_LIST_FIELD_LABELS = ['UUID', 'Name', 'Goal', 'Strategy']
AUDIT_FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at', AUDIT_FIELDS = ['uuid', 'name', 'created_at', 'updated_at', 'deleted_at',
'state', 'audit_type', 'parameters', 'interval', 'goal_name', 'state', 'audit_type', 'parameters', 'interval', 'goal_name',
'strategy_name', 'scope', 'auto_trigger', 'next_run_time', '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', AUDIT_FIELD_LABELS = ['UUID', 'Name', 'Created At', 'Updated At', 'Deleted At',
'State', 'Audit Type', 'Parameters', 'Interval', 'Goal', 'State', 'Audit Type', 'Parameters', 'Interval', 'Goal',
'Strategy', 'Audit Scope', 'Auto Trigger', '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', AUDIT_SHORT_LIST_FIELDS = ['uuid', 'name', 'audit_type',
'state', 'goal_name', 'strategy_name', 'state', 'goal_name', 'strategy_name',