diff options
-rw-r--r-- | newton/newton/pub/exceptions.py | 6 | ||||
-rw-r--r-- | newton/newton/pub/msapi/extsys.py | 21 | ||||
-rw-r--r-- | newton/newton/pub/utils/restcall.py | 57 | ||||
-rw-r--r--[-rwxr-xr-x] | newton/newton/pub/utils/share_lock.py | 0 | ||||
-rw-r--r-- | newton/newton/requests/__init__.py | 13 | ||||
-rw-r--r-- | newton/newton/requests/tests/__init__.py | 13 | ||||
-rw-r--r-- | newton/newton/requests/tests/test_reqeust.py | 26 | ||||
-rw-r--r-- | newton/newton/requests/urls.py | 25 | ||||
-rw-r--r-- | newton/newton/requests/views.py | 151 | ||||
-rw-r--r-- | newton/newton/urls.py | 9 |
10 files changed, 295 insertions, 26 deletions
diff --git a/newton/newton/pub/exceptions.py b/newton/newton/pub/exceptions.py index dff87e8a..b750d0a8 100644 --- a/newton/newton/pub/exceptions.py +++ b/newton/newton/pub/exceptions.py @@ -11,4 +11,8 @@ class VimDriverNewtonException(Exception): - pass + def __init__(self, message, status_code="", content=""): + super(VimDriverNewtonException, self).__init__(message) + self.status_code = status_code + self.content = content + pass diff --git a/newton/newton/pub/msapi/extsys.py b/newton/newton/pub/msapi/extsys.py index 748d33a9..297d4716 100644 --- a/newton/newton/pub/msapi/extsys.py +++ b/newton/newton/pub/msapi/extsys.py @@ -19,16 +19,19 @@ logger = logging.getLogger(__name__) def get_vims(): - ret = req_by_msb("/openoapi/extsys/v1/vims", "GET") - if ret[0] != 0: - logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + retcode, content, status_code = \ + req_by_msb("/openoapi/extsys/v1/vims", "GET") + if retcode != 0: + logger.error("Status code is %s, detail is %s.", status_code, content) raise VimDriverNewtonException("Failed to query VIMs from extsys.") - return json.JSONDecoder().decode(ret[1]) + return json.JSONDecoder().decode(content) def get_vim_by_id(vim_id): - ret = req_by_msb("/openoapi/extsys/v1/vims/%s" % vim_id, "GET") - if ret[0] != 0: - logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) - raise VimDriverNewtonException("Failed to query VIM with id (%s) from extsys." % vim_id) - return json.JSONDecoder().decode(ret[1]) + retcode, content, status_code = \ + req_by_msb("/openoapi/extsys/v1/vims/%s" % vim_id, "GET") + if retcode != 0: + logger.error("Status code is %s, detail is %s.", status_code, content) + raise VimDriverNewtonException( + "Failed to query VIM with id (%s) from extsys." % vim_id) + return json.JSONDecoder().decode(content) diff --git a/newton/newton/pub/utils/restcall.py b/newton/newton/pub/utils/restcall.py index 6f9f9d8b..7f372982 100644 --- a/newton/newton/pub/utils/restcall.py +++ b/newton/newton/pub/utils/restcall.py @@ -19,14 +19,19 @@ import httplib2 from newton.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2 -HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '200', '201', '204', '202' -status_ok_list = [HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED] -HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '404', '403', '401', '400' +HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED \ + = '200', '201', '204', '202' +status_ok_list \ + = [HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED] +HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, \ + HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '404', '403', '401', '400' +MAX_RETRY_TIME = 3 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, extra_headers='', content=''): 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)) @@ -34,17 +39,31 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''): resp_status = '' try: full_url = combine_url(base_url, resource) - headers = {'content-type': 'application/json', 'accept': 'application/json'} + headers = { + 'content-type': 'application/json', + 'accept': 'application/json' + } + + if extra_headers: + headers.update(extra_headers) if user: - headers['Authorization'] = 'Basic ' + ('%s:%s' % (user, passwd)).encode("base64") + headers['Authorization'] = \ + 'Basic ' + ('%s:%s' % (user, passwd)).encode("base64") ca_certs = None - for retry_times in range(3): - http = httplib2.Http(ca_certs=ca_certs, disable_ssl_certificate_validation=(auth_type == rest_no_auth)) + for retry_times in range(MAX_RETRY_TIME): + http = httplib2.Http( + ca_certs=ca_certs, + disable_ssl_certificate_validation=(auth_type == rest_no_auth)) http.follow_all_redirects = True try: - 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)) + 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] else: @@ -64,7 +83,8 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''): 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 + res_info = ("The URL[%s] request failed or is not responding." % + full_url) ret = [3, res_info, resp_status] except: logger.error(traceback.format_exc()) @@ -76,11 +96,22 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''): def req_by_msb(resource, method, content=''): base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT) - return call_req(base_url, "", "", rest_no_auth, resource, method, content) + logger.debug("Networks--get::> %s" % "33") + return call_req(base_url, "", "", rest_no_auth, + resource, method, "", content) + + +def req_to_vim(base_url, resource, method, extra_headers='', content=''): + return call_req(base_url, "", "", rest_no_auth, + resource, method, extra_headers, content) def combine_url(base_url, resource): full_url = None + + if not resource: + return base_url + if base_url.endswith('/') and resource.startswith('/'): full_url = base_url[:-1] + resource elif base_url.endswith('/') and not resource.startswith('/'): diff --git a/newton/newton/pub/utils/share_lock.py b/newton/newton/pub/utils/share_lock.py index aba599a2..aba599a2 100755..100644 --- a/newton/newton/pub/utils/share_lock.py +++ b/newton/newton/pub/utils/share_lock.py diff --git a/newton/newton/requests/__init__.py b/newton/newton/requests/__init__.py new file mode 100644 index 00000000..48b6e44b --- /dev/null +++ b/newton/newton/requests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017 Wind River Systems, 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. diff --git a/newton/newton/requests/tests/__init__.py b/newton/newton/requests/tests/__init__.py new file mode 100644 index 00000000..48b6e44b --- /dev/null +++ b/newton/newton/requests/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2017 Wind River Systems, 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. diff --git a/newton/newton/requests/tests/test_reqeust.py b/newton/newton/requests/tests/test_reqeust.py new file mode 100644 index 00000000..bae2b84b --- /dev/null +++ b/newton/newton/requests/tests/test_reqeust.py @@ -0,0 +1,26 @@ +# Copyright (c) 2017 Wind River Systems, 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 django.test import TestCase + + +class TestNetworksRequest(TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def assert_true_result(self): + self.assertTrue(1) diff --git a/newton/newton/requests/urls.py b/newton/newton/requests/urls.py new file mode 100644 index 00000000..f832eec4 --- /dev/null +++ b/newton/newton/requests/urls.py @@ -0,0 +1,25 @@ +# Copyright (c) 2017 Wind River Systems, 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 django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns + +from . import views + +urlpatterns = [ + url(r'^networks(/(?P<networkid>[0-9a-zA-Z_-]+))?', views.Networks.as_view()), + url(r'^subnets/(?P<subnetid>[0-9a-zA-Z_-]+)', views.Subnets.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/newton/newton/requests/views.py b/newton/newton/requests/views.py new file mode 100644 index 00000000..0333f86c --- /dev/null +++ b/newton/newton/requests/views.py @@ -0,0 +1,151 @@ +# Copyright (c) 2017 Wind River Systems, 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. +import logging +import json +from json import JSONEncoder + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from newton.pub.utils.restcall import req_to_vim +from newton.pub.exceptions import VimDriverNewtonException +from newton.pub.msapi.extsys import get_vim_by_id + +logger = logging.getLogger(__name__) + + +class VimDriverUtils(object): + @staticmethod + def get_vim_info(vimid): + # get vim info from local cache firstly + # if cache miss, get it from ESR service + vim = get_vim_by_id(vimid) + return vim + + @staticmethod + def relay_request_to_vim_service(vimid, tenantid, request, service_type, + req_resouce, req_content=""): + """ + if there is no token cache, do auth + if there is, use token directly + if response is 'need auth', do auth + get the use extractor to get token + and service rul and then do request + """ + vim = VimDriverUtils.get_vim_info(vimid) + auth_resouce = "/tokens" + method = "POST" + headers = "" + r_content_dict = { + "auth": { + "tenantName": vim["tenant"], + "passwordCredentials": { + "username": vim["userName"], + "password": vim["password"] + } + } + } + r_content = JSONEncoder().encode(r_content_dict) + retcode, content, status_code = \ + req_to_vim(vim["url"], auth_resouce, method, headers, r_content) + if retcode != 0: + logger.error("Status code is %s, detail is %s.", + status_code, content) + raise VimDriverNewtonException("Fail to authorize", + status_code, content) + else: + # extract token id and issue the get request + json_content = None + auth_resp = json.JSONDecoder().decode(content) + tokenid, svcurl = VimDriverUtils.extractor(auth_resp, service_type) + method = request.method + headers = {'X-Auth-Token': tokenid} + retcode, content, status_code = \ + req_to_vim(svcurl, req_resouce, method, headers, req_content) + if retcode != 0: + logger.error("Status code is %s, detail is %s.", + status_code, content) + raise VimDriverNewtonException("Fail to complte request", + status_code, content) + else: + json_content = json.JSONDecoder().decode(content) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + json_content.update(vim_dict) + return status_code, json_content + + @staticmethod + def extractor(resp_data, service_type): + try: + tokenid = resp_data["access"]["token"]["id"] + sc = resp_data["access"]["serviceCatalog"] + service = [svc for svc in sc if svc["type"] == service_type] + return tokenid, service[0]["endpoints"][0]["publicURL"] + except Exception: + raise Exception( + "There is no valid %s token or service info" % service_type) + + +class Networks(APIView): + SERVICE = "network" + keys_map_resp = [ + ("provider:segmentation_id", "segmentationId"), + ("provider:physical_network", "physicalNetwork"), + ("router:external", "routerExternal"), + ("provider:network_type", "networkType"), + ("vlan_transparent", "vlanTransparent"), + ] + + def get(self, request, vimid="", tenantid="", networkid=""): + logger.debug("Networks--get::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "v2.0/networks" + full_path = request.get_full_path() + if '?' in full_path: + _, query = request.get_full_path().split('?') + req_resouce += "?%s" % query + status_code, content = VimDriverUtils.relay_request_to_vim_service( + vimid, tenantid, request, self.SERVICE, req_resouce) + + logger.debug("response content after11: %s" % content) + # convert the key naming in networks + for network in content["networks"]: + for k in self.keys_map_resp: + v = network.pop(k[0], None) + if v: + network[k[1]] = v + logger.debug("response content after: %s" % content) + return Response(data=content, status=status_code) + except VimDriverNewtonException as e: + return Response(data=e.content, status=e.status_code) + except Exception as e: + return Response(data={'error': e}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def post(self, request, tenantid=""): + logger.debug("Networks--post::> %s" % request.data) + pass + + def delete(self, request): + logger.debug("Networks--delete::> %s" % request.data) + pass + + +class Subnets(APIView): + pass diff --git a/newton/newton/urls.py b/newton/newton/urls.py index 94ca0bc4..ad2979ff 100644 --- a/newton/newton/urls.py +++ b/newton/newton/urls.py @@ -10,16 +10,19 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from django.conf.urls import include, url -from newton.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM +from newton.pub.config.config \ + import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM urlpatterns = [ url(r'^', include('newton.swagger.urls')), url(r'^', include('newton.samples.urls')), + url(r'^openoapi/multivim-newton/v1/(?P<vimid>[0-9a-zA-Z_-]+)/' + '(?P<tenantid>[0-9a-zA-Z_-]+)/', include('newton.requests.urls')), ] -#url(r'^', include('newton.forward.urls')), # regist to MSB when startup if REG_TO_MSB_WHEN_START: import json from newton.pub.utils.restcall import req_by_msb - req_by_msb(REG_TO_MSB_REG_URL, "POST", json.JSONEncoder().encode(REG_TO_MSB_REG_PARAM)) + req_by_msb(REG_TO_MSB_REG_URL, "POST", + json.JSONEncoder().encode(REG_TO_MSB_REG_PARAM)) |