Refactored check for invalid goal

When creating a new audit template, the verification of its goal
existence was previously done in watcher/objects/audit_template.py.
This check was moved to api/controllers/v1/audit_template.py, rather
than in the DAO class.

Change-Id: I6efb0657f64c46a56914a946ec78013b9e47331b
Closes-Bug: #1536191
This commit is contained in:
Larry Rensing
2016-02-15 09:52:59 -06:00
parent 083b170083
commit 920bd502ec
4 changed files with 45 additions and 10 deletions

View File

@@ -50,6 +50,7 @@ provided as a list of key-value pairs.
import datetime import datetime
from oslo_config import cfg
import pecan import pecan
from pecan import rest from pecan import rest
import wsme import wsme
@@ -67,11 +68,25 @@ from watcher import objects
class AuditTemplatePatchType(types.JsonPatchType): class AuditTemplatePatchType(types.JsonPatchType):
@staticmethod @staticmethod
def mandatory_attrs(): def mandatory_attrs():
return [] 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): class AuditTemplate(base.APIBase):
"""API representation of a audit template. """API representation of a audit template.
@@ -156,6 +171,12 @@ class AuditTemplate(base.APIBase):
updated_at=datetime.datetime.utcnow()) updated_at=datetime.datetime.utcnow())
return cls._convert_with_links(sample, 'http://localhost:9322', expand) 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): class AuditTemplateCollection(collection.Collection):
"""API representation of a collection of audit templates.""" """API representation of a collection of audit templates."""
@@ -287,12 +308,14 @@ class AuditTemplatesController(rest.RestController):
return AuditTemplate.convert_with_links(rpc_audit_template) return AuditTemplate.convert_with_links(rpc_audit_template)
@wsme.validate(types.uuid, AuditTemplate)
@wsme_pecan.wsexpose(AuditTemplate, body=AuditTemplate, status_code=201) @wsme_pecan.wsexpose(AuditTemplate, body=AuditTemplate, status_code=201)
def post(self, audit_template): def post(self, audit_template):
"""Create a new audit template. """Create a new audit template.
:param audit template: a audit template within the request body. :param audit template: a audit template within the request body.
""" """
if self.from_audit_templates: if self.from_audit_templates:
raise exception.OperationNotPermitted raise exception.OperationNotPermitted

View File

@@ -47,7 +47,6 @@ contain a list of extra parameters related to the
provided as a list of key-value pairs. provided as a list of key-value pairs.
""" """
from oslo_config import cfg
from watcher.common import exception from watcher.common import exception
from watcher.common import utils from watcher.common import utils
from watcher.db import api as dbapi from watcher.db import api as dbapi
@@ -204,9 +203,6 @@ class AuditTemplate(base.WatcherObject):
""" """
values = self.obj_get_changes() 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) db_audit_template = self.dbapi.create_audit_template(values)
self._from_db_object(self, db_audit_template) self._from_db_object(self, db_audit_template)

View File

@@ -237,6 +237,8 @@ class TestPatch(api_base.FunctionalTest):
self.mock_audit_template_update = p.start() self.mock_audit_template_update = p.start()
self.mock_audit_template_update.side_effect = \ self.mock_audit_template_update.side_effect = \
self._simulate_rpc_audit_template_update 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) self.addCleanup(p.stop)
def _simulate_rpc_audit_template_update(self, audit_template): 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) test_time = datetime.datetime(2000, 1, 1, 0, 0)
mock_utcnow.return_value = test_time mock_utcnow.return_value = test_time
new_goal = 'BALANCE_LOAD' new_goal = "BASIC"
response = self.get_json( response = self.get_json(
'/audit_templates/%s' % self.audit_template.uuid) '/audit_templates/%s' % self.audit_template.uuid)
self.assertNotEqual(new_goal, response['goal']) self.assertNotEqual(new_goal, response['goal'])
@@ -272,7 +274,7 @@ class TestPatch(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
new_goal = 'BALANCE_LOAD' new_goal = 'BASIC'
response = self.get_json(urlparse.quote( response = self.get_json(urlparse.quote(
'/audit_templates/%s' % self.audit_template.name)) '/audit_templates/%s' % self.audit_template.name))
self.assertNotEqual(new_goal, response['goal']) self.assertNotEqual(new_goal, response['goal'])
@@ -294,15 +296,29 @@ class TestPatch(api_base.FunctionalTest):
def test_replace_non_existent_audit_template(self): def test_replace_non_existent_audit_template(self):
response = self.patch_json( response = self.patch_json(
'/audit_templates/%s' % utils.generate_uuid(), '/audit_templates/%s' % utils.generate_uuid(),
[{'path': '/goal', 'value': 'BALANCE_LOAD', [{'path': '/goal', 'value': 'DUMMY',
'op': 'replace'}], 'op': 'replace'}],
expect_errors=True) expect_errors=True)
self.assertEqual(404, response.status_int) self.assertEqual(404, response.status_int)
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'])
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): def test_add_ok(self):
new_goal = 'BALANCE_LOAD' new_goal = 'DUMMY'
response = self.patch_json( response = self.patch_json(
'/audit_templates/%s' % self.audit_template.uuid, '/audit_templates/%s' % self.audit_template.uuid,
[{'path': '/goal', 'value': new_goal, 'op': 'add'}]) [{'path': '/goal', 'value': new_goal, 'op': 'add'}])

View File

@@ -137,7 +137,7 @@ class TestAuditTemplate(base.BaseInfraOptimTest):
new_name = 'my at new name %s' % uuid.uuid4() new_name = 'my at new name %s' % uuid.uuid4()
new_description = 'my new at description' new_description = 'my new at description'
new_host_aggregate = 10 new_host_aggregate = 10
new_goal = 'A NEW GOAL' new_goal = 'BASIC_CONSOLIDATION'
new_extra = {'key1': 'new-value1', 'key2': 'new-value2'} new_extra = {'key1': 'new-value1', 'key2': 'new-value2'}
patch = [{'path': '/name', patch = [{'path': '/name',