diff --git a/valence/api/route.py b/valence/api/route.py index 33490e7..290eb3c 100644 --- a/valence/api/route.py +++ b/valence/api/route.py @@ -27,15 +27,32 @@ from valence.api.v1.storages import StoragesList as v1StoragesList from valence.api.v1.systems import Systems as v1Systems from valence.api.v1.systems import SystemsList as v1SystemsList from valence.api.v1.version import V1 +from valence.common import exception app = flaskapp.get_app() cors = CORS(app) -api = Api(app) + + +class ValenceService(Api): + """Overriding Flask Restful Error handler""" + + def handle_error(self, error): + + if issubclass(error.__class__, exception.ValenceError): + return self.make_response(error.as_dict(), error.status) + elif hasattr(error, 'status'): + return self.make_response(exception.httpexception(error), + error.code) + else: + return self.make_response(exception.generalexception(error, 500), + 500) + +api = ValenceService(app) + """API V1.0 Operations""" - -# API Root operation +# Root Operations api.add_resource(Root, '/', endpoint='root') # V1 Root operations diff --git a/valence/common/exception.py b/valence/common/exception.py new file mode 100644 index 0000000..a7ddb73 --- /dev/null +++ b/valence/common/exception.py @@ -0,0 +1,113 @@ +# Copyright (c) 2016 Intel, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from valence.api import base +from valence.api import types + + +class ValenceError(Exception, base.APIBase): + """Valence Error representation. + + As per openstack Error Schema + http://specs.openstack.org/openstack/api-wg/guidelines/errors.html + + """ + + fields = { + 'request_id': { + 'validate': types.Text.validate + }, + 'code': { + 'validate': types.Text.validate + }, + 'status': { + 'validate': types.Integer.validate + }, + 'title': { + 'validate': types.Text.validate + }, + 'detail': { + 'validate': types.Text.validate + } + } + + +class ValenceConfirmation(base.APIBase): + """Valence Confirmation Message representation. + + Whenever confirmation response needs to send back to client + for successfull operation + + """ + + fields = { + 'request_id': { + 'validate': types.Text.validate + }, + 'code': { + 'validate': types.Text.validate + }, + 'detail': { + 'validate': types.Text.validate + } + } + + +class RedfishException(ValenceError): + + def __init__(self, responsejson, status_code=400): + Exception.__init__(self) + data = responsejson['error'] + self.request_id = "00000000-0000-0000-0000-000000000000" + self.code = data['code'] + self.status = status_code + self.title = data['message'] + message_detail = " ".join( + [i['Message'] + for i in data['@Message.ExtendedInfo']]) + self.detail = message_detail + + +class NotFound(Exception): + status = 404 + + +def error(requestid, error_code, http_status, + error_title, error_detail): + # responseobj - the response object of Requests framework + err_obj = ValenceError() + err_obj.request_id = requestid + err_obj.code = error_code + err_obj.status = http_status + err_obj.title = error_title + err_obj.detail = error_detail + return err_obj.as_dict() + + +def httpexception(e): + return error("", type(e).__name__, e.code, type(e).__name__, str(e)) + + +def generalexception(e, errorcode): + return error("", type(e).__name__, errorcode, type(e).__name__, str(e)) + + +def confirmation(requestid, confirm_code, confirm_detail): + # responseobj - the response object of Requests framework + confirm_obj = ValenceConfirmation() + confirm_obj.request_id = requestid + confirm_obj.code = confirm_code + confirm_obj.detail = confirm_detail + return confirm_obj.as_dict() diff --git a/valence/redfish/redfish.py b/valence/redfish/redfish.py index f95ffce..e6570df 100644 --- a/valence/redfish/redfish.py +++ b/valence/redfish/redfish.py @@ -20,6 +20,7 @@ import os import requests from requests.auth import HTTPBasicAuth +from valence.common import exception from valence.common import utils from valence import config as cfg from valence.redfish import tree @@ -256,7 +257,10 @@ def get_systembyid(systemid): def get_nodebyid(nodeid): - return nodes_list({"Id": nodeid}) + node = nodes_list({"Id": nodeid}) + if not node: + raise exception.NotFound() + return node[0] def build_hierarchy_tree(): @@ -282,21 +286,24 @@ def compose_node(data): compose_url = nodes_url + "/Actions/Allocate" headers = {'Content-type': 'application/json'} criteria = data["criteria"] - if not criteria: - resp = send_request(compose_url, "POST", headers=headers) + resp = send_request(compose_url, "POST", json=criteria, headers=headers) + if resp.status_code == 201: + composednode = resp.headers['Location'] + return {"node": composednode} else: - resp = send_request(compose_url, "POST", json=criteria, - headers=headers) - - composed_node = resp.headers['Location'] - return {"node": composed_node} + raise exception.RedfishException(resp.json(), + status_code=resp.status_code) def delete_composednode(nodeid): nodes_url = get_base_resource_url("Nodes") delete_url = nodes_url + str(nodeid) resp = send_request(delete_url, "DELETE") - return resp + if resp.status_code == 204: + return exception.confirmation("", "DELETED"), resp.status_code + else: + raise exception.RedfishException(resp.json(), + status_code=resp.status_code) def nodes_list(filters={}):