Merge "Audit API supports new force option"
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add force field to Audit. User can set --force to enable the new option when
|
||||||
|
launching audit. If force is True, audit will be executed despite of ongoing
|
||||||
|
actionplan. The new audit may create a wrong actionplan if they use the same
|
||||||
|
data model.
|
||||||
@@ -73,6 +73,8 @@ def hide_fields_in_newer_versions(obj):
|
|||||||
if not api_utils.allow_start_end_audit_time():
|
if not api_utils.allow_start_end_audit_time():
|
||||||
obj.start_time = wtypes.Unset
|
obj.start_time = wtypes.Unset
|
||||||
obj.end_time = wtypes.Unset
|
obj.end_time = wtypes.Unset
|
||||||
|
if not api_utils.allow_force():
|
||||||
|
obj.force = wtypes.Unset
|
||||||
|
|
||||||
|
|
||||||
class AuditPostType(wtypes.Base):
|
class AuditPostType(wtypes.Base):
|
||||||
@@ -194,7 +196,8 @@ class AuditPostType(wtypes.Base):
|
|||||||
scope=self.scope,
|
scope=self.scope,
|
||||||
auto_trigger=self.auto_trigger,
|
auto_trigger=self.auto_trigger,
|
||||||
start_time=self.start_time,
|
start_time=self.start_time,
|
||||||
end_time=self.end_time)
|
end_time=self.end_time,
|
||||||
|
force=self.force)
|
||||||
|
|
||||||
|
|
||||||
class AuditPatchType(types.JsonPatchType):
|
class AuditPatchType(types.JsonPatchType):
|
||||||
|
|||||||
@@ -165,3 +165,12 @@ def allow_start_end_audit_time():
|
|||||||
audits.
|
audits.
|
||||||
"""
|
"""
|
||||||
return pecan.request.version.minor >= versions.MINOR_1_START_END_TIMING
|
return pecan.request.version.minor >= versions.MINOR_1_START_END_TIMING
|
||||||
|
|
||||||
|
|
||||||
|
def allow_force():
|
||||||
|
"""Check if we should support optional force attribute for Audit.
|
||||||
|
|
||||||
|
Version 1.2 of the API added support for forced audits that allows to
|
||||||
|
launch audit when other action plan is ongoing.
|
||||||
|
"""
|
||||||
|
return pecan.request.version.minor >= versions.MINOR_2_FORCE
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ BASE_VERSION = 1
|
|||||||
#
|
#
|
||||||
# v1.0: corresponds to Rocky API
|
# v1.0: corresponds to Rocky API
|
||||||
# v1.1: Add start/end time for continuous audit
|
# v1.1: Add start/end time for continuous audit
|
||||||
|
# v1.2: Add force field to audit
|
||||||
|
|
||||||
MINOR_0_ROCKY = 0
|
MINOR_0_ROCKY = 0
|
||||||
MINOR_1_START_END_TIMING = 1
|
MINOR_1_START_END_TIMING = 1
|
||||||
|
MINOR_2_FORCE = 2
|
||||||
|
|
||||||
MINOR_MAX_VERSION = MINOR_1_START_END_TIMING
|
MINOR_MAX_VERSION = MINOR_2_FORCE
|
||||||
|
|
||||||
# String representations of the minor and maximum versions
|
# String representations of the minor and maximum versions
|
||||||
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_0_ROCKY)
|
_MIN_VERSION_STRING = '{}.{}'.format(BASE_VERSION, MINOR_0_ROCKY)
|
||||||
|
|||||||
@@ -121,7 +121,10 @@ class AuditHandler(BaseAuditHandler):
|
|||||||
|
|
||||||
def pre_execute(self, audit, request_context):
|
def pre_execute(self, audit, request_context):
|
||||||
LOG.debug("Trigger audit %s", audit.uuid)
|
LOG.debug("Trigger audit %s", audit.uuid)
|
||||||
self.check_ongoing_action_plans(request_context)
|
# If audit.force is true, audit will be executed
|
||||||
|
# despite of ongoing actionplan
|
||||||
|
if not audit.force:
|
||||||
|
self.check_ongoing_action_plans(request_context)
|
||||||
# Write hostname that will execute this audit.
|
# Write hostname that will execute this audit.
|
||||||
audit.hostname = CONF.host
|
audit.hostname = CONF.host
|
||||||
# change state of the audit to ONGOING
|
# change state of the audit to ONGOING
|
||||||
|
|||||||
@@ -950,6 +950,39 @@ class TestPost(api_base.FunctionalTest):
|
|||||||
self.assertIn(expected_error_msg, response.json['error_message'])
|
self.assertIn(expected_error_msg, response.json['error_message'])
|
||||||
assert not mock_trigger_audit.called
|
assert not mock_trigger_audit.called
|
||||||
|
|
||||||
|
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
|
||||||
|
def test_create_audit_with_force_false(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', 'goal'])
|
||||||
|
|
||||||
|
response = self.post_json(
|
||||||
|
'/audits',
|
||||||
|
audit_dict,
|
||||||
|
headers={'OpenStack-API-Version': 'infra-optim 1.2'})
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
|
self.assertEqual(201, response.status_int)
|
||||||
|
self.assertFalse(response.json['force'])
|
||||||
|
|
||||||
|
@mock.patch.object(deapi.DecisionEngineAPI, 'trigger_audit')
|
||||||
|
def test_create_audit_with_force_true(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', 'goal'])
|
||||||
|
|
||||||
|
audit_dict['force'] = True
|
||||||
|
response = self.post_json(
|
||||||
|
'/audits',
|
||||||
|
audit_dict,
|
||||||
|
headers={'OpenStack-API-Version': 'infra-optim 1.2'})
|
||||||
|
self.assertEqual('application/json', response.content_type)
|
||||||
|
self.assertEqual(201, response.status_int)
|
||||||
|
self.assertTrue(response.json['force'])
|
||||||
|
|
||||||
|
|
||||||
class TestDelete(api_base.FunctionalTest):
|
class TestDelete(api_base.FunctionalTest):
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,13 @@ class TestAutoTriggerActionPlan(base.DbTestCase):
|
|||||||
mock_applier.assert_called_once_with(self.context,
|
mock_applier.assert_called_once_with(self.context,
|
||||||
self.recommended_action_plan.uuid)
|
self.recommended_action_plan.uuid)
|
||||||
|
|
||||||
|
@mock.patch.object(oneshot.OneShotAuditHandler, 'do_execute')
|
||||||
|
def test_trigger_audit_with_force(self, mock_do_execute):
|
||||||
|
audit_handler = oneshot.OneShotAuditHandler()
|
||||||
|
self.audit.force = True
|
||||||
|
audit_handler.execute(self.audit, self.context)
|
||||||
|
self.assertTrue(mock_do_execute.called)
|
||||||
|
|
||||||
|
|
||||||
class TestContinuousAuditHandler(base.DbTestCase):
|
class TestContinuousAuditHandler(base.DbTestCase):
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user