diff --git a/watcher/api/controllers/v1/audit_template.py b/watcher/api/controllers/v1/audit_template.py index 4b850c9c2..6e1ee2c73 100644 --- a/watcher/api/controllers/v1/audit_template.py +++ b/watcher/api/controllers/v1/audit_template.py @@ -50,6 +50,7 @@ provided as a list of key-value pairs. import datetime +from oslo_config import cfg import pecan from pecan import rest import wsme @@ -67,11 +68,25 @@ from watcher import objects class AuditTemplatePatchType(types.JsonPatchType): - @staticmethod def mandatory_attrs(): return [] + @staticmethod + def validate(patch): + if patch.path == "/goal": + AuditTemplatePatchType._validate_goal(patch) + return types.JsonPatchType.validate(patch) + + @staticmethod + def _validate_goal(patch): + serialized_patch = {'path': patch.path, 'op': patch.op} + if patch.value is not wsme.Unset: + serialized_patch['value'] = patch.value + new_goal = patch.value + if new_goal and new_goal not in cfg.CONF.watcher_goals.goals.keys(): + raise exception.InvalidGoal(goal=new_goal) + class AuditTemplate(base.APIBase): """API representation of a audit template. @@ -156,6 +171,12 @@ class AuditTemplate(base.APIBase): updated_at=datetime.datetime.utcnow()) return cls._convert_with_links(sample, 'http://localhost:9322', expand) + @staticmethod + def validate(audit_template): + if audit_template.goal not in cfg.CONF.watcher_goals.goals.keys(): + raise exception.InvalidGoal(audit_template.goal) + return audit_template + class AuditTemplateCollection(collection.Collection): """API representation of a collection of audit templates.""" @@ -287,12 +308,14 @@ class AuditTemplatesController(rest.RestController): return AuditTemplate.convert_with_links(rpc_audit_template) + @wsme.validate(types.uuid, AuditTemplate) @wsme_pecan.wsexpose(AuditTemplate, body=AuditTemplate, status_code=201) def post(self, audit_template): """Create a new audit template. :param audit template: a audit template within the request body. """ + if self.from_audit_templates: raise exception.OperationNotPermitted diff --git a/watcher/objects/audit_template.py b/watcher/objects/audit_template.py index 33bdb870b..95b026754 100644 --- a/watcher/objects/audit_template.py +++ b/watcher/objects/audit_template.py @@ -47,7 +47,6 @@ contain a list of extra parameters related to the provided as a list of key-value pairs. """ -from oslo_config import cfg from watcher.common import exception from watcher.common import utils from watcher.db import api as dbapi @@ -204,9 +203,6 @@ class AuditTemplate(base.WatcherObject): """ values = self.obj_get_changes() - goal = values['goal'] - if goal not in cfg.CONF.watcher_goals.goals.keys(): - raise exception.InvalidGoal(goal=goal) db_audit_template = self.dbapi.create_audit_template(values) self._from_db_object(self, db_audit_template) diff --git a/watcher/tests/api/v1/test_audit_templates.py b/watcher/tests/api/v1/test_audit_templates.py index 7039961e7..5844c931e 100644 --- a/watcher/tests/api/v1/test_audit_templates.py +++ b/watcher/tests/api/v1/test_audit_templates.py @@ -237,6 +237,8 @@ class TestPatch(api_base.FunctionalTest): self.mock_audit_template_update = p.start() self.mock_audit_template_update.side_effect = \ self._simulate_rpc_audit_template_update + cfg.CONF.set_override('goals', {"DUMMY": "DUMMY", "BASIC": "BASIC"}, + group='watcher_goals', enforce_type=True) self.addCleanup(p.stop) def _simulate_rpc_audit_template_update(self, audit_template): @@ -248,7 +250,7 @@ class TestPatch(api_base.FunctionalTest): test_time = datetime.datetime(2000, 1, 1, 0, 0) mock_utcnow.return_value = test_time - new_goal = 'BALANCE_LOAD' + new_goal = "BASIC" response = self.get_json( '/audit_templates/%s' % self.audit_template.uuid) self.assertNotEqual(new_goal, response['goal']) @@ -272,7 +274,7 @@ class TestPatch(api_base.FunctionalTest): test_time = datetime.datetime(2000, 1, 1, 0, 0) mock_utcnow.return_value = test_time - new_goal = 'BALANCE_LOAD' + new_goal = 'BASIC' response = self.get_json(urlparse.quote( '/audit_templates/%s' % self.audit_template.name)) self.assertNotEqual(new_goal, response['goal']) @@ -294,15 +296,29 @@ class TestPatch(api_base.FunctionalTest): def test_replace_non_existent_audit_template(self): response = self.patch_json( '/audit_templates/%s' % utils.generate_uuid(), - [{'path': '/goal', 'value': 'BALANCE_LOAD', + [{'path': '/goal', 'value': 'DUMMY', 'op': 'replace'}], expect_errors=True) self.assertEqual(404, response.status_int) self.assertEqual('application/json', response.content_type) self.assertTrue(response.json['error_message']) + def test_replace_invalid_goal(self): + with mock.patch.object( + self.dbapi, + 'update_audit_template', + wraps=self.dbapi.update_audit_template + ) as cn_mock: + response = self.patch_json( + '/audit_templates/%s' % self.audit_template.uuid, + [{'path': '/goal', 'value': 'INVALID_GOAL', + 'op': 'replace'}], + expect_errors=True) + self.assertEqual(400, response.status_int) + assert not cn_mock.called + def test_add_ok(self): - new_goal = 'BALANCE_LOAD' + new_goal = 'DUMMY' response = self.patch_json( '/audit_templates/%s' % self.audit_template.uuid, [{'path': '/goal', 'value': new_goal, 'op': 'add'}]) diff --git a/watcher_tempest_plugin/tests/api/admin/test_audit_template.py b/watcher_tempest_plugin/tests/api/admin/test_audit_template.py index 9a4911049..345b68a9c 100644 --- a/watcher_tempest_plugin/tests/api/admin/test_audit_template.py +++ b/watcher_tempest_plugin/tests/api/admin/test_audit_template.py @@ -137,7 +137,7 @@ class TestAuditTemplate(base.BaseInfraOptimTest): new_name = 'my at new name %s' % uuid.uuid4() new_description = 'my new at description' new_host_aggregate = 10 - new_goal = 'A NEW GOAL' + new_goal = 'BASIC_CONSOLIDATION' new_extra = {'key1': 'new-value1', 'key2': 'new-value2'} patch = [{'path': '/name',