From 6e31938a0fff264200ddad235ed57398d3ffcb02 Mon Sep 17 00:00:00 2001 From: Dmitry Guryanov Date: Mon, 22 Aug 2016 11:46:32 +0300 Subject: [PATCH] Split @content decorator Decorator content does too many different actions, so let's split it into 3 decorators: @handle_errors, @validate and @serialize. We can skip calling @serialize in many places, because those methods just raise some http error. Also I've changed return value of all handlers from serialized json (string) to python's structures, lists and dicts. So It's become possible to implement different serializers and I've added support for YAML output. Change-Id: Ia3da3bd809bcca923d53666eca54def78c995f65 Closes-Bug: #1606211 --- nailgun/nailgun/api/v1/handlers/assignment.py | 9 +- nailgun/nailgun/api/v1/handlers/base.py | 198 +++++++++--------- nailgun/nailgun/api/v1/handlers/capacity.py | 11 +- nailgun/nailgun/api/v1/handlers/cluster.py | 57 +++-- .../api/v1/handlers/cluster_plugin_link.py | 27 ++- nailgun/nailgun/api/v1/handlers/component.py | 11 +- .../api/v1/handlers/deployment_graph.py | 41 ++-- .../api/v1/handlers/deployment_history.py | 8 +- nailgun/nailgun/api/v1/handlers/logs.py | 21 +- .../api/v1/handlers/master_node_settings.py | 9 +- nailgun/nailgun/api/v1/handlers/node.py | 44 ++-- nailgun/nailgun/api/v1/handlers/node_group.py | 16 +- .../nailgun/api/v1/handlers/notifications.py | 10 +- .../api/v1/handlers/openstack_config.py | 20 +- .../nailgun/api/v1/handlers/orchestrator.py | 37 +++- nailgun/nailgun/api/v1/handlers/plugin.py | 9 +- .../nailgun/api/v1/handlers/plugin_link.py | 26 ++- nailgun/nailgun/api/v1/handlers/release.py | 26 ++- nailgun/nailgun/api/v1/handlers/removed.py | 8 +- nailgun/nailgun/api/v1/handlers/role.py | 27 ++- nailgun/nailgun/api/v1/handlers/tasks.py | 15 +- .../nailgun/api/v1/handlers/transactions.py | 14 +- nailgun/nailgun/api/v1/handlers/version.py | 8 +- nailgun/nailgun/api/v1/handlers/vms.py | 15 +- .../handlers/network_configuration.py | 26 ++- .../network_manager/handlers/network_group.py | 16 +- .../network_manager/handlers/nic.py | 24 ++- .../network_manager/handlers/vip.py | 31 ++- .../volume_manager/handlers/disks.py | 20 +- nailgun/nailgun/fake_keystone/handlers.py | 20 +- .../test_graph_related_handlers.py | 2 + nailgun/nailgun/test/unit/test_handlers.py | 63 ++++-- 32 files changed, 581 insertions(+), 288 deletions(-) diff --git a/nailgun/nailgun/api/v1/handlers/assignment.py b/nailgun/nailgun/api/v1/handlers/assignment.py index cc36780c76..eb80b5b88a 100644 --- a/nailgun/nailgun/api/v1/handlers/assignment.py +++ b/nailgun/nailgun/api/v1/handlers/assignment.py @@ -19,7 +19,8 @@ Handlers dealing with nodes assignment """ from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.assignment import NodeAssignmentValidator from nailgun.api.v1.validators.assignment import NodeUnassignmentValidator @@ -31,7 +32,8 @@ class NodeAssignmentHandler(BaseHandler): """Node assignment handler""" validator = NodeAssignmentValidator - @content + @handle_errors + @validate def POST(self, cluster_id): """:returns: Empty string @@ -62,7 +64,8 @@ class NodeUnassignmentHandler(BaseHandler): """Node assignment handler""" validator = NodeUnassignmentValidator - @content + @handle_errors + @validate def POST(self, cluster_id): """:returns: Empty string diff --git a/nailgun/nailgun/api/v1/handlers/base.py b/nailgun/nailgun/api/v1/handlers/base.py index ca231c4f5b..1a94997eae 100644 --- a/nailgun/nailgun/api/v1/handlers/base.py +++ b/nailgun/nailgun/api/v1/handlers/base.py @@ -15,12 +15,13 @@ # under the License. from datetime import datetime +from decorator import decorator +from oslo_serialization import jsonutils import six import traceback +import yaml -from decorator import decorator from distutils.version import StrictVersion -from oslo_serialization import jsonutils from sqlalchemy import exc as sa_exc import web @@ -31,7 +32,6 @@ from nailgun.api.v1.validators.orchestrator_graph import \ from nailgun import consts from nailgun.db import db from nailgun import errors -from nailgun.errors.base import NailgunException from nailgun.logger import logger from nailgun import objects from nailgun.objects.serializers.base import BasicSerializer @@ -271,40 +271,25 @@ class BaseHandler(object): else: return default + @staticmethod + def get_requested_mime(): + accept = web.ctx.env.get("HTTP_ACCEPT", "application/json") + accept = accept.strip().split(',')[0] + accept = accept.split(';')[0] + return accept -def content_json(func, cls, *args, **kwargs): - json_resp = lambda data: ( - jsonutils.dumps(data) - if isinstance(data, (dict, list)) or data is None else data - ) - request_validate_needed = True +def json_resp(data): + if isinstance(data, (dict, list)) or data is None: + return jsonutils.dumps(data) + else: + return data - resource_type = "single" - if issubclass( - cls.__class__, - CollectionHandler - ) and not func.func_name == "POST": - resource_type = "collection" - - if ( - func.func_name in ("GET", "DELETE") or - getattr(cls.__class__, 'validator', None) is None or - resource_type == "single" and not cls.validator.single_schema or - resource_type == "collection" and not cls.validator.collection_schema - ): - request_validate_needed = False +@decorator +def handle_errors(func, cls, *args, **kwargs): try: - if request_validate_needed: - BaseHandler.checked_data( - cls.validator.validate_request, - resource_type=resource_type - ) - - resp = func(cls, *args, **kwargs) - except web.notmodified: - raise + return func(cls, *args, **kwargs) except web.HTTPError as http_error: if http_error.status_code != 204: web.header('Content-Type', 'application/json', unique=True) @@ -316,7 +301,7 @@ def content_json(func, cls, *args, **kwargs): else: http_error.data = json_resp(http_error.data) raise - except NailgunException as exc: + except errors.NailgunException as exc: logger.exception('NailgunException occured') http_error = BaseHandler.http(400, exc.message) web.header('Content-Type', 'text/plain') @@ -336,62 +321,66 @@ def content_json(func, cls, *args, **kwargs): web.header('Content-Type', 'text/plain') raise http_error - web.header('Content-Type', 'application/json', unique=True) - return json_resp(resp) + +@decorator +def validate(func, cls, *args, **kwargs): + request_validation_needed = True + + resource_type = "single" + if issubclass( + cls.__class__, + CollectionHandler + ) and not func.func_name == "POST": + resource_type = "collection" + + if ( + func.func_name in ("GET", "DELETE") or + getattr(cls.__class__, 'validator', None) is None or + (resource_type == "single" and not cls.validator.single_schema) or + (resource_type == "collection" and not cls.validator.collection_schema) + ): + request_validation_needed = False + + if request_validation_needed: + BaseHandler.checked_data( + cls.validator.validate_request, + resource_type=resource_type + ) + + return func(cls, *args, **kwargs) -def content(*args, **kwargs): - """Set context-type of response based on Accept header +@decorator +def serialize(func, cls, *args, **kwargs): + """Set context-type of response based on Accept header. This decorator checks Accept header received from client and returns corresponding wrapper (only JSON is currently supported). It can be used as is: - @content - def GET(self): - ... - - Default behavior may be overriden by passing list of - exact mimetypes to decorator: - - @content(["text/plain"]) + @handle_errors + @validate + @serialize def GET(self): ... """ - # TODO(ikutukov): this decorator is not coherent and doing more - # than just a response mimetype setting via type-specific content_json - # method that perform validation. - # Before you start to implement handler business logic ensure that - # @content decorator not already doing what you are planning to write. - # I think that validation routine and common http headers formation not - # depending on each other and should be decoupled. At least they should - # not be under one decorator with abstract name. + accepted_types = ( + "application/json", + "application/x-yaml", + "*/*" + ) + accept = cls.get_requested_mime() + if accept not in accepted_types: + raise BaseHandler.http(415) - exact_mimetypes = None - if len(args) >= 1 and isinstance(args[0], list): - exact_mimetypes = args[0] - - @decorator - def wrapper(func, *args, **kwargs): - accept = web.ctx.env.get("HTTP_ACCEPT", "application/json") - accepted_types = [ - "application/json", - "*/*" - ] - if exact_mimetypes and isinstance(exact_mimetypes, list): - accepted_types = exact_mimetypes - if any(map(lambda m: m in accept, accepted_types)): - return content_json(func, *args, **kwargs) - else: - raise BaseHandler.http(415) - - # case of @content without arguments, meaning arg[0] to be callable - # handler - if len(args) >= 1 and callable(args[0]): - return wrapper(args[0], *args[1:], **kwargs) - - # case of @content(["mimetype"]) with explicit arguments - return wrapper + resp = func(cls, *args, **kwargs) + if accept == 'application/x-yaml': + web.header('Content-Type', 'application/x-yaml', unique=True) + return yaml.dump(resp, default_flow_style=False) + else: + # default is json + web.header('Content-Type', 'application/json', unique=True) + return jsonutils.dumps(resp) class SingleHandler(BaseHandler): @@ -399,7 +388,9 @@ class SingleHandler(BaseHandler): single = None validator = BasicValidator - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """:returns: JSONized REST object. @@ -407,9 +398,11 @@ class SingleHandler(BaseHandler): * 404 (object not found in db) """ obj = self.get_object_or_404(self.single, obj_id) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """:returns: JSONized REST object. @@ -423,9 +416,10 @@ class SingleHandler(BaseHandler): instance=obj ) self.single.update(obj, data) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate def DELETE(self, obj_id): """:returns: Empty string @@ -452,16 +446,19 @@ class CollectionHandler(BaseHandler): validator = BasicValidator eager = () - @content + @handle_errors + @validate + @serialize def GET(self): """:returns: Collection of JSONized REST objects. :http: * 200 (OK) """ q = self.collection.eager(None, self.eager) - return self.collection.to_json(q) + return self.collection.to_list(q) - @content + @handle_errors + @validate def POST(self): """:returns: JSONized REST object. @@ -495,7 +492,9 @@ class DBSingletonHandler(BaseHandler): return instance - @content + @handle_errors + @validate + @serialize def GET(self): """Get singleton object from DB @@ -504,9 +503,11 @@ class DBSingletonHandler(BaseHandler): """ instance = self.get_one_or_404() - return self.single.to_json(instance) + return self.single.to_dict(instance) - @content + @handle_errors + @validate + @serialize def PUT(self): """Change object in DB @@ -520,9 +521,11 @@ class DBSingletonHandler(BaseHandler): self.single.update(instance, data) - return self.single.to_json(instance) + return self.single.to_dict(instance) - @content + @handle_errors + @validate + @serialize def PATCH(self): """Update object @@ -538,7 +541,7 @@ class DBSingletonHandler(BaseHandler): self.single.serializer.serialize(instance), data )) - return self.single.to_json(instance) + return self.single.to_dict(instance) # TODO(enchantner): rewrite more handlers to inherit from this @@ -557,7 +560,8 @@ class DeferredTaskHandler(BaseHandler): def get_options(cls): return {} - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -618,7 +622,9 @@ class OrchestratorDeploymentTasksHandler(SingleHandler): validator = GraphSolverTasksValidator - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """:returns: Deployment tasks @@ -654,7 +660,9 @@ class OrchestratorDeploymentTasksHandler(SingleHandler): 'name.'.format(e.task_name)) return tasks - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """:returns: Deployment tasks diff --git a/nailgun/nailgun/api/v1/handlers/capacity.py b/nailgun/nailgun/api/v1/handlers/capacity.py index ba252c6d40..5c6dab748e 100644 --- a/nailgun/nailgun/api/v1/handlers/capacity.py +++ b/nailgun/nailgun/api/v1/handlers/capacity.py @@ -26,7 +26,9 @@ import web from nailgun import objects from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.task.manager import GenerateCapacityLogTaskManager @@ -78,14 +80,17 @@ class CapacityLogHandler(BaseHandler): "report" ) - @content + @handle_errors + @validate + @serialize def GET(self): capacity_log = objects.CapacityLog.get_latest() if not capacity_log: raise self.http(404) return self.render(capacity_log) - @content + @handle_errors + @validate def PUT(self): """Starts capacity data generation. diff --git a/nailgun/nailgun/api/v1/handlers/cluster.py b/nailgun/nailgun/api/v1/handlers/cluster.py index 370b507657..2e18ac990d 100644 --- a/nailgun/nailgun/api/v1/handlers/cluster.py +++ b/nailgun/nailgun/api/v1/handlers/cluster.py @@ -23,10 +23,12 @@ import web from nailgun.api.v1.handlers.base import BaseHandler from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import content from nailgun.api.v1.handlers.base import DeferredTaskHandler +from nailgun.api.v1.handlers.base import handle_errors from nailgun.api.v1.handlers.base import OrchestratorDeploymentTasksHandler +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.handlers.deployment_graph import \ RelatedDeploymentGraphCollectionHandler from nailgun.api.v1.handlers.deployment_graph import \ @@ -55,7 +57,9 @@ class ClusterHandler(SingleHandler): single = objects.Cluster validator = ClusterValidator - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """:returns: JSONized Cluster object. @@ -79,9 +83,10 @@ class ClusterHandler(SingleHandler): except errors.NetworkTemplateCannotBeApplied as exc: raise self.http(400, exc.message) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate def DELETE(self, obj_id): """:returns: {} @@ -174,7 +179,9 @@ class ClusterAttributesHandler(BaseHandler): validator = ClusterAttributesValidator - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized Cluster attributes. @@ -206,7 +213,9 @@ class ClusterAttributesHandler(BaseHandler): # entity and PATCH method for changing its parts. return self.PATCH(cluster_id) - @content + @handle_errors + @validate + @serialize def PATCH(self, cluster_id): """:returns: JSONized Cluster attributes. @@ -239,7 +248,9 @@ class ClusterAttributesDefaultsHandler(BaseHandler): "editable", ) - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized default Cluster attributes. @@ -253,7 +264,9 @@ class ClusterAttributesDefaultsHandler(BaseHandler): raise self.http(500, "No attributes found!") return {"editable": attrs} - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id): """:returns: JSONized Cluster attributes. @@ -290,7 +303,9 @@ class ClusterAttributesDefaultsHandler(BaseHandler): class ClusterAttributesDeployedHandler(BaseHandler): """Cluster deployed attributes handler""" - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized deployed Cluster editable attributes with plugins @@ -312,7 +327,9 @@ class ClusterAttributesDeployedHandler(BaseHandler): class ClusterGeneratedData(BaseHandler): """Cluster generated data""" - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized cluster generated data @@ -333,7 +350,9 @@ class ClusterPluginsDeploymentTasksHandler(BaseHandler): """Handler for cluster plugins merged deployment tasks serialization.""" single = objects.Cluster - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """:returns: Deployment tasks @@ -351,7 +370,9 @@ class ClusterReleaseDeploymentTasksHandler(BaseHandler): """Handler for cluster release deployment tasks serialization.""" single = objects.Cluster - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """:returns: Deployment tasks @@ -374,7 +395,9 @@ class VmwareAttributesHandler(BaseHandler): validator = VmwareAttributesValidator - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized Cluster vmware attributes. @@ -401,7 +424,9 @@ class VmwareAttributesHandler(BaseHandler): return self.render(attributes) - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id): """:returns: JSONized Cluster vmware attributes. @@ -442,7 +467,9 @@ class VmwareAttributesHandler(BaseHandler): class VmwareAttributesDefaultsHandler(BaseHandler): """Vmware default attributes handler""" - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized default Cluster vmware attributes. diff --git a/nailgun/nailgun/api/v1/handlers/cluster_plugin_link.py b/nailgun/nailgun/api/v1/handlers/cluster_plugin_link.py index ded80e11e1..66f82a13d1 100644 --- a/nailgun/nailgun/api/v1/handlers/cluster_plugin_link.py +++ b/nailgun/nailgun/api/v1/handlers/cluster_plugin_link.py @@ -15,7 +15,9 @@ # under the License. from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators import cluster_plugin_link from nailgun import errors from nailgun import objects @@ -26,6 +28,9 @@ class ClusterPluginLinkHandler(base.SingleHandler): validator = cluster_plugin_link.ClusterPluginLinkValidator single = objects.ClusterPluginLink + @handle_errors + @validate + @serialize def GET(self, cluster_id, obj_id): """:returns: JSONized REST object. @@ -35,9 +40,11 @@ class ClusterPluginLinkHandler(base.SingleHandler): self.get_object_or_404(objects.Cluster, cluster_id) obj = self.get_object_or_404(self.single, obj_id) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id, obj_id): """:returns: JSONized REST object. @@ -51,7 +58,7 @@ class ClusterPluginLinkHandler(base.SingleHandler): instance=obj ) self.single.update(obj, data) - return self.single.to_json(obj) + return self.single.to_dict(obj) def PATCH(self, cluster_id, obj_id): """:returns: JSONized REST object. @@ -62,7 +69,8 @@ class ClusterPluginLinkHandler(base.SingleHandler): """ return self.PUT(cluster_id, obj_id) - @content + @handle_errors + @validate def DELETE(self, cluster_id, obj_id): """:returns: JSONized REST object. @@ -79,7 +87,9 @@ class ClusterPluginLinkCollectionHandler(base.CollectionHandler): collection = objects.ClusterPluginLinkCollection validator = cluster_plugin_link.ClusterPluginLinkValidator - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: Collection of JSONized ClusterPluginLink objects. @@ -87,11 +97,12 @@ class ClusterPluginLinkCollectionHandler(base.CollectionHandler): * 404 (cluster not found in db) """ self.get_object_or_404(objects.Cluster, cluster_id) - return self.collection.to_json( + return self.collection.to_list( self.collection.get_by_cluster_id(cluster_id) ) - @content + @handle_errors + @validate def POST(self, cluster_id): """:returns: JSONized REST object. diff --git a/nailgun/nailgun/api/v1/handlers/component.py b/nailgun/nailgun/api/v1/handlers/component.py index 10c1f130da..5fb48de5be 100644 --- a/nailgun/nailgun/api/v1/handlers/component.py +++ b/nailgun/nailgun/api/v1/handlers/component.py @@ -14,15 +14,20 @@ # License for the specific language governing permissions and limitations # under the License. -from nailgun.api.v1.handlers import base +from nailgun.api.v1.handlers.base import CollectionHandler +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.objects import Release from nailgun.objects.serializers.release import ComponentSerializer -class ComponentCollectionHandler(base.CollectionHandler): +class ComponentCollectionHandler(CollectionHandler): """Component collection handler""" - @base.content + @handle_errors + @validate + @serialize def GET(self, release_id): """:returns: JSONized component data for release and releated plugins. diff --git a/nailgun/nailgun/api/v1/handlers/deployment_graph.py b/nailgun/nailgun/api/v1/handlers/deployment_graph.py index fac04cf1e6..2d6ce06958 100644 --- a/nailgun/nailgun/api/v1/handlers/deployment_graph.py +++ b/nailgun/nailgun/api/v1/handlers/deployment_graph.py @@ -16,8 +16,10 @@ from nailgun.api.v1.handlers.base import BaseHandler from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators import deployment_graph as validators from nailgun import errors @@ -35,7 +37,9 @@ class RelatedDeploymentGraphHandler(SingleHandler): single = objects.DeploymentGraph related = None # related should be substituted during handler inheritance - @content + @handle_errors + @validate + @serialize def GET(self, obj_id, graph_type=None): """Get deployment graph. @@ -54,12 +58,14 @@ class RelatedDeploymentGraphHandler(SingleHandler): obj = self.get_object_or_404(self.related, int(obj_id)) deployment_graph = self.single.get_for_model(obj, graph_type) if deployment_graph: - return self.single.to_json(deployment_graph) + return self.single.to_dict(deployment_graph) else: raise self.http(404, "Graph with type: {0} is not defined".format( graph_type)) - @content + @handle_errors + @validate + @serialize def POST(self, obj_id, graph_type=None): """Create deployment graph. @@ -85,9 +91,11 @@ class RelatedDeploymentGraphHandler(SingleHandler): else: deployment_graph = self.single.create_for_model( data, obj, graph_type=graph_type) - return self.single.to_json(deployment_graph) + return self.single.to_dict(deployment_graph) - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id, graph_type=None): """Update deployment graph. @@ -109,12 +117,14 @@ class RelatedDeploymentGraphHandler(SingleHandler): deployment_graph = self.single.get_for_model(obj, graph_type) if deployment_graph: self.single.update(deployment_graph, data) - return self.single.to_json(deployment_graph) + return self.single.to_dict(deployment_graph) else: raise self.http(404, "Graph with type: {0} is not defined".format( graph_type)) - @content + @handle_errors + @validate + @serialize def PATCH(self, obj_id, graph_type=None): """Update deployment graph. @@ -132,6 +142,8 @@ class RelatedDeploymentGraphHandler(SingleHandler): """ return self.PUT(obj_id, graph_type) + @handle_errors + @validate def DELETE(self, obj_id, graph_type=None): """Delete deployment graph. @@ -161,7 +173,9 @@ class RelatedDeploymentGraphCollectionHandler(CollectionHandler): collection = objects.DeploymentGraphCollection related = None # related should be substituted during handler inheritance - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """Get deployment graphs list for given object. @@ -176,7 +190,7 @@ class RelatedDeploymentGraphCollectionHandler(CollectionHandler): """ related_model = self.get_object_or_404(self.related, int(obj_id)) graphs = self.collection.get_for_model(related_model) - return self.collection.to_json(graphs) + return self.collection.to_list(graphs) class DeploymentGraphHandler(SingleHandler): @@ -185,7 +199,8 @@ class DeploymentGraphHandler(SingleHandler): validator = validators.DeploymentGraphValidator single = objects.DeploymentGraph - @content + @handle_errors + @validate def DELETE(self, obj_id): """Delete deployment graph. @@ -196,7 +211,6 @@ class DeploymentGraphHandler(SingleHandler): self.single.delete(d_e) raise self.http(204) - @content def PATCH(self, obj_id): return self.PUT(obj_id) @@ -210,7 +224,8 @@ class GraphsExecutorHandler(BaseHandler): validator = validators.GraphExecuteParamsValidator - @content + @handle_errors + @validate def POST(self): """:returns: JSONized Task object. diff --git a/nailgun/nailgun/api/v1/handlers/deployment_history.py b/nailgun/nailgun/api/v1/handlers/deployment_history.py index fa0aab8685..7532af7193 100644 --- a/nailgun/nailgun/api/v1/handlers/deployment_history.py +++ b/nailgun/nailgun/api/v1/handlers/deployment_history.py @@ -15,7 +15,9 @@ # under the License. from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.deployment_history import \ DeploymentHistoryValidator from nailgun import errors @@ -27,7 +29,9 @@ class DeploymentHistoryCollectionHandler(base.CollectionHandler): collection = objects.DeploymentHistoryCollection validator = DeploymentHistoryValidator - @content + @handle_errors + @validate + @serialize def GET(self, transaction_id): """:returns: Collection of JSONized DeploymentHistory records. diff --git a/nailgun/nailgun/api/v1/handlers/logs.py b/nailgun/nailgun/api/v1/handlers/logs.py index 6e840f27cf..cd8bc9c2c4 100644 --- a/nailgun/nailgun/api/v1/handlers/logs.py +++ b/nailgun/nailgun/api/v1/handlers/logs.py @@ -32,7 +32,9 @@ from nailgun import consts from nailgun import objects from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.settings import settings from nailgun.task.manager import DumpTaskManager from nailgun.task.task import DumpTask @@ -195,7 +197,9 @@ def read_log( class LogEntryCollectionHandler(BaseHandler): """Log entry collection handler""" - @content + @handle_errors + @validate + @serialize def GET(self): """Receives following parameters: @@ -382,7 +386,8 @@ class LogEntryCollectionHandler(BaseHandler): class LogPackageHandler(BaseHandler): """Log package handler""" - @content + @handle_errors + @validate def PUT(self): """:returns: JSONized Task object. @@ -404,7 +409,9 @@ class LogPackageHandler(BaseHandler): class LogPackageDefaultConfig(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self): """Generates default config for snapshot @@ -416,7 +423,7 @@ class LogPackageDefaultConfig(BaseHandler): class LogSourceCollectionHandler(BaseHandler): """Log source collection handler""" - @content + @serialize def GET(self): """:returns: Collection of log sources (from settings) @@ -443,7 +450,9 @@ class SnapshotDownloadHandler(BaseHandler): class LogSourceByNodeCollectionHandler(BaseHandler): """Log source by node collection handler""" - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: Collection of log sources by node (from settings) diff --git a/nailgun/nailgun/api/v1/handlers/master_node_settings.py b/nailgun/nailgun/api/v1/handlers/master_node_settings.py index a6acf7d4b7..1f3f76f019 100644 --- a/nailgun/nailgun/api/v1/handlers/master_node_settings.py +++ b/nailgun/nailgun/api/v1/handlers/master_node_settings.py @@ -14,8 +14,9 @@ from oslo_serialization import jsonutils -from nailgun.api.v1.handlers.base import content from nailgun.api.v1.handlers.base import DBSingletonHandler +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.master_node_settings \ import MasterNodeSettingsValidator from nailgun.logger import logger @@ -71,12 +72,14 @@ class MasterNodeSettingsHandler(DBSingletonHandler): self._handle_stats_opt_in(settings_data=jsonutils.loads(result)) return result - @content + @handle_errors + @validate def PUT(self): return self._perform_update( super(MasterNodeSettingsHandler, self).PUT) - @content + @handle_errors + @validate def PATCH(self): return self._perform_update( super(MasterNodeSettingsHandler, self).PATCH) diff --git a/nailgun/nailgun/api/v1/handlers/node.py b/nailgun/nailgun/api/v1/handlers/node.py index eee7e7259e..ac488689c0 100644 --- a/nailgun/nailgun/api/v1/handlers/node.py +++ b/nailgun/nailgun/api/v1/handlers/node.py @@ -24,8 +24,10 @@ import web from nailgun.api.v1.handlers.base import BaseHandler from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators import node as node_validators from nailgun import errors @@ -44,7 +46,9 @@ class NodeHandler(SingleHandler): single = objects.Node validator = node_validators.NodeValidator - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """:returns: JSONized Node object. @@ -69,9 +73,10 @@ class NodeHandler(SingleHandler): except errors.NetworkTemplateCannotBeApplied as exc: raise self.http(400, exc.message) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate def DELETE(self, obj_id): """Deletes a node from DB and from Cobbler. @@ -100,7 +105,9 @@ class NodeCollectionHandler(CollectionHandler): validator = node_validators.NodeValidator collection = objects.NodeCollection - @content + @handle_errors + @validate + @serialize def GET(self): """May receive cluster_id parameter to filter list of nodes @@ -115,9 +122,11 @@ class NodeCollectionHandler(CollectionHandler): elif cluster_id: nodes = nodes.filter_by(cluster_id=cluster_id) - return self.collection.to_json(nodes) + return self.collection.to_list(nodes) - @content + @handle_errors + @validate + @serialize def PUT(self): """:returns: Collection of JSONized Node objects. @@ -147,9 +156,10 @@ class NodeCollectionHandler(CollectionHandler): self.collection.eager_nodes_handlers(None), nodes_updated ) - return self.collection.to_json(nodes) + return self.collection.to_list(nodes) - @content + @handle_errors + @validate def DELETE(self): """Deletes a batch of nodes. @@ -188,7 +198,9 @@ class NodeAgentHandler(BaseHandler): collection = objects.NodeCollection validator = node_validators.NodeValidator - @content + @handle_errors + @validate + @serialize def PUT(self): """:returns: node id. @@ -227,7 +239,9 @@ class NodeAgentHandler(BaseHandler): class NodesAllocationStatsHandler(BaseHandler): """Node allocation stats handler""" - @content + @handle_errors + @validate + @serialize def GET(self): """:returns: Total and unallocated nodes count. @@ -245,7 +259,9 @@ class NodeAttributesHandler(BaseHandler): validator = node_validators.NodeAttributesValidator - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: JSONized Node attributes. @@ -256,7 +272,9 @@ class NodeAttributesHandler(BaseHandler): return objects.Node.get_attributes(node) - @content + @handle_errors + @validate + @serialize def PUT(self, node_id): """:returns: JSONized Node attributes. diff --git a/nailgun/nailgun/api/v1/handlers/node_group.py b/nailgun/nailgun/api/v1/handlers/node_group.py index 657372f982..5539a6ce96 100644 --- a/nailgun/nailgun/api/v1/handlers/node_group.py +++ b/nailgun/nailgun/api/v1/handlers/node_group.py @@ -21,9 +21,10 @@ Handlers dealing with node groups import web from nailgun.api.v1.handlers.base import CollectionHandler +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler - -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.node_group import NodeGroupValidator from nailgun import errors @@ -35,7 +36,8 @@ class NodeGroupHandler(SingleHandler): single = objects.NodeGroup validator = NodeGroupValidator - @content + @handle_errors + @validate def DELETE(self, group_id): """:returns: {} @@ -67,7 +69,9 @@ class NodeGroupCollectionHandler(CollectionHandler): collection = objects.NodeGroupCollection validator = NodeGroupValidator - @content + @handle_errors + @validate + @serialize def GET(self): """May receive cluster_id parameter to filter list of groups @@ -78,10 +82,10 @@ class NodeGroupCollectionHandler(CollectionHandler): user_data = web.input(cluster_id=None) if user_data.cluster_id is not None: - return self.collection.to_json( + return self.collection.to_list( self.collection.get_by_cluster_id( user_data.cluster_id ) ) else: - return self.collection.to_json() + return self.collection.to_list() diff --git a/nailgun/nailgun/api/v1/handlers/notifications.py b/nailgun/nailgun/api/v1/handlers/notifications.py index 2991720af8..6113c8fd89 100644 --- a/nailgun/nailgun/api/v1/handlers/notifications.py +++ b/nailgun/nailgun/api/v1/handlers/notifications.py @@ -24,7 +24,9 @@ from nailgun.api.v1.handlers.base import SingleHandler from nailgun import objects -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.notification import NotificationValidator @@ -40,7 +42,9 @@ class NotificationCollectionHandler(CollectionHandler): collection = objects.NotificationCollection validator = NotificationValidator - @content + @handle_errors + @validate + @serialize def PUT(self): """:returns: Collection of JSONized Notification objects. @@ -54,4 +58,4 @@ class NotificationCollectionHandler(CollectionHandler): notif = self.collection.single.get_by_uid(nd["id"]) self.collection.single.update(notif, nd) notifications_updated.append(notif) - return self.collection.to_json(notifications_updated) + return self.collection.to_list(notifications_updated) diff --git a/nailgun/nailgun/api/v1/handlers/openstack_config.py b/nailgun/nailgun/api/v1/handlers/openstack_config.py index 8a95e9f04f..7cd89d1483 100644 --- a/nailgun/nailgun/api/v1/handlers/openstack_config.py +++ b/nailgun/nailgun/api/v1/handlers/openstack_config.py @@ -18,8 +18,10 @@ import six import web from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.openstack_config import OpenstackConfigValidator from nailgun import errors from nailgun.logger import logger @@ -31,7 +33,9 @@ class OpenstackConfigCollectionHandler(BaseHandler): validator = OpenstackConfigValidator - @content + @handle_errors + @validate + @serialize def GET(self): """Returns list of filtered config objects. @@ -48,7 +52,8 @@ class OpenstackConfigCollectionHandler(BaseHandler): configs, 'node_id', node_ids) return objects.OpenstackConfigCollection.to_list(configs) - @content + @handle_errors + @validate def POST(self): """Creates new config object. @@ -72,7 +77,8 @@ class OpenstackConfigHandler(SingleHandler): single = objects.OpenstackConfig validator = OpenstackConfigValidator - @content + @handle_errors + @validate def PUT(self, obj_id): """Update an existing configuration is not allowed @@ -80,7 +86,8 @@ class OpenstackConfigHandler(SingleHandler): """ raise self.http(405) - @content + @handle_errors + @validate def DELETE(self, obj_id): """:returns: Empty string @@ -111,7 +118,8 @@ class OpenstackConfigExecuteHandler(BaseHandler): validator = OpenstackConfigValidator task_manager = OpenstackConfigTaskManager - @content + @handle_errors + @validate def PUT(self): """Executes update tasks for specified resources. diff --git a/nailgun/nailgun/api/v1/handlers/orchestrator.py b/nailgun/nailgun/api/v1/handlers/orchestrator.py index bf2d66a0e3..c26a403f04 100644 --- a/nailgun/nailgun/api/v1/handlers/orchestrator.py +++ b/nailgun/nailgun/api/v1/handlers/orchestrator.py @@ -20,7 +20,9 @@ import six import web from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.cluster import ProvisionSelectedNodesValidator from nailgun.api.v1.validators.node import DeploySelectedNodesValidator from nailgun.api.v1.validators.node import NodeDeploymentValidator @@ -79,7 +81,9 @@ class DefaultOrchestratorInfo(NodesFilterMixin, BaseHandler): Need to redefine serializer variable """ - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized default data which will be passed to orchestrator @@ -110,7 +114,9 @@ class OrchestratorInfo(BaseHandler): """Method should override data which will be passed to orchestrator""" raise NotImplementedError('Please Implement this method') - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized data which will be passed to orchestrator @@ -120,7 +126,9 @@ class OrchestratorInfo(BaseHandler): cluster = self.get_object_or_404(objects.Cluster, cluster_id) return self.get_orchestrator_info(cluster) - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id): """:returns: JSONized data which will be passed to orchestrator @@ -136,7 +144,8 @@ class OrchestratorInfo(BaseHandler): .format(cluster_id)) return data - @content + @handle_errors + @validate def DELETE(self, cluster_id): """:returns: {} @@ -235,7 +244,8 @@ class SelectedNodesBase(NodesFilterMixin, BaseHandler): self.raise_task(task) - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -257,7 +267,8 @@ class ProvisionSelectedNodes(SelectedNodesBase): def get_default_nodes(self, cluster): return TaskHelper.nodes_to_provision(cluster) - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -303,7 +314,8 @@ class BaseDeploySelectedNodes(SelectedNodesBase): class DeploySelectedNodes(BaseDeploySelectedNodes, DryRunMixin): """Handler for deployment selected nodes.""" - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -324,7 +336,8 @@ class DeploySelectedNodesWithTasks(BaseDeploySelectedNodes, DryRunMixin): validator = NodeDeploymentValidator - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -353,6 +366,8 @@ class TaskDeployGraph(BaseHandler): validator = GraphSolverVisualizationValidator + @handle_errors + @validate def GET(self, cluster_id): """:returns: DOT representation of deployment graph. @@ -407,7 +422,9 @@ class SerializedTasksHandler(NodesFilterMixin, BaseHandler): return objects.Cluster.get_nodes_not_for_deletion(cluster).all() return TaskHelper.nodes_to_deploy(cluster) - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: serialized tasks in json format diff --git a/nailgun/nailgun/api/v1/handlers/plugin.py b/nailgun/nailgun/api/v1/handlers/plugin.py index 14c0348c34..f41f58905d 100644 --- a/nailgun/nailgun/api/v1/handlers/plugin.py +++ b/nailgun/nailgun/api/v1/handlers/plugin.py @@ -17,7 +17,8 @@ import six from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.handlers.deployment_graph import \ RelatedDeploymentGraphCollectionHandler from nailgun.api.v1.handlers.deployment_graph import \ @@ -39,7 +40,8 @@ class PluginCollectionHandler(base.CollectionHandler): collection = objects.PluginCollection validator = plugin.PluginValidator - @content + @handle_errors + @validate def POST(self): """:returns: JSONized REST object. @@ -59,7 +61,8 @@ class PluginSyncHandler(base.BaseHandler): validator = plugin.PluginSyncValidator - @content + @handle_errors + @validate def POST(self): """:returns: JSONized REST object. diff --git a/nailgun/nailgun/api/v1/handlers/plugin_link.py b/nailgun/nailgun/api/v1/handlers/plugin_link.py index a51c579485..2cd97d8cdd 100644 --- a/nailgun/nailgun/api/v1/handlers/plugin_link.py +++ b/nailgun/nailgun/api/v1/handlers/plugin_link.py @@ -15,7 +15,9 @@ # under the License. from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators import plugin_link from nailgun import errors from nailgun import objects @@ -36,6 +38,9 @@ class PluginLinkHandler(base.SingleHandler): "Plugin with id {0} not found".format(plugin_id) ) + @handle_errors + @validate + @serialize def GET(self, plugin_id, obj_id): """:returns: JSONized REST object. @@ -43,9 +48,11 @@ class PluginLinkHandler(base.SingleHandler): * 404 (dashboard entry not found in db) """ obj = self._get_plugin_link_object(plugin_id, obj_id) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate + @serialize def PUT(self, plugin_id, obj_id): """:returns: JSONized REST object. @@ -58,7 +65,7 @@ class PluginLinkHandler(base.SingleHandler): self.validator.validate_update, instance=obj) self.single.update(obj, data) - return self.single.to_json(obj) + return self.single.to_dict(obj) def PATCH(self, plugin_id, obj_id): """:returns: JSONized REST object. @@ -69,7 +76,8 @@ class PluginLinkHandler(base.SingleHandler): """ return self.PUT(plugin_id, obj_id) - @content + @handle_errors + @validate def DELETE(self, plugin_id, obj_id): """:returns: JSONized REST object. @@ -86,7 +94,8 @@ class PluginLinkCollectionHandler(base.CollectionHandler): collection = objects.PluginLinkCollection validator = plugin_link.PluginLinkValidator - @content + @handle_errors + @validate def GET(self, plugin_id): """:returns: Collection of JSONized PluginLink objects. @@ -94,11 +103,12 @@ class PluginLinkCollectionHandler(base.CollectionHandler): * 404 (plugin not found in db) """ self.get_object_or_404(objects.Plugin, plugin_id) - return self.collection.to_json( + return self.collection.to_list( self.collection.get_by_plugin_id(plugin_id) ) - @content + @handle_errors + @validate def POST(self, plugin_id): """:returns: JSONized REST object. diff --git a/nailgun/nailgun/api/v1/handlers/release.py b/nailgun/nailgun/api/v1/handlers/release.py index 94efdcc5f9..155b97e0e5 100644 --- a/nailgun/nailgun/api/v1/handlers/release.py +++ b/nailgun/nailgun/api/v1/handlers/release.py @@ -19,9 +19,11 @@ Handlers dealing with releases """ from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors from nailgun.api.v1.handlers.base import OrchestratorDeploymentTasksHandler +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.handlers.deployment_graph import \ RelatedDeploymentGraphCollectionHandler from nailgun.api.v1.handlers.deployment_graph import \ @@ -47,7 +49,9 @@ class ReleaseAttributesMetadataHandler(SingleHandler): single = Release validator = ReleaseAttributesMetadataValidator - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """:returns: JSONized Release attributes metadata. @@ -57,7 +61,9 @@ class ReleaseAttributesMetadataHandler(SingleHandler): release = self.get_object_or_404(self.single, obj_id) return release['attributes_metadata'] - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """:returns: JSONized Release attributes metadata. @@ -77,14 +83,16 @@ class ReleaseCollectionHandler(CollectionHandler): validator = ReleaseValidator collection = ReleaseCollection - @content + @handle_errors + @validate + @serialize def GET(self): """:returns: Sorted releases' collection in JSON format :http: * 200 (OK) """ q = sorted(self.collection.all(), reverse=True) - return self.collection.to_json(q) + return self.collection.to_list(q) class ReleaseNetworksHandler(SingleHandler): @@ -93,7 +101,9 @@ class ReleaseNetworksHandler(SingleHandler): single = Release validator = ReleaseNetworksValidator - @content + @handle_errors + @validate + @serialize def GET(self, obj_id): """Read release networks metadata @@ -105,7 +115,9 @@ class ReleaseNetworksHandler(SingleHandler): obj = self.get_object_or_404(self.single, obj_id) return obj['networks_metadata'] - @content + @handle_errors + @validate + @serialize def PUT(self, obj_id): """Updates release networks metadata diff --git a/nailgun/nailgun/api/v1/handlers/removed.py b/nailgun/nailgun/api/v1/handlers/removed.py index cadbbf15a9..cae367d5fe 100644 --- a/nailgun/nailgun/api/v1/handlers/removed.py +++ b/nailgun/nailgun/api/v1/handlers/removed.py @@ -17,7 +17,9 @@ Handlers for removed resources """ from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate class BaseRemovedInHandler(BaseHandler): @@ -27,7 +29,9 @@ class BaseRemovedInHandler(BaseHandler): def fuel_version(self): raise NotImplementedError - @content + @handle_errors + @validate + @serialize def GET(self): """A stub for the request. Always returns 410 with removed message. diff --git a/nailgun/nailgun/api/v1/handlers/role.py b/nailgun/nailgun/api/v1/handlers/role.py index 4e2cb4f9d0..382517776f 100644 --- a/nailgun/nailgun/api/v1/handlers/role.py +++ b/nailgun/nailgun/api/v1/handlers/role.py @@ -17,7 +17,9 @@ import six from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.role import RoleValidator from nailgun import errors from nailgun import objects @@ -37,7 +39,9 @@ class RoleHandler(base.SingleHandler): release=release_id, name=role_name)) return role - @content + @handle_errors + @validate + @serialize def GET(self, release_id, role_name): """Retrieve role @@ -48,7 +52,9 @@ class RoleHandler(base.SingleHandler): release = self.get_object_or_404(objects.Release, release_id) return RoleSerializer.serialize_from_release(release, role_name) - @content + @handle_errors + @validate + @serialize def PUT(self, release_id, role_name): """Update role @@ -85,7 +91,8 @@ class RoleCollectionHandler(base.CollectionHandler): validator = RoleValidator - @content + @handle_errors + @validate def POST(self, release_id): """Create role for release @@ -110,7 +117,9 @@ class RoleCollectionHandler(base.CollectionHandler): raise self.http( 201, RoleSerializer.serialize_from_release(release, role_name)) - @content + @handle_errors + @validate + @serialize def GET(self, release_id): release = self.get_object_or_404(objects.Release, release_id) role_names = six.iterkeys(release.roles_metadata) @@ -125,7 +134,9 @@ class ClusterRolesHandler(base.BaseHandler): if role_name not in available_roles: raise self.http(404, 'Role is not found for the cluster') - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id, role_name): """:returns: JSON-ed metadata for the role @@ -140,7 +151,9 @@ class ClusterRolesHandler(base.BaseHandler): class ClusterRolesCollectionHandler(base.BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: collection of JSON-ed cluster roles metadata diff --git a/nailgun/nailgun/api/v1/handlers/tasks.py b/nailgun/nailgun/api/v1/handlers/tasks.py index 695a015826..5268592e5f 100644 --- a/nailgun/nailgun/api/v1/handlers/tasks.py +++ b/nailgun/nailgun/api/v1/handlers/tasks.py @@ -18,7 +18,9 @@ import web from nailgun.api.v1.handlers.base import CollectionHandler from nailgun.api.v1.handlers.base import SingleHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.task import TaskValidator from nailgun import errors @@ -38,7 +40,8 @@ class TaskHandler(SingleHandler): single = objects.Task validator = TaskValidator - @content + @handle_errors + @validate def DELETE(self, obj_id): """:returns: Empty string @@ -68,7 +71,9 @@ class TaskCollectionHandler(CollectionHandler): collection = objects.TaskCollection validator = TaskValidator - @content + @handle_errors + @validate + @serialize def GET(self): """May receive cluster_id parameter to filter list of tasks @@ -79,8 +84,8 @@ class TaskCollectionHandler(CollectionHandler): cluster_id = web.input(cluster_id=None).cluster_id if cluster_id is not None: - return self.collection.to_json( + return self.collection.to_list( self.collection.get_by_cluster_id(cluster_id) ) else: - return self.collection.to_json(self.collection.all_not_deleted()) + return self.collection.to_list(self.collection.all_not_deleted()) diff --git a/nailgun/nailgun/api/v1/handlers/transactions.py b/nailgun/nailgun/api/v1/handlers/transactions.py index 2ebf223284..703fe17e1a 100644 --- a/nailgun/nailgun/api/v1/handlers/transactions.py +++ b/nailgun/nailgun/api/v1/handlers/transactions.py @@ -18,7 +18,9 @@ import web from nailgun.api.v1.handlers.base import CollectionHandler from nailgun.api.v1.handlers.tasks import TaskHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators.transaction import TransactionValidator from nailgun import errors @@ -42,7 +44,9 @@ class TransactionCollectionHandler(CollectionHandler): collection = objects.TransactionCollection validator = TransactionValidator - @content + @handle_errors + @validate + @serialize def GET(self): """May receive cluster_id parameter to filter list of tasks @@ -60,7 +64,7 @@ class TransactionCollectionHandler(CollectionHandler): except errors.InvalidData as exc: raise self.http(400, exc.message) - return self.collection.to_json( + return self.collection.to_list( self.collection.get_transactions( cluster_id=cluster_id, statuses=statuses, @@ -72,7 +76,9 @@ class BaseTransactionDataHandler(TransactionHandler): get_data = None - @content + @handle_errors + @validate + @serialize def GET(self, transaction_id): """:returns: Collection of JSONized DeploymentInfo objects. diff --git a/nailgun/nailgun/api/v1/handlers/version.py b/nailgun/nailgun/api/v1/handlers/version.py index 975678abc0..5b21a11a3a 100644 --- a/nailgun/nailgun/api/v1/handlers/version.py +++ b/nailgun/nailgun/api/v1/handlers/version.py @@ -19,14 +19,18 @@ Product info handlers """ from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.settings import settings class VersionHandler(BaseHandler): """Version info handler""" - @content + @handle_errors + @validate + @serialize def GET(self): """:returns: FUEL/FUELWeb commit SHA, release version. diff --git a/nailgun/nailgun/api/v1/handlers/vms.py b/nailgun/nailgun/api/v1/handlers/vms.py index 1022ce7b14..5a7d0551c0 100644 --- a/nailgun/nailgun/api/v1/handlers/vms.py +++ b/nailgun/nailgun/api/v1/handlers/vms.py @@ -20,7 +20,9 @@ import six import web from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.api.v1.validators import node as validators from nailgun.logger import logger @@ -72,7 +74,8 @@ class SpawnVmsHandler(BaseHandler): else: raise self.http(400, "No VMs to spawn") - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: JSONized Task object. @@ -93,7 +96,9 @@ class NodeVMsHandler(BaseHandler): validator = validators.NodeVMsValidator - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: JSONized node vms_conf. @@ -104,7 +109,9 @@ class NodeVMsHandler(BaseHandler): node_vms = node.vms_conf return {"vms_conf": node_vms} - @content + @handle_errors + @validate + @serialize def PUT(self, node_id): """:returns: JSONized node vms_conf. diff --git a/nailgun/nailgun/extensions/network_manager/handlers/network_configuration.py b/nailgun/nailgun/extensions/network_manager/handlers/network_configuration.py index afb257f69c..21b0c5bf2f 100644 --- a/nailgun/nailgun/extensions/network_manager/handlers/network_configuration.py +++ b/nailgun/nailgun/extensions/network_manager/handlers/network_configuration.py @@ -19,7 +19,9 @@ Handlers dealing with network configurations """ from ..task.manager import UpdateDnsmasqTaskManager from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.extensions.network_manager.objects.serializers.\ network_configuration import NeutronNetworkConfigurationSerializer @@ -71,7 +73,9 @@ class ProviderHandler(BaseHandler): cluster=cluster, networks_required=False) return cluster, data - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized network configuration for cluster. @@ -91,7 +95,9 @@ class ProviderHandler(BaseHandler): logger.exception('Serialization failed') raise - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id): """:returns: JSONized network configuration for cluster. @@ -153,7 +159,9 @@ class TemplateNetworkConfigurationHandler(BaseHandler): raise self.http(403, "Network template cannot be changed " "during deployment and after upgrade.") - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: network template for cluster (json format) @@ -163,7 +171,8 @@ class TemplateNetworkConfigurationHandler(BaseHandler): cluster = self.get_object_or_404(objects.Cluster, cluster_id) return cluster.network_config.configuration_template - @content + @handle_errors + @validate def PUT(self, cluster_id): """:returns: {} @@ -202,7 +211,8 @@ class NetworkConfigurationVerifyHandler(ProviderHandler): validator = NetworkConfigurationValidator - @content + @handle_errors + @validate def PUT(self, cluster_id): """:IMPORTANT: this method should be rewritten to be more RESTful @@ -252,7 +262,9 @@ class NeutronNetworkConfigurationVerifyHandler( class NetworkAttributesDeployedHandler(BaseHandler): """Cluster deployed network attributes handler""" - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """:returns: JSONized deployed Cluster network configuration. diff --git a/nailgun/nailgun/extensions/network_manager/handlers/network_group.py b/nailgun/nailgun/extensions/network_manager/handlers/network_group.py index 27189e8b0f..55c3700bfe 100644 --- a/nailgun/nailgun/extensions/network_manager/handlers/network_group.py +++ b/nailgun/nailgun/extensions/network_manager/handlers/network_group.py @@ -15,8 +15,10 @@ # under the License. from nailgun.api.v1.handlers.base import CollectionHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.api.v1.handlers.base import SingleHandler +from nailgun.api.v1.handlers.base import validate from nailgun.extensions.network_manager.validators.network import \ NetworkGroupValidator @@ -38,7 +40,9 @@ class NetworkGroupHandler(SingleHandler): validator = NetworkGroupValidator single = objects.NetworkGroup - @content + @handle_errors + @validate + @serialize def PUT(self, group_id): """:returns: JSONized Network Group object. @@ -57,9 +61,10 @@ class NetworkGroupHandler(SingleHandler): ) self.single.update(ng, data) - return self.single.to_json(ng) + return self.single.to_dict(ng) - @content + @handle_errors + @validate def DELETE(self, group_id): """Remove Network Group @@ -87,7 +92,8 @@ class NetworkGroupCollectionHandler(CollectionHandler): collection = objects.NetworkGroupCollection validator = NetworkGroupValidator - @content + @handle_errors + @validate def POST(self): """:returns: JSONized Network Group object. diff --git a/nailgun/nailgun/extensions/network_manager/handlers/nic.py b/nailgun/nailgun/extensions/network_manager/handlers/nic.py index 586c879efa..38028a62f7 100644 --- a/nailgun/nailgun/extensions/network_manager/handlers/nic.py +++ b/nailgun/nailgun/extensions/network_manager/handlers/nic.py @@ -21,7 +21,9 @@ Handlers dealing with nodes import web from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.extensions.network_manager.validators.network import \ NetAssignmentValidator @@ -42,7 +44,9 @@ class NodeNICsHandler(BaseHandler): validator = NetAssignmentValidator serializer = NodeInterfacesSerializer - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: Collection of JSONized Node interfaces. @@ -52,7 +56,9 @@ class NodeNICsHandler(BaseHandler): node = self.get_object_or_404(objects.Node, node_id) return map(self.render, node.interfaces) - @content + @handle_errors + @validate + @serialize def PUT(self, node_id): """:returns: Collection of JSONized Node objects. @@ -79,7 +85,9 @@ class NodeCollectionNICsHandler(BaseHandler): validator = NetAssignmentValidator serializer = NodeInterfacesSerializer - @content + @handle_errors + @validate + @serialize def PUT(self): """:returns: Collection of JSONized Node objects. @@ -107,7 +115,9 @@ class NodeCollectionNICsHandler(BaseHandler): class NodeNICsDefaultHandler(BaseHandler): """Node default network interfaces handler""" - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: Collection of default JSONized interfaces for node. @@ -129,7 +139,9 @@ class NodeCollectionNICsDefaultHandler(NodeNICsDefaultHandler): validator = NetAssignmentValidator - @content + @handle_errors + @validate + @serialize def GET(self): """May receive cluster_id parameter to filter list of nodes diff --git a/nailgun/nailgun/extensions/network_manager/handlers/vip.py b/nailgun/nailgun/extensions/network_manager/handlers/vip.py index 61b063b927..f46d38132e 100644 --- a/nailgun/nailgun/extensions/network_manager/handlers/vip.py +++ b/nailgun/nailgun/extensions/network_manager/handlers/vip.py @@ -18,7 +18,9 @@ import web from nailgun.api.v1.handlers import base -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.extensions.network_manager.validators import ip_addr from nailgun import objects @@ -51,7 +53,9 @@ class ClusterVIPHandler(base.SingleHandler): else: return obj - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id, ip_addr_id): """Get VIP record. @@ -68,9 +72,11 @@ class ClusterVIPHandler(base.SingleHandler): """ obj = self._get_vip_from_cluster_or_http_error( int(cluster_id), int(ip_addr_id)) - return self.single.to_json(obj) + return self.single.to_dict(obj) - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id, ip_addr_id): """Update VIP record. @@ -94,7 +100,7 @@ class ClusterVIPHandler(base.SingleHandler): existing_obj=obj ) self.single.update(obj, data) - return self.single.to_json(obj) + return self.single.to_dict(obj) def PATCH(self, cluster_id, ip_addr_id): """Update VIP record. @@ -126,7 +132,9 @@ class ClusterVIPCollectionHandler(base.CollectionHandler): collection = objects.IPAddrCollection validator = ip_addr.IPAddrValidator - @content + @handle_errors + @validate + @serialize def GET(self, cluster_id): """Get VIPs collection optionally filtered by network or network role. @@ -141,7 +149,7 @@ class ClusterVIPCollectionHandler(base.CollectionHandler): network_role = web.input(network_role=None).network_role self.get_object_or_404(objects.Cluster, int(cluster_id)) - return self.collection.to_json( + return self.collection.to_list( self.collection.get_vips_by_cluster_id( int(cluster_id), network_id, @@ -149,7 +157,8 @@ class ClusterVIPCollectionHandler(base.CollectionHandler): ) ) - @content + @handle_errors + @validate def POST(self, cluster_id): """Create (allocate) VIP @@ -174,7 +183,9 @@ class ClusterVIPCollectionHandler(base.CollectionHandler): raise self.http(200, self.collection.single.to_json(vip)) - @content + @handle_errors + @validate + @serialize def PUT(self, cluster_id): """Update VIPs collection. @@ -191,7 +202,7 @@ class ClusterVIPCollectionHandler(base.CollectionHandler): cluster_id=int(cluster_id) ) - return self.collection.to_json( + return self.collection.to_list( self.collection.update_vips(update_data) ) diff --git a/nailgun/nailgun/extensions/volume_manager/handlers/disks.py b/nailgun/nailgun/extensions/volume_manager/handlers/disks.py index 1a6597cecf..079be485c8 100644 --- a/nailgun/nailgun/extensions/volume_manager/handlers/disks.py +++ b/nailgun/nailgun/extensions/volume_manager/handlers/disks.py @@ -22,7 +22,9 @@ from ..manager import DisksFormatConvertor from ..manager import VolumeManager from ..validators.disks import NodeDisksValidator from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun import objects @@ -30,7 +32,9 @@ class NodeDisksHandler(BaseHandler): validator = NodeDisksValidator - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: JSONized node disks. @@ -43,7 +47,9 @@ class NodeDisksHandler(BaseHandler): node_volumes = VolumeObject.get_volumes(node) return DisksFormatConvertor.format_disks_to_simple(node_volumes) - @content + @handle_errors + @validate + @serialize def PUT(self, node_id): """:returns: JSONized node disks. @@ -75,7 +81,9 @@ class NodeDisksHandler(BaseHandler): class NodeDefaultsDisksHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: JSONized node disks. @@ -92,7 +100,9 @@ class NodeDefaultsDisksHandler(BaseHandler): class NodeVolumesInformationHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self, node_id): """:returns: JSONized volumes info for node. diff --git a/nailgun/nailgun/fake_keystone/handlers.py b/nailgun/nailgun/fake_keystone/handlers.py index 535dfd0f44..41e528cb81 100644 --- a/nailgun/nailgun/fake_keystone/handlers.py +++ b/nailgun/nailgun/fake_keystone/handlers.py @@ -15,7 +15,9 @@ # under the License. from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize +from nailgun.api.v1.handlers.base import validate from nailgun.fake_keystone import generate_token from nailgun.fake_keystone import validate_password_credentials from nailgun.fake_keystone import validate_token @@ -24,7 +26,9 @@ from nailgun.settings import settings class TokensHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def POST(self): data = self.checked_data() try: @@ -73,7 +77,9 @@ class TokensHandler(BaseHandler): class VersionHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self): keystone_href = 'http://{ip_addr}:{port}/keystone/v2.0/'.format( ip_addr=settings.LISTEN_ADDRESS, port=settings.LISTEN_PORT) @@ -107,7 +113,9 @@ class VersionHandler(BaseHandler): class ServicesHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self): return { 'OS-KSADM:services': [{'type': 'ostf', 'enabled': True, @@ -127,7 +135,9 @@ class ServicesHandler(BaseHandler): class EndpointsHandler(BaseHandler): - @content + @handle_errors + @validate + @serialize def GET(self): keystone_href = 'http://{ip_addr}:{port}/keystone/v2.0'.format( ip_addr=settings.AUTH['auth_host'], port=settings.LISTEN_PORT) diff --git a/nailgun/nailgun/test/integration/test_graph_related_handlers.py b/nailgun/nailgun/test/integration/test_graph_related_handlers.py index 218ad69415..f9aa4b430e 100644 --- a/nailgun/nailgun/test/integration/test_graph_related_handlers.py +++ b/nailgun/nailgun/test/integration/test_graph_related_handlers.py @@ -636,6 +636,8 @@ class TestStartEndTaskPassedCorrectly(BaseGraphTasksTests): def assert_passed_correctly(self, url, **kwargs): with mock.patch.object(GraphSolver, 'find_subgraph') as mfind_subgraph: + mfind_subgraph.return_value.node.values.return_value = {} + resp = self.app.get( url, params=kwargs, diff --git a/nailgun/nailgun/test/unit/test_handlers.py b/nailgun/nailgun/test/unit/test_handlers.py index 2f6e02add5..428ac1760a 100644 --- a/nailgun/nailgun/test/unit/test_handlers.py +++ b/nailgun/nailgun/test/unit/test_handlers.py @@ -19,10 +19,9 @@ import urllib import web -from mock import patch - from nailgun.api.v1.handlers.base import BaseHandler -from nailgun.api.v1.handlers.base import content +from nailgun.api.v1.handlers.base import handle_errors +from nailgun.api.v1.handlers.base import serialize from nailgun.test.base import BaseIntegrationTest from nailgun.utils import reverse @@ -97,15 +96,15 @@ class TestHandlers(BaseIntegrationTest): def test_content_decorator(self): - class FakeHandler(object): + class FakeHandler(BaseHandler): - @content + @serialize def GET(self): return {} - @content(["text/plain"]) + @serialize def POST(self): - return "Plain Text" + return {} web.ctx.headers = [] web.ctx.env = {"HTTP_ACCEPT": "text/html"} @@ -116,18 +115,7 @@ class TestHandlers(BaseIntegrationTest): fake_handler.GET ) - web.ctx.env = {"HTTP_ACCEPT": "application/json"} - - with patch("nailgun.api.v1.handlers.base.content_json") as cj: - fake_handler.GET() - self.assertEqual(cj.call_count, 1) - web.ctx.env = {"HTTP_ACCEPT": "*/*"} - - with patch("nailgun.api.v1.handlers.base.content_json") as cj: - fake_handler.GET() - self.assertEqual(cj.call_count, 1) - web.ctx.headers = [] fake_handler.GET() self.assertIn( @@ -136,7 +124,7 @@ class TestHandlers(BaseIntegrationTest): ) web.ctx.headers = [] - web.ctx.env = {"HTTP_ACCEPT": "text/plain"} + web.ctx.env = {"HTTP_ACCEPT": "application/json"} fake_handler.POST() self.assertIn( # we don't have plain/text serializer right now @@ -144,6 +132,20 @@ class TestHandlers(BaseIntegrationTest): web.ctx.headers ) + def test_invalid_handler_output(self): + + class FakeHandler(object): + + @handle_errors + @serialize + def GET(self): + return {set([1, 2, 3])} + + fake_handler = FakeHandler() + web.ctx.env = {"HTTP_ACCEPT": "*/*"} + web.ctx.headers = [] + self.assertRaises(web.HTTPError, fake_handler.GET) + def test_get_param_as_set(self): urls = ("/hello", "hello") @@ -160,3 +162,26 @@ class TestHandlers(BaseIntegrationTest): self.assertEqual(set(json.loads(resp.data)), set(['1', '4', '777', 'x'])) + + def check_get_requested_mime(self, headers, expected_mime): + urls = ("/hello", "hello") + + class hello(object): + def GET(self_inner): + web.header('Content-Type', 'text/plain') + return BaseHandler.get_requested_mime() + + app = web.application(urls, {'hello': hello}) + resp = app.request('/hello', headers=headers) + + self.assertEqual(resp.data, expected_mime) + + def test_get_requested_mime1(self): + self.check_get_requested_mime({'ACCEPT': 'text/html'}, 'text/html') + + def test_get_requested_mime2(self): + self.check_get_requested_mime( + {'ACCEPT': 'text/plain;q=0.7, text/html;level=1,'}, 'text/plain') + + def test_get_requested_default(self): + self.check_get_requested_mime({}, 'application/json')