diff options
-rw-r--r-- | multivimbroker/multivimbroker/forwarder/base.py | 65 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/forwarder/urls.py | 10 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/forwarder/views.py | 125 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/pub/exceptions.py | 23 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/pub/msapi/extsys.py | 4 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/pub/utils/restcall.py | 33 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/pub/utils/syscomm.py | 44 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/swagger/multivim.identity.swagger.json | 145 | ||||
-rw-r--r-- | multivimbroker/multivimbroker/swagger/views.py | 7 |
9 files changed, 377 insertions, 79 deletions
diff --git a/multivimbroker/multivimbroker/forwarder/base.py b/multivimbroker/multivimbroker/forwarder/base.py new file mode 100644 index 0000000..8f70c8b --- /dev/null +++ b/multivimbroker/multivimbroker/forwarder/base.py @@ -0,0 +1,65 @@ +# Copyright (c) 2017 VMware, 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. + +import logging + +from django.http import HttpResponse +from rest_framework import status + +import multivimbroker.pub.exceptions as exceptions +from multivimbroker.pub.utils.syscomm import getHeadersKeys +from multivimbroker.pub.utils.syscomm import getMultivimDriver +from multivimbroker.pub.utils.restcall import req_by_msb + + +logger = logging.getLogger(__name__) + +class BaseHandler(object): + + def _request(self,route_uri,method,body="",headers=None): + + try: + retcode, content, status_code, resp = \ + req_by_msb(route_uri, method, body, headers) + if retcode != 0: + # Execptions are handled within req_by_msb + logger.error("Status code is %s, detail is %s.", + status_code, content) + + except exceptions.NotFound as e: + return HttpResponse(str(e), status=status.HTTP_404_NOT_FOUND) + + except Exception as e: + content = e + status_code = status.HTTP_500_INTERNAL_SERVER_ERROR + logger.exception("exception: %s" % e) + + response = HttpResponse(content, status=status_code) + for k in getHeadersKeys(resp): + response[k] = resp[k] + return response + + + def send(self,vimid,full_path,body,method,headers=None): + + try: + url = getMultivimDriver(vimid,full_path=full_path) + + except exceptions.VimBrokerException as e: + logging.exception("vimbroker exception: %s"%e) + return HttpResponse(e.content,status=e.status_code) + except Exception as e: + logging.exception("unkown exception: %s" %e) + return HttpResponse(str(e),status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + return self._request(url,method,body=body,headers=headers) + diff --git a/multivimbroker/multivimbroker/forwarder/urls.py b/multivimbroker/multivimbroker/forwarder/urls.py index 5598c44..be20280 100644 --- a/multivimbroker/multivimbroker/forwarder/urls.py +++ b/multivimbroker/multivimbroker/forwarder/urls.py @@ -15,10 +15,16 @@ from django.conf.urls import url from rest_framework.urlpatterns import format_suffix_patterns -from . import views +from multivimbroker.forwarder.views import Forward +from multivimbroker.forwarder.views import Identity + urlpatterns = [ - url(r'^openoapi/multivim/v1/(?P<vimid>[0-9a-zA-Z_-]+)', views.route) + + url(r'^openoapi/multivim/v1/(?P<vimid>[0-9a-zA-Z_-]+)/identity/v3$',Identity.as_view()), + url(r'^openoapi/multivim/v1/(?P<vimid>[0-9a-zA-Z_-]+)/identity/v3/auth/tokens$',Identity.as_view()), + url(r'^openoapi/multivim/v1/(?P<vimid>[0-9a-zA-Z_-]+)', Forward.as_view()), + ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/multivimbroker/multivimbroker/forwarder/views.py b/multivimbroker/multivimbroker/forwarder/views.py index ca93b8e..82759e5 100644 --- a/multivimbroker/multivimbroker/forwarder/views.py +++ b/multivimbroker/multivimbroker/forwarder/views.py @@ -11,62 +11,69 @@ # 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. -import logging -import re - -from django.http import HttpResponse -from django.views.decorators.csrf import csrf_exempt - -from rest_framework import status - -from multivimbroker.pub.utils.restcall import req_by_msb -from multivimbroker.pub.msapi.extsys import get_vim_by_id - -logger = logging.getLogger(__name__) - - -@csrf_exempt -def route(request, vimid=''): - """ get vim info from vimid from local cache first - and then to ESR if cache miss - """ - content = "" - status_code = status.HTTP_200_OK - try: - vim = get_vim_by_id(vimid) - if vim["type"] and vim["version"]: - pass - - except Exception as e: - logger.error("get_vim_by_id, exception: %s" % e) - return HttpResponse("Not a valid VIM instance", status=status.HTTP_404_NOT_FOUND) - - try: - if vim and vim["type"] == "openstack": - if vim["version"] == "kilo": - multivimdriver = "multivim-kilo" - elif vim["version"] == "newton": - multivimdriver = "multivim-newton" - else: - # if vim type is openstack, use latest "newton" version as default - multivimdriver = "multivim-newton" - elif vim and vim["type"] == "vmware": - multivimdriver = "multivim-vio" - else: - logger.error("wrong vim id: %s, return from extsys:%s" % - vimid, vim) - return HttpResponse("Not support VIM type", status=status.HTTP_404_NOT_FOUND) - - route_uri = re.sub('multivim', multivimdriver, request.get_full_path()) - - retcode, content, status_code = \ - req_by_msb(route_uri, request.method, request.body) - if retcode != 0: - # Execptions are handled within req_by_msb - logger.error("Status code is %s, detail is %s.", - status_code, content) - except Exception as e: - content = e - status_code = status.HTTP_500_INTERNAL_SERVER_ERROR - logger.error("exception: %s" % e) - return HttpResponse(content, status=status_code) + +from rest_framework.views import APIView +from multivimbroker.forwarder.base import BaseHandler + +# +class BaseServer(BaseHandler,APIView): + + + def get(self,request,vimid): + raise NotImplementedError() + + def post(self,request,vimid): + raise NotImplementedError() + + def put(self,request,vimid): + raise NotImplementedError() + + def delete(self,request,vimid): + raise NotImplementedError() + + def head(self,request,vimid): + raise NotImplementedError() + + def patch(self,request,vimid): + raise NotImplementedError() + + +# vio proxy handler +class Identity(BaseServer): + + def get(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"GET") + + def post(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"POST") + + +# forward handler +class Forward(BaseServer): + + def get(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"GET") + + def post(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"POST",headers=None) + + def patch(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"PATCH",headers=None) + + def delete(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"DELETE",headers=None) + + def head(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"HEAD") + + def put(self,request,vimid): + + return self.send(vimid,request.get_full_path(),request.body,"PUT",headers=None) + diff --git a/multivimbroker/multivimbroker/pub/exceptions.py b/multivimbroker/multivimbroker/pub/exceptions.py index f11bb16..d2b2c80 100644 --- a/multivimbroker/multivimbroker/pub/exceptions.py +++ b/multivimbroker/multivimbroker/pub/exceptions.py @@ -10,5 +10,24 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -class VimBrokerException(Exception): - pass + +class BaseException(Exception): + + message = "Exception" + + def __init__(self, message=None, status_code="", content=""): + super(BaseException, self).__init__(message) + self.message = message or self.message + self.status_code = status_code + self.content = content + +class VimBrokerException(BaseException): + + message = "vim error" + + +class NotFound(BaseException): + + message = "not found error" + + diff --git a/multivimbroker/multivimbroker/pub/msapi/extsys.py b/multivimbroker/multivimbroker/pub/msapi/extsys.py index ac2fa13..1baa08a 100644 --- a/multivimbroker/multivimbroker/pub/msapi/extsys.py +++ b/multivimbroker/multivimbroker/pub/msapi/extsys.py @@ -23,7 +23,7 @@ def get_vims(): ret = req_by_msb(ESR_GET_VIM_URI, "GET") if ret[0] != 0: logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) - raise VimBrokerException("Failed to query VIMs from extsys.") + raise VimBrokerException(status_code=404 ,content="Failed to query VIMs from extsys.") return json.JSONDecoder().decode(ret[1]) @@ -31,6 +31,6 @@ def get_vim_by_id(vim_id): ret = req_by_msb("%s/%s" % (ESR_GET_VIM_URI, vim_id), "GET") if ret[0] != 0: logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) - raise VimBrokerException( + raise VimBrokerException(status_code=404,content= "Failed to query VIM with id (%s) from extsys." % vim_id) return json.JSONDecoder().decode(ret[1]) diff --git a/multivimbroker/multivimbroker/pub/utils/restcall.py b/multivimbroker/multivimbroker/pub/utils/restcall.py index 0b76bae..dc0b822 100644 --- a/multivimbroker/multivimbroker/pub/utils/restcall.py +++ b/multivimbroker/multivimbroker/pub/utils/restcall.py @@ -16,6 +16,7 @@ import urllib2 import uuid import httplib2 + from multivimbroker.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2 @@ -26,15 +27,22 @@ HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUES logger = logging.getLogger(__name__) -def call_req(base_url, user, passwd, auth_type, resource, method, content=''): +def call_req(base_url, user, passwd, auth_type, resource, method, content='',headers=None): callid = str(uuid.uuid1()) # logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s','%s')" % ( # callid, base_url, user, passwd, auth_type, resource, method, content)) ret = None resp_status = '' + resp = "" + full_url = "" + + try: full_url = combine_url(base_url, resource) - headers = {'content-type': 'application/json', 'accept': 'application/json'} + if headers == None: + headers = {} + headers['content-type']='application/json' + if user: headers['Authorization'] = 'Basic ' + ('%s:%s' % (user, passwd)).encode("base64") ca_certs = None @@ -42,42 +50,41 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''): http = httplib2.Http(ca_certs=ca_certs, disable_ssl_certificate_validation=(auth_type == rest_no_auth)) http.follow_all_redirects = True try: -# logger.debug("request=%s)" % full_url) + logger.debug("request=%s)" % full_url) resp, resp_content = http.request(full_url, method=method.upper(), body=content, headers=headers) resp_status, resp_body = resp['status'], resp_content.decode('UTF-8') -# logger.debug("[%s][%d]status=%s,resp_body=%s)" % (callid, retry_times, resp_status, resp_body)) + if resp_status in status_ok_list: - ret = [0, resp_body, resp_status] + ret = [0, resp_body, resp_status, resp] else: - ret = [1, resp_body, resp_status] + ret = [1, resp_body, resp_status, resp] break except Exception as ex: if 'httplib.ResponseNotReady' in str(sys.exc_info()): -# logger.debug("retry_times=%d", retry_times) logger.error(traceback.format_exc()) - ret = [1, "Unable to connect to %s" % full_url, resp_status] + ret = [1, "Unable to connect to %s" % full_url, resp_status, resp] continue raise ex except urllib2.URLError as err: - ret = [2, str(err), resp_status] + ret = [2, str(err), resp_status, resp] except Exception as ex: logger.error(traceback.format_exc()) logger.error("[%s]ret=%s" % (callid, str(sys.exc_info()))) res_info = str(sys.exc_info()) if 'httplib.ResponseNotReady' in res_info: res_info = "The URL[%s] request failed or is not responding." % full_url - ret = [3, res_info, resp_status] + ret = [3, res_info, resp_status, resp] except: logger.error(traceback.format_exc()) - ret = [4, str(sys.exc_info()), resp_status] + ret = [4, str(sys.exc_info()), resp_status, resp] # logger.debug("[%s]ret=%s" % (callid, str(ret))) return ret -def req_by_msb(resource, method, content=''): +def req_by_msb(resource, method, content='',headers=None): base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT) - return call_req(base_url, "", "", rest_no_auth, resource, method, content) + return call_req(base_url, "", "", rest_no_auth, resource, method, content,headers) def combine_url(base_url, resource): diff --git a/multivimbroker/multivimbroker/pub/utils/syscomm.py b/multivimbroker/multivimbroker/pub/utils/syscomm.py index 735129a..07606bf 100644 --- a/multivimbroker/multivimbroker/pub/utils/syscomm.py +++ b/multivimbroker/multivimbroker/pub/utils/syscomm.py @@ -10,7 +10,49 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import inspect +import re - +import multivimbroker.pub.exceptions as exceptions +from multivimbroker.pub.msapi.extsys import get_vim_by_id def fun_name(): return inspect.stack()[1][3] + + + + +# Which headers are hop-by-hop headers by default +HOP_BY_HOP = ['connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', 'transfer-encoding', 'upgrade'] +def getHeadersKeys(response): + hopbyhop = HOP_BY_HOP + hopbyhop.extend([x.strip() for x in response.get('connection', '').split(',')]) + return [header for header in response.keys() if header not in hopbyhop] + + + +def findMultivimDriver(vim=None): + + if vim and vim["type"] == "openstack": + if vim["version"] == "kilo": + multivimdriver = "multivim-kilo" + elif vim["version"] == "newton": + multivimdriver = "multivim-newton" + else: + # if vim type is openstack, use latest "newton" version as default + multivimdriver = "multivim-newton" + elif vim and vim["type"] == "vmware": + multivimdriver = "multivim-vio" + else: + raise exceptions.NotFound("Not support VIM type") + return multivimdriver + + + +def getMultivimDriver(vimid,full_path=""): + + multivim = "multivim" + vim = get_vim_by_id(vimid) + if vim["type"] and vim["version"]: + pass + + multivimdriver = findMultivimDriver(vim=vim) + return re.sub(multivim, multivimdriver, full_path) diff --git a/multivimbroker/multivimbroker/swagger/multivim.identity.swagger.json b/multivimbroker/multivimbroker/swagger/multivim.identity.swagger.json new file mode 100644 index 0000000..923924e --- /dev/null +++ b/multivimbroker/multivimbroker/swagger/multivim.identity.swagger.json @@ -0,0 +1,145 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "MultiVIM Service rest API" + }, + "basePath": "/openoapi/multivim/v1/", + "tags": [ + { + "name": "MultiVIM services" + } + ], + "paths": { + "/{vimid}/identity/v3": { + "get": { + "tags": [ + "vim identity" + ], + "summary": "query vim identity server ", + "description": "query vim identity server", + "operationId": "query_vim_identity", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vimid", + "in": "path", + "description": "vim instance id", + "required": true, + "type": "string" + } + + + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/IdentityInfo" + } + }, + "404": { + "description": "the vim id is wrong" + }, + "500": { + "description": "the vim tenants is not accessable" + } + } + } + }, + "/{vimid}/identity/v3/auth/tokens":{ + "post": { + "tags": [ + "vim identity" + ], + "summary": "get vim token ", + "description": "get vim token", + "operationId": "get_vim_token", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vimid", + "in": "path", + "description": "vim instance id", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "description": "authrication data", + "required": true, + "type": "string" + } + + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/TokenInfo" + } + }, + "404": { + "description": "the vim id is wrong" + }, + "500": { + "description": "the vim tenants is not accessable" + } + } + } + } + }, + "definitions": { + "IdentityInfo": { + "type": "object", + "required": [ + "href", + "id" + ], + "properties": { + "href": { + "type": "string", + "description": "keystone url" + }, + "id": { + "type": "string", + "description": "keystone version" + } + } + }, + "TokenInfo": { + "type": "object", + "required": [ + "value", + "endpoints" + ], + "properties": { + "value": { + "type": "string", + "description": "token uuid" + }, + "endpoints": { + "type": "string", + "description": "serivce endpoints" + } + } + } + } +} + + + + + + diff --git a/multivimbroker/multivimbroker/swagger/views.py b/multivimbroker/multivimbroker/swagger/views.py index 8e8a2bf..5fcfb53 100644 --- a/multivimbroker/multivimbroker/swagger/views.py +++ b/multivimbroker/multivimbroker/swagger/views.py @@ -83,5 +83,12 @@ class SwaggerJsonView(APIView): f.close() json_data["paths"].update(json_data_temp["paths"]) json_data["definitions"].update(json_data_temp["definitions"]) + # + json_file = os.path.join(os.path.dirname(__file__), 'multivim.identity.swagger.json') + f = open(json_file) + json_data_temp = json.JSONDecoder().decode(f.read()) + f.close() + json_data["paths"].update(json_data_temp["paths"]) + json_data["definitions"].update(json_data_temp["definitions"]) return Response(json_data) |