Merge "Fix audit creation with named goal and strategy"

This commit is contained in:
Zuul 2018-10-05 09:34:05 +00:00 committed by Gerrit Code Review
commit 05055b7064
3 changed files with 127 additions and 92 deletions

View File

@ -28,10 +28,6 @@ itself. In the first case, there also should be supplied
``audit_template_uuid``. If ``Audit`` is created without ``Audit Template``, ``audit_template_uuid``. If ``Audit`` is created without ``Audit Template``,
``goal`` should be provided. ``goal`` should be provided.
.. warning::
**Only ``audit_template_uuid`` can be used to create audit so far.**
It should be fixed during the ``Rocky`` cycle.
Normal response codes: 201 Normal response codes: 201
Error codes: 400,404 Error codes: 400,404

View File

@ -54,6 +54,13 @@ from watcher import objects
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
def _get_object_by_value(context, class_name, value):
if utils.is_uuid_like(value) or utils.is_int_like(value):
return class_name.get(context, value)
else:
return class_name.get_by_name(context, value)
class AuditPostType(wtypes.Base): class AuditPostType(wtypes.Base):
name = wtypes.wsattr(wtypes.text, mandatory=False) name = wtypes.wsattr(wtypes.text, mandatory=False)
@ -93,6 +100,10 @@ class AuditPostType(wtypes.Base):
raise exception.AuditIntervalNotSpecified( raise exception.AuditIntervalNotSpecified(
audit_type=self.audit_type) audit_type=self.audit_type)
if self.audit_template_uuid and self.goal:
raise exception.Invalid('Either audit_template_uuid '
'or goal should be provided.')
# If audit_template_uuid was provided, we will provide any # If audit_template_uuid was provided, we will provide any
# variables not included in the request, but not override # variables not included in the request, but not override
# those variables that were included. # those variables that were included.
@ -123,7 +134,8 @@ class AuditPostType(wtypes.Base):
# Note: If audit name was not provided, used a default name # Note: If audit name was not provided, used a default name
if not self.name: if not self.name:
if self.strategy: if self.strategy:
strategy = objects.Strategy.get(context, self.strategy) strategy = _get_object_by_value(context, objects.Strategy,
self.strategy)
self.name = "%s-%s" % (strategy.name, self.name = "%s-%s" % (strategy.name,
datetime.datetime.utcnow().isoformat()) datetime.datetime.utcnow().isoformat())
elif self.audit_template_uuid: elif self.audit_template_uuid:
@ -132,7 +144,7 @@ class AuditPostType(wtypes.Base):
self.name = "%s-%s" % (audit_template.name, self.name = "%s-%s" % (audit_template.name,
datetime.datetime.utcnow().isoformat()) datetime.datetime.utcnow().isoformat())
else: else:
goal = objects.Goal.get(context, self.goal) goal = _get_object_by_value(context, objects.Goal, self.goal)
self.name = "%s-%s" % (goal.name, self.name = "%s-%s" % (goal.name,
datetime.datetime.utcnow().isoformat()) datetime.datetime.utcnow().isoformat())
# No more than 63 characters # No more than 63 characters

View File

@ -37,13 +37,16 @@ def post_get_test_audit(**kw):
audit_template = db_utils.get_test_audit_template() audit_template = db_utils.get_test_audit_template()
goal = db_utils.get_test_goal() goal = db_utils.get_test_goal()
del_keys = ['goal_id', 'strategy_id'] del_keys = ['goal_id', 'strategy_id']
del_keys.extend(kw.get('params_to_exclude', []))
add_keys = {'audit_template_uuid': audit_template['uuid'], add_keys = {'audit_template_uuid': audit_template['uuid'],
'goal': goal['uuid'], 'goal': goal['uuid'],
} }
for k in del_keys: if kw.get('use_named_goal'):
del audit[k] add_keys['goal'] = 'TEST'
for k in add_keys: for k in add_keys:
audit[k] = kw.get(k, add_keys[k]) audit[k] = kw.get(k, add_keys[k])
for k in del_keys:
del audit[k]
return audit return audit
@ -491,13 +494,10 @@ class TestPost(api_base.FunctionalTest):
test_time = datetime.datetime(2000, 1, 1, 0, 0) test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time mock_utcnow.return_value = test_time
audit_dict = post_get_test_audit(state=objects.audit.State.PENDING) audit_dict = post_get_test_audit(
del audit_dict['uuid'] state=objects.audit.State.PENDING,
del audit_dict['state'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['interval'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict) response = self.post_json('/audits', audit_dict)
self.assertEqual('application/json', response.content_type) self.assertEqual('application/json', response.content_type)
@ -530,18 +530,78 @@ class TestPost(api_base.FunctionalTest):
self.assertEqual('application/json', response.content_type) self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message']) self.assertTrue(response.json['error_message'])
@mock.patch('oslo_utils.timeutils.utcnow')
def test_create_audit_with_at_uuid_and_goal_specified(self, mock_utcnow):
test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time
audit_dict = post_get_test_audit(
state=objects.audit.State.PENDING,
params_to_exclude=['uuid', 'state', 'interval', 'scope',
'next_run_time', 'hostname'])
response = self.post_json('/audits', audit_dict, expect_errors=True)
self.assertEqual(400, response.status_int)
self.assertEqual('application/json', response.content_type)
self.assertTrue(response.json['error_message'])
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
def test_create_audit_with_goal(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit(
params_to_exclude=['uuid', 'state', 'interval', 'scope',
'next_run_time', 'hostname',
'audit_template_uuid'])
response = self.post_json('/audits', audit_dict)
self.assertEqual('application/json', response.content_type)
self.assertEqual(201, response.status_int)
self.assertEqual(objects.audit.State.PENDING,
response.json['state'])
self.assertTrue(utils.is_uuid_like(response.json['uuid']))
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
def test_create_audit_with_goal_without_strategy(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit(
params_to_exclude=['uuid', 'state', 'interval', 'scope',
'next_run_time', 'hostname',
'audit_template_uuid', 'strategy'])
response = self.post_json('/audits', audit_dict)
self.assertEqual('application/json', response.content_type)
self.assertEqual(201, response.status_int)
self.assertEqual(objects.audit.State.PENDING,
response.json['state'])
self.assertTrue(utils.is_uuid_like(response.json['uuid']))
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
def test_create_audit_with_named_goal(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit(
params_to_exclude=['uuid', 'state', 'interval', 'scope',
'next_run_time', 'hostname',
'audit_template_uuid'],
use_named_goal=True)
response = self.post_json('/audits', audit_dict)
self.assertEqual('application/json', response.content_type)
self.assertEqual(201, response.status_int)
self.assertEqual(objects.audit.State.PENDING,
response.json['state'])
self.assertTrue(utils.is_uuid_like(response.json['uuid']))
@mock.patch('oslo_utils.timeutils.utcnow') @mock.patch('oslo_utils.timeutils.utcnow')
def test_create_audit_invalid_audit_template_uuid(self, mock_utcnow): def test_create_audit_invalid_audit_template_uuid(self, mock_utcnow):
test_time = datetime.datetime(2000, 1, 1, 0, 0) test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time mock_utcnow.return_value = test_time
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['interval']
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
# Make the audit template UUID some garbage value # Make the audit template UUID some garbage value
audit_dict['audit_template_uuid'] = ( audit_dict['audit_template_uuid'] = (
'01234567-8910-1112-1314-151617181920') '01234567-8910-1112-1314-151617181920')
@ -558,14 +618,12 @@ class TestPost(api_base.FunctionalTest):
def test_create_audit_doesnt_contain_id(self, mock_trigger_audit): def test_create_audit_doesnt_contain_id(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit(state=objects.audit.State.PENDING) audit_dict = post_get_test_audit(
state=objects.audit.State.PENDING,
params_to_exclude=['uuid', 'interval', 'scope',
'next_run_time', 'hostname', 'goal'])
state = audit_dict['state'] state = audit_dict['state']
del audit_dict['uuid']
del audit_dict['state'] del audit_dict['state']
del audit_dict['interval']
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
with mock.patch.object(self.dbapi, 'create_audit', with mock.patch.object(self.dbapi, 'create_audit',
wraps=self.dbapi.create_audit) as cn_mock: wraps=self.dbapi.create_audit) as cn_mock:
response = self.post_json('/audits', audit_dict) response = self.post_json('/audits', audit_dict)
@ -578,13 +636,9 @@ class TestPost(api_base.FunctionalTest):
def test_create_audit_generate_uuid(self, mock_trigger_audit): def test_create_audit_generate_uuid(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['interval']
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict) response = self.post_json('/audits', audit_dict)
self.assertEqual('application/json', response.content_type) self.assertEqual('application/json', response.content_type)
@ -597,12 +651,9 @@ class TestPost(api_base.FunctionalTest):
def test_create_continuous_audit_with_interval(self, mock_trigger_audit): def test_create_continuous_audit_with_interval(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value
audit_dict['interval'] = '1200' audit_dict['interval'] = '1200'
@ -619,12 +670,9 @@ class TestPost(api_base.FunctionalTest):
mock_trigger_audit): mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value
audit_dict['interval'] = '* * * * *' audit_dict['interval'] = '* * * * *'
@ -641,12 +689,9 @@ class TestPost(api_base.FunctionalTest):
mock_trigger_audit): mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value
audit_dict['interval'] = 'zxc' audit_dict['interval'] = 'zxc'
@ -662,14 +707,10 @@ class TestPost(api_base.FunctionalTest):
def test_create_continuous_audit_without_period(self, mock_trigger_audit): def test_create_continuous_audit_without_period(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value audit_dict['audit_type'] = objects.audit.AuditType.CONTINUOUS.value
del audit_dict['interval']
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict, expect_errors=True) response = self.post_json('/audits', audit_dict, expect_errors=True)
self.assertEqual(400, response.status_int) self.assertEqual(400, response.status_int)
@ -683,13 +724,10 @@ class TestPost(api_base.FunctionalTest):
def test_create_oneshot_audit_with_period(self, mock_trigger_audit): def test_create_oneshot_audit_with_period(self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
del audit_dict['uuid'] params_to_exclude=['uuid', 'state', 'scope',
del audit_dict['state'] 'next_run_time', 'hostname', 'goal'])
audit_dict['audit_type'] = objects.audit.AuditType.ONESHOT.value audit_dict['audit_type'] = objects.audit.AuditType.ONESHOT.value
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict, expect_errors=True) response = self.post_json('/audits', audit_dict, expect_errors=True)
self.assertEqual(400, response.status_int) self.assertEqual(400, response.status_int)
@ -701,13 +739,10 @@ class TestPost(api_base.FunctionalTest):
def test_create_audit_trigger_decision_engine(self): def test_create_audit_trigger_decision_engine(self):
with mock.patch.object(deapi.DecisionEngineAPI, with mock.patch.object(deapi.DecisionEngineAPI,
'trigger_audit') as de_mock: 'trigger_audit') as de_mock:
audit_dict = post_get_test_audit(state=objects.audit.State.PENDING) audit_dict = post_get_test_audit(
del audit_dict['uuid'] state=objects.audit.State.PENDING,
del audit_dict['state'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['interval'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict) response = self.post_json('/audits', audit_dict)
de_mock.assert_called_once_with(mock.ANY, response.json['uuid']) de_mock.assert_called_once_with(mock.ANY, response.json['uuid'])
@ -726,13 +761,10 @@ class TestPost(api_base.FunctionalTest):
def test_create_audit_parameters_no_predefined_strategy( def test_create_audit_parameters_no_predefined_strategy(
self, mock_trigger_audit): self, mock_trigger_audit):
mock_trigger_audit.return_value = mock.ANY mock_trigger_audit.return_value = mock.ANY
audit_dict = post_get_test_audit(parameters={'name': 'Tom'}) audit_dict = post_get_test_audit(
del audit_dict['uuid'] parameters={'name': 'Tom'},
del audit_dict['state'] params_to_exclude=['uuid', 'state', 'interval', 'scope',
del audit_dict['interval'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
response = self.post_json('/audits', audit_dict, expect_errors=True) response = self.post_json('/audits', audit_dict, expect_errors=True)
self.assertEqual('application/json', response.content_type) self.assertEqual('application/json', response.content_type)
@ -831,16 +863,13 @@ class TestPost(api_base.FunctionalTest):
test_time = datetime.datetime(2000, 1, 1, 0, 0) test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time mock_utcnow.return_value = test_time
audit_dict = post_get_test_audit() audit_dict = post_get_test_audit(
params_to_exclude=['state', 'interval', 'scope',
'next_run_time', 'hostname', 'goal'])
normal_name = 'this audit name is just for test' normal_name = 'this audit name is just for test'
# long_name length exceeds 63 characters # long_name length exceeds 63 characters
long_name = normal_name + audit_dict['uuid'] long_name = normal_name + audit_dict['uuid']
del audit_dict['uuid'] del audit_dict['uuid']
del audit_dict['state']
del audit_dict['interval']
del audit_dict['scope']
del audit_dict['next_run_time']
del audit_dict['hostname']
audit_dict['name'] = normal_name audit_dict['name'] = normal_name
response = self.post_json('/audits', audit_dict) response = self.post_json('/audits', audit_dict)
@ -962,12 +991,10 @@ class TestAuditPolicyEnforcement(api_base.FunctionalTest):
'op': 'replace'}], expect_errors=True) 'op': 'replace'}], expect_errors=True)
def test_policy_disallow_create(self): def test_policy_disallow_create(self):
audit_dict = post_get_test_audit(state=objects.audit.State.PENDING) audit_dict = post_get_test_audit(
del audit_dict['uuid'] state=objects.audit.State.PENDING,
del audit_dict['state'] params_to_exclude=['uuid', 'state', 'scope',
del audit_dict['scope'] 'next_run_time', 'hostname', 'goal'])
del audit_dict['next_run_time']
del audit_dict['hostname']
self._common_policy_check( self._common_policy_check(
"audit:create", self.post_json, '/audits', audit_dict, "audit:create", self.post_json, '/audits', audit_dict,
expect_errors=True) expect_errors=True)