diff options
23 files changed, 2011 insertions, 73 deletions
diff --git a/kilo/kilo/pub/exceptions.py b/kilo/kilo/pub/exceptions.py index 7a9ee4e5..39a3bc89 100644 --- a/kilo/kilo/pub/exceptions.py +++ b/kilo/kilo/pub/exceptions.py @@ -11,4 +11,8 @@ class VimDriverKiloException(Exception): + def __init__(self, message, status_code="", content=""): + super(VimDriverKiloException, self).__init__(message) + self.status_code = status_code + self.content = content pass diff --git a/kilo/kilo/pub/msapi/extsys.py b/kilo/kilo/pub/msapi/extsys.py index 4b4fd057..035f949d 100644 --- a/kilo/kilo/pub/msapi/extsys.py +++ b/kilo/kilo/pub/msapi/extsys.py @@ -12,6 +12,7 @@ import json import logging +from rest_framework import status from kilo.pub.exceptions import VimDriverKiloException from kilo.pub.utils.restcall import req_by_msb @@ -19,16 +20,20 @@ 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]) - raise VimDrvierKiloException("Failed to query VIMs from extsys.") - return json.JSONDecoder().decode(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 VimDriverKiloException("Failed to query VIMs from extsys.") + 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 VimDrvierKiloException("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 VimDriverKiloException( + "Failed to query VIM with id (%s) from extsys." % vim_id, + status.HTTP_404_NOT_FOUND, content) + return json.JSONDecoder().decode(content) diff --git a/kilo/kilo/pub/utils/restcall.py b/kilo/kilo/pub/utils/restcall.py index 4b96d357..62824cc4 100644 --- a/kilo/kilo/pub/utils/restcall.py +++ b/kilo/kilo/pub/utils/restcall.py @@ -14,73 +14,96 @@ import traceback import logging import urllib2 import uuid +import httplib import httplib2 +from rest_framework import status + from kilo.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' +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)) +# 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_status = None 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: ret = [1, resp_body, resp_status] 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] - continue - raise ex + except httplib.ResponseNotReady: +# logger.debug("retry_times=%d", retry_times) + ret = [1, "Unable to connect to %s" % full_url, resp_status] + continue except urllib2.URLError as err: ret = [2, str(err), resp_status] - except Exception as ex: + except Exception: 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] - except: - logger.error(traceback.format_exc()) - ret = [4, str(sys.exc_info()), resp_status] + if not resp_status: + resp_status = status.HTTP_500_INTERNAL_SERVER_ERROR + ret = [3, str(sys.exc_info()), resp_status] - logger.debug("[%s]ret=%s" % (callid, str(ret))) +# logger.debug("[%s]ret=%s" % (callid, str(ret))) return ret 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("request--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('/'): @@ -90,3 +113,4 @@ def combine_url(base_url, resource): else: full_url = base_url + '/' + resource return full_url + diff --git a/kilo/kilo/requests/__init__.py b/kilo/kilo/requests/__init__.py new file mode 100644 index 00000000..48b6e44b --- /dev/null +++ b/kilo/kilo/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/kilo/kilo/requests/tests/__init__.py b/kilo/kilo/requests/tests/__init__.py new file mode 100644 index 00000000..48b6e44b --- /dev/null +++ b/kilo/kilo/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/kilo/kilo/requests/tests/test_reqeust.py b/kilo/kilo/requests/tests/test_reqeust.py new file mode 100644 index 00000000..bae2b84b --- /dev/null +++ b/kilo/kilo/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/kilo/kilo/requests/urls.py b/kilo/kilo/requests/urls.py new file mode 100644 index 00000000..77045340 --- /dev/null +++ b/kilo/kilo/requests/urls.py @@ -0,0 +1,47 @@ +# 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 views import network +from views import subnet +from views import image +from views import volume +from views import server +from views import vport +from views import limits +from views import hosts +from views import flavor + +urlpatterns = [ + url(r'^networks(/(?P<networkid>[0-9a-zA-Z_-]+))?', + network.Networks.as_view()), + url(r'^subnets(/(?P<subnetid>[0-9a-zA-Z_-]+))?', + subnet.Subnets.as_view()), + url(r'^images(/(?P<imageid>[0-9a-zA-Z_-]+))?', + image.Images.as_view()), + url(r'^volumes(/(?P<volumeid>[0-9a-zA-Z_-]+))?', + volume.Volumes.as_view()), + url(r'^servers(/(?P<serverid>[0-9a-zA-Z_-]+))?', + server.Servers.as_view()), + url(r'^ports(/(?P<portid>[0-9a-zA-Z_-]+))?', + vport.Vports.as_view()), + url(r'^flavors(/(?P<flavorid>[0-9a-zA-Z_-]+))?', + flavor.Flavors.as_view()), + url(r'^limits$', limits.Limits.as_view()), + url(r'^hosts(/(?P<hostname>[0-9a-zA-Z_-]+))?', hosts.Hosts.as_view()), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/kilo/kilo/requests/views/__init__.py b/kilo/kilo/requests/views/__init__.py new file mode 100644 index 00000000..802f3fba --- /dev/null +++ b/kilo/kilo/requests/views/__init__.py @@ -0,0 +1,10 @@ +# 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. diff --git a/kilo/kilo/requests/views/flavor.py b/kilo/kilo/requests/views/flavor.py new file mode 100644 index 00000000..2e2194bd --- /dev/null +++ b/kilo/kilo/requests/views/flavor.py @@ -0,0 +1,357 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Flavors(APIView): + service = {'service_type': 'compute', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("project_id", "tenantId"), + ("ram", "memory"), + ("vcpus", "vcpu"), + ("OS-FLV-EXT-DATA:ephemeral", "ephemeral"), + ("os-flavor-access:is_public", "isPublic"), + ("extra_specs", "extraSpecs"), + ] + + def convertExtraSpecs(self, extraSpecs, extra_specs, reverse=False): + if reverse == False: + #from extraSpecs to extra_specs + for spec in extraSpecs: + extra_specs[spec['keyName']] = spec['value'] + else: + for k,v in extra_specs.items(): + spec={} + spec['keyName']=k + spec['value']=v + extraSpecs.append(spec) + pass + + def get(self, request, vimid="", tenantid="", flavorid=""): + logger.debug("Flavors--get::> %s" % request.data) + try: + # prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = self.get_flavor(sess, request, flavorid) + content = resp.json() + + if flavorid: + flavor = content.pop("flavor", None) + extraResp = self.get_flavor_extra_specs(sess, flavor["id"]) + extraContent = extraResp.json() + if extraContent["extra_specs"]: + extraSpecs = [] + self.convertExtraSpecs(extraSpecs, extraContent["extra_specs"], True) + flavor["extraSpecs"] = extraSpecs + VimDriverUtils.replace_key_by_mapping(flavor, + self.keys_mapping) + content.update(flavor) + + else: + wanted = None + #check if query contains name="flavorname" + if query: + for queryone in query.split('&'): + k,v = queryone.split('=') + if k == "name": + wanted = v + break + pass + + if wanted: + oldFlavors = content.pop("flavors", None) + content["flavors"] = [] + for flavor in oldFlavors: + if wanted == flavor["name"]: + content["flavors"].append(flavor) + pass + + + #iterate each flavor to get extra_specs + for flavor in content["flavors"]: + extraResp = self.get_flavor_extra_specs(sess, flavor["id"]) + extraContent = extraResp.json() + if extraContent["extra_specs"]: + extraSpecs = [] + self.convertExtraSpecs(extraSpecs, extraContent["extra_specs"], True) + flavor["extraSpecs"] = extraSpecs + VimDriverUtils.replace_key_by_mapping(flavor, + self.keys_mapping) + + #add extra keys + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + + return Response(data=content, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def get_flavor_extra_specs(self, sess, flavorid): + if not flavorid: + return {} + else: + logger.debug("Flavors--get_extra_specs::> %s" % flavorid) + # prepare request resource to vim instance + req_resouce = "/flavors/%s/os-extra_specs" % flavorid + + resp = sess.get(req_resouce, endpoint_filter=self.service) + return resp + pass + + def get_flavor(self, sess, request, flavorid=""): + logger.debug("Flavors--get basic") + if not sess: + return {} + else: + # prepare request resource to vim instance + req_resouce = "/flavors" + if flavorid: + req_resouce += "/%s" % flavorid + else: + req_resouce += "/detail" + + query = VimDriverUtils.get_query_part(request) + if query: + req_resouce += "?%s" % query + + resp = sess.get(req_resouce, endpoint_filter=self.service) + return resp + pass + + + def post(self, request, vimid="", tenantid="", flavorid=""): + logger.debug("Flavors--post::> %s" % request.data) + sess = None + resp = None + resp_body = None + try: + # prepare request resource to vim instance + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + + #check if the flavor is already created: name or id + tmpresp = self.get_flavor(sess, request) + content = tmpresp.json() + #iterate each flavor to get extra_specs + existed = False + for flavor in content["flavors"]: + if flavor["name"] == request.data["name"]: + existed = True + break + elif hasattr(request.data, "id") and flavor["id"] == request.data["id"]: + existed = True + break + pass + + if existed == True: + extraResp = self.get_flavor_extra_specs(sess, flavor["id"]) + extraContent = extraResp.json() + if extraContent["extra_specs"]: + extraSpecs = [] + self.convertExtraSpecs(extraSpecs, extraContent["extra_specs"], True) + flavor["extraSpecs"] = extraSpecs + VimDriverUtils.replace_key_by_mapping(flavor, + self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 0, + } + flavor.update(vim_dict) + return Response(data=flavor, status=tmpresp.status_code) + + extraSpecs = request.data.pop("extraSpecs", None) + #create flavor first + resp = self.create_flavor(sess, request) + if resp.status_code == 200: + resp_body = resp.json()["flavor"] + else: + return resp + + + flavorid = resp_body['id'] + if extraSpecs: + extra_specs={} + self.convertExtraSpecs(extraSpecs, extra_specs, False) +# logger.debug("extraSpecs:%s" % extraSpecs) +# logger.debug("extra_specs:%s" % extra_specs) + extraResp = self.create_flavor_extra_specs(sess, extra_specs, flavorid) + if extraResp.status_code == 200: + #combine the response body and return + tmpSpecs = [] + tmpRespBody = extraResp.json() + self.convertExtraSpecs(tmpSpecs, tmpRespBody['extra_specs'], True) + + resp_body.update({"extraSpecs":tmpSpecs}) + else: + #rollback + delete_flavor(self, request, vimid, tenantid, flavorid) + return extraResp + + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + if sess and resp and resp.status_code == 200: + self.delete_flavor(sess, flavorid) + + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + if sess and resp and resp.status_code == 200: + self.delete_flavor(sess, flavorid) + + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + + def create_flavor(self, sess, request): + logger.debug("Flavors--create::> %s" % request.data) + # prepare request resource to vim instance + req_resouce = "/flavors" + + flavor = request.data + + VimDriverUtils.replace_key_by_mapping(flavor, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"flavor": flavor}) +# logger.debug("SBI:%s" % req_body) + return sess.post(req_resouce, data=req_body, + endpoint_filter=self.service, headers={"Content-Type": "application/json", + "Accept": "application/json" }) + pass + + + + def create_flavor_extra_specs(self, sess, extraspecs, flavorid): + logger.debug("Flavors extra_specs--post::> %s" % extraspecs) + # prepare request resource to vim instance + req_resouce = "/flavors" + if flavorid: + req_resouce += "/%s/os-extra_specs" % flavorid + else: + raise VimDriverKiloException(message="VIM newton exception", + content="internal bug in creating flavor extra specs", + status_code=500) + + req_body = json.JSONEncoder().encode({"extra_specs": extraspecs}) + + return sess.post(req_resouce, data=req_body, + endpoint_filter=self.service, headers={"Content-Type": "application/json", + "Accept": "application/json" }) + pass + + + + + + def delete(self, request, vimid="", tenantid="", flavorid=""): + logger.debug("Flavors--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + + #delete extra specs one by one + resp = self.delete_flavor_extra_specs(sess, flavorid) + + #delete flavor + resp = self.delete_flavor(sess, flavorid) + + #return results + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + + def delete_flavor_extra_specs(self, sess, flavorid): + logger.debug("Flavors--delete extra::> %s" % flavorid) + + #delete extra specs one by one + resp = self.get_flavor_extra_specs(sess, flavorid) + extra_specs = resp.json() + if extra_specs and extra_specs["extra_specs"]: + for k, _ in extra_specs["extra_specs"].items(): + self.delete_flavor_one_extra_spec(sess, flavorid, k) + return resp + pass + + def delete_flavor_one_extra_spec(self, sess, flavorid, extra_spec_key): + logger.debug("Flavors--delete 1 extra::> %s" % extra_spec_key) + # prepare request resource to vim instance + req_resouce = "/flavors" + if flavorid and extra_spec_key: + req_resouce += "/%s" % flavorid + req_resouce += "/os-extra_specs/%s" % extra_spec_key + else: + raise VimDriverKiloException(message="VIM newton exception", + content="internal bug in deleting flavor extra specs: %s" % extra_spec_key, + status_code=500) + + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return resp + pass + + + def delete_flavor(self, sess, flavorid): + logger.debug("Flavors--delete basic::> %s" % flavorid) + # prepare request resource to vim instance + req_resouce = "/flavors" + if flavorid: + req_resouce += "/%s" % flavorid + else: + raise VimDriverKiloException(message="VIM newton exception", + content="internal bug in deleting flavor", + status_code=500) + + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return resp + pass + diff --git a/kilo/kilo/requests/views/hosts.py b/kilo/kilo/requests/views/hosts.py new file mode 100644 index 00000000..b9d0ed1b --- /dev/null +++ b/kilo/kilo/requests/views/hosts.py @@ -0,0 +1,81 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Hosts(APIView): + service = {'service_type': 'compute', + 'interface': 'public', + 'region_name': 'RegionOne'} + + hosts_keys_mapping = [ + ("host_name", "name"), + ] + host_keys_mapping = [ + ("host", "name"), + ] + + def get(self, request, vimid="", tenantid="", hostname=""): + logger.debug("Hosts--get::> %s" % request.data) + try: + #prepare request resource to vim instance + req_resouce = "/os-hosts" + if hostname: + req_resouce += "/%s" % hostname + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + + if not hostname: + # convert the key naming in hosts + for host in content["hosts"]: + VimDriverUtils.replace_key_by_mapping(host, + self.hosts_keys_mapping) + else: + #restructure host data model + old_host = content["host"] + content["host"] = [] + # convert the key naming in resources + for res in old_host: + VimDriverUtils.replace_key_by_mapping(res['resource'], + self.host_keys_mapping) + content["host"].append(res['resource']) + + return Response(data=content, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + diff --git a/kilo/kilo/requests/views/image.py b/kilo/kilo/requests/views/image.py new file mode 100644 index 00000000..339c73e0 --- /dev/null +++ b/kilo/kilo/requests/views/image.py @@ -0,0 +1,225 @@ +# 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 +import urllib2 +import threading + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + +running_threads = {} +running_thread_lock = threading.Lock() + +class imageThread (threading.Thread): + service = {'service_type': 'image', + 'interface': 'public', + 'region_name': 'RegionOne'} + def __init__(self, vimid, tenantid, imageid, imagefd): + threading.Thread.__init__(self) + self.vimid = vimid + self.tenantid = tenantid + self.imageid = imageid + self.imagefd = imagefd + + def run(self): + logger.debug("start imagethread %s, %s, %s" % (self.vimid, self.tenantid, self.imageid)) + self.transfer_image(self.vimid, self.tenantid, self.imageid, self.imagefd) + logger.debug("stop imagethread %s, %s, %s" % (self.vimid, self.tenantid, self.imageid)) + running_thread_lock.acquire() + running_threads.pop(self.imageid) + running_thread_lock.release() + + def transfer_image(self, vimid, tenantid, imageid, imagefd): + logger.debug("Images--transfer_image::> %s" % (imageid)) + try: + # prepare request resource to vim instance + req_resouce = "v2/images/%s/file" % imageid + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + + #open imageurl + resp = sess.put(req_resouce, endpoint_filter=self.service, data=imagefd.read(), + headers={"Content-Type": "application/octet-stream", + "Accept": ""}) + + logger.debug("response status code of transfer_image %s" % resp.status_code) + return None + except Exception as e: + logger.debug("Failed to transfer_image:%s" % str(e)) + return None + pass + +class Images(APIView): + service = {'service_type': 'image', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("project_id", "tenantId"), + ("disk_format", "imageType"), + ("container_format", "containerFormat") + ] + + def get(self, request, vimid="", tenantid="", imageid=""): + logger.debug("Images--get::> %s" % request.data) + try: + # prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + content, status_code = self.get_images(query, vimid, tenantid, imageid) + return Response(data=content, status=status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_images(self, query="", vimid="", tenantid="", imageid=""): + logger.debug("Images--get_images::> %s" % imageid) + + # prepare request resource to vim instance + req_resouce = "v2/images" + if imageid: + req_resouce += "/%s" % imageid + elif query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not imageid: + # convert the key naming in images + for image in content["images"]: + VimDriverUtils.replace_key_by_mapping(image, + self.keys_mapping) + else: + # convert the key naming in the image specified by id + #image = content.pop("image", None) + VimDriverUtils.replace_key_by_mapping(content, + self.keys_mapping) + #content.update(image) + + return content, resp.status_code + + def post(self, request, vimid="", tenantid="", imageid=""): + logger.debug("Images--post::> %s" % request.data) + try: + #check if created already: check name + query = "name=%s" % request.data["name"] + content, status_code = self.get_images(query, vimid, tenantid) + existed = False + if status_code == 200: + for image in content["images"]: + if image["name"] == request.data["name"]: + existed = True + break + pass + if existed == True: + vim_dict = { + "returnCode": 0, + } + image.update(vim_dict) + return Response(data=image, status=status_code) + + imageurl = request.data.pop("imagePath", None) + imagefd = None + if not imageurl: + return Response(data={'error': 'imagePath is not specified'}, status=500) + + #valid image url + imagefd = urllib2.urlopen(imageurl) + if not imagefd: + logger.debug("image is not available at %s" % imageurl) + return Response(data={'error': 'cannot access to specified imagePath'}, status=500) + + # prepare request resource to vim instance + req_resouce = "v2/images" + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + image = request.data + VimDriverUtils.replace_key_by_mapping(image, + self.keys_mapping, True) + #req_body = json.JSONEncoder().encode({"image": image}) + req_body = json.JSONEncoder().encode(image) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service) + #resp_body = resp.json()["image"] + resp_body = resp.json() + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + + #launch a thread to download image and upload to VIM + if resp.status_code == 201: + imageid = resp_body["id"] + logger.debug("launch thread to upload image: %s" % imageid) + tmp_thread = imageThread(vimid, tenantid,imageid,imagefd) + running_thread_lock.acquire() + running_threads[imageid] = tmp_thread + running_thread_lock.release() + tmp_thread.start() + else: + logger.debug("resp.status_code: %s" % resp.status_code) + pass + + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except urllib2.URLError as e: + return Response(data={'error': 'image is not accessible:%s' % str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def delete(self, request, vimid="", tenantid="", imageid=""): + logger.debug("Images--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "v2/images" + if imageid: + req_resouce += "/%s" % imageid + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass diff --git a/kilo/kilo/requests/views/limits.py b/kilo/kilo/requests/views/limits.py new file mode 100644 index 00000000..2cddfb42 --- /dev/null +++ b/kilo/kilo/requests/views/limits.py @@ -0,0 +1,69 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Limits(APIView): + service = {'service_type': 'compute', + 'interface': 'public', + 'region_name': 'RegionOne'} + + service_network = {'service_type': 'network', + 'interface': 'public', + 'region_name': 'RegionOne'} + + def get(self, request, vimid="", tenantid=""): + logger.debug("Limits--get::> %s" % request.data) + try: + #get limits first + # prepare request resource to vim instance + req_resouce = "/limits" + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + content_all =content['limits']['absolute'] + + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content_all.update(vim_dict) + + #now get quota + # prepare request resource to vim instance + req_resouce = "/v2.0/quotas/%s" % tenantid + resp = sess.get(req_resouce, endpoint_filter=self.service_network) + content = resp.json() + content_all.update(content['quota']) + + return Response(data=content_all, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + diff --git a/kilo/kilo/requests/views/network.py b/kilo/kilo/requests/views/network.py new file mode 100644 index 00000000..c0133481 --- /dev/null +++ b/kilo/kilo/requests/views/network.py @@ -0,0 +1,162 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Networks(APIView): + service = {'service_type': 'network', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("provider:segmentation_id", "segmentationId"), + ("provider:physical_network", "physicalNetwork"), + ("router:external", "routerExternal"), + ("provider:network_type", "networkType"), + ("vlan_transparent", "vlanTransparent"), + ("project_id", "tenantId"), + ] + + def get(self, request, vimid="", tenantid="", networkid=""): + logger.debug("Networks--get::> %s" % request.data) + try: + query = VimDriverUtils.get_query_part(request) + content, status_code = self.get_networks(query, vimid, tenantid, networkid) + return Response(data=content, status=status_code) + + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_networks(self, query, vimid="", tenantid="", networkid=""): + logger.debug("Networks--get_networks::> %s" % networkid) + + # prepare request resource to vim instance + req_resouce = "v2.0/networks" + if networkid: + req_resouce += "/%s" % networkid + + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not networkid: + # convert the key naming in networks + for network in content["networks"]: + VimDriverUtils.replace_key_by_mapping(network, + self.keys_mapping) + else: + # convert the key naming in the network specified by id + network = content.pop("network", None) + VimDriverUtils.replace_key_by_mapping(network, + self.keys_mapping) + content.update(network) + + return content, resp.status_code + + def post(self, request, vimid="", tenantid="", networkid=""): + logger.debug("Networks--post::> %s" % request.data) + try: + #check if created already: check name + query = "name=%s" % request.data["name"] + content, status_code = self.get_networks(query, vimid, tenantid) + existed = False + if status_code == 200: + for network in content["networks"]: + if network["name"] == request.data["name"]: + existed = True + break + pass + if existed == True: + vim_dict = { + "returnCode": 0, + } + network.update(vim_dict) + return Response(data=network, status=status_code) + + # prepare request resource to vim instance + req_resouce = "v2.0/networks.json" + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + network = request.data + VimDriverUtils.replace_key_by_mapping(network, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"network": network}) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service) + resp_body = resp.json()["network"] + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def delete(self, request, vimid="", tenantid="", networkid=""): + logger.debug("Networks--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "v2.0/networks" + if networkid: + req_resouce += "/%s" % networkid + query = VimDriverUtils.get_query_part(request) + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + +class Subnets(APIView): + pass diff --git a/kilo/kilo/requests/views/server.py b/kilo/kilo/requests/views/server.py new file mode 100644 index 00000000..53efde23 --- /dev/null +++ b/kilo/kilo/requests/views/server.py @@ -0,0 +1,260 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Servers(APIView): + service = {'service_type': 'compute', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("tenant_id", "tenantId"), + ("flavorRef", "flavorId"), + ("user_data", "userdata"), + ("security_groups", "securityGroups"), + ("availability_zone ", "availabilityZone"), + ] + + service_volume = {'service_type': 'volumev2', + 'interface': 'public', + 'region_name': 'RegionOne'} + + def attachVolume(self, sess, serverId, volumeId): + req_resouce = "volumes" + if volumeId: + req_resouce += "/%s/action" % volumeId + + req_data = {"os-attach": { + "instance_uuid": serverId + }} + req_body = json.JSONEncoder().encode(req_data) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service, headers={"Content-Type": "application/json", + "Accept": "application/json"}) + pass + + def convertMetadata(self, metadata, mata_data, reverse=False): + if reverse == False: + # from extraSpecs to extra_specs + for spec in metadata: + mata_data[spec['keyName']] = spec['value'] + else: + for k, v in mata_data.items(): + spec = {} + spec['keyName'] = k + spec['value'] = v + metadata.append(spec) + + pass + + def get(self, request, vimid="", tenantid="", serverid=""): + logger.debug("Servers--get::> %s" % request.data) + try: + # prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + content, status_code = self.get_servers(query, vimid, tenantid, serverid) + return Response(data=content, status=status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_servers(self, query="", vimid="", tenantid="", serverid=None): + logger.debug("Servers--get_servers::> %s,%s" % (tenantid, serverid)) + + # prepare request resource to vim instance + req_resouce = "servers" + if serverid: + req_resouce += "/%s" % serverid + else: + req_resouce += "/detail" + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not serverid: + # convert the key naming in servers + for server in content["servers"]: + metadata = server.pop("metadata", None) + if metadata: + meta_data = [] + self.convertMetadata(metadata, meta_data, True) + server["metadata"] = meta_data + VimDriverUtils.replace_key_by_mapping(server, + self.keys_mapping) + else: + # convert the key naming in the server specified by id + server = content.pop("server", None) + metadata = server.pop("metadata", None) + if metadata: + meta_data = [] + self.convertMetadata(metadata, meta_data, True) + server["metadata"] = meta_data + VimDriverUtils.replace_key_by_mapping(server, + self.keys_mapping) + content.update(server) + + return content, resp.status_code + + def post(self, request, vimid="", tenantid="", serverid=""): + logger.debug("Servers--post::> %s" % request.data) + try: + # check if created already: check name + query = "name=%s" % request.data["name"] + content, status_code = self.get_servers(query, vimid, tenantid) + existed = False + if status_code == 200: + for server in content["servers"]: + if server["name"] == request.data["name"]: + existed = True + break + pass + if existed == True and server: + vim_dict = { + "returnCode": 0, + } + server.update(vim_dict) + return Response(data=server, status=status_code) + + # prepare request resource to vim instance + req_resouce = "servers" + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + server = request.data + + # convert parameters + boot = server.pop("boot", None) + if not boot: + return Response(data={'error': "missing boot paramters"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + if boot["type"] == 1: + # boot from volume + server["block_device_mapping_v2 "] = {"uuid": boot["volumeId"], + "source_type": "volume", + "destination_type": "volume", + "delete_on_termination": "false"} + else: + # boot from image + server["imageRef"] = boot["imageId"] + + nicarray = server.pop("nicArray", None) + if not nicarray: + return Response(data={'error': "missing nicArray paramters"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + else: + networks = [] + for nic in nicarray: + networks.append({"port": nic["portId"]}) + if len(networks) > 0: + server["networks"] = networks + + meta_data = server.pop("metadata", None) + if meta_data: + metadata = {} + self.convertMetadata(metadata, meta_data, False) + server["metadata"] = metadata + + contextarray = server.pop("contextArray", None) + if contextarray: + # now set "contextArray" array + personalities = [] + for context in contextarray: + personalities.append({"path": context["fileName"], "contents": context["fileData"]}) + if len(personalities) > 0: + server["personality"] = personalities + pass + + volumearray = server.pop("volumeArray", None) + + VimDriverUtils.replace_key_by_mapping(server, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"server": server}) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service, headers={"Content-Type": "application/json", + "Accept": "application/json"}) + + resp_body = resp.json().pop("server", None) + + if resp.status_code == 201 and volumearray: + # server is created, now attach volumes + for volumeId in volumearray: + self.attachVolume(sess, resp_body["id"], volumeId) + pass + + metadata = resp_body.pop("metadata", None) + if metadata: + meta_data = [] + self.convertMetadata(metadata, meta_data, True) + resp_body["metadata"] = meta_data + + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + resp_body["volumeArray"] = volumearray + resp_body["nicArray"] = nicarray + resp_body["contextArray"] = contextarray + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def delete(self, request, vimid="", tenantid="", serverid=""): + logger.debug("Servers--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "servers" + if serverid: + req_resouce += "/%s" % serverid + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass diff --git a/kilo/kilo/requests/views/subnet.py b/kilo/kilo/requests/views/subnet.py new file mode 100644 index 00000000..ef90c53c --- /dev/null +++ b/kilo/kilo/requests/views/subnet.py @@ -0,0 +1,160 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Subnets(APIView): + service = {'service_type': 'network', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("project_id", "tenantId"), + ("network_id", "networkId"), + ("ip_version", "ipVersion"), + ("enable_dhcp", "enableDhcp"), + ("gateway_ip", "gatewayIp"), + ("dns_nameservers", "dnsNameservers"), + ("host_routes", "hostRoutes"), + ("allocation_pools", "allocationPools"), + ] + + def get(self, request, vimid="", tenantid="", subnetid=""): + logger.debug("Subnets--get::> %s" % request.data) + try: + # prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + content, status_code = self.get_subnets(query, vimid, tenantid, subnetid) + return Response(data=content, status=status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_subnets(self, query="", vimid="", tenantid="", subnetid=""): + logger.debug("Subnets--get_subnets::> %s" % subnetid) + + # prepare request resource to vim instance + req_resouce = "v2.0/subnets" + if subnetid: + req_resouce += "/%s" % subnetid + + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not subnetid: + # convert the key naming in subnets + for subnet in content["subnets"]: + VimDriverUtils.replace_key_by_mapping(subnet, + self.keys_mapping) + else: + # convert the key naming in the subnet specified by id + subnet = content.pop("subnet", None) + VimDriverUtils.replace_key_by_mapping(subnet, + self.keys_mapping) + content.update(subnet) + + return content, resp.status_code + + def post(self, request, vimid="", tenantid="", subnetid=""): + logger.debug("Subnets--post::> %s" % request.data) + try: + #check if created already: check name + query = "name=%s" % request.data["name"] + content, status_code = self.get_subnets(query, vimid, tenantid) + existed = False + if status_code == 200: + for subnet in content["subnets"]: + if subnet["name"] == request.data["name"]: + existed = True + break + pass + if existed == True: + vim_dict = { + "returnCode": 0, + } + subnet.update(vim_dict) + return Response(data=subnet, status=status_code) + + # prepare request resource to vim instance + req_resouce = "v2.0/subnets" + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + subnet = request.data + VimDriverUtils.replace_key_by_mapping(subnet, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"subnet": subnet}) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service) + resp_body = resp.json()["subnet"] + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def delete(self, request, vimid="", tenantid="", subnetid=""): + logger.debug("Subnets--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "v2.0/subnets" + if subnetid: + req_resouce += "/%s" % subnetid + query = VimDriverUtils.get_query_part(request) + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass diff --git a/kilo/kilo/requests/views/tenants.py b/kilo/kilo/requests/views/tenants.py new file mode 100644 index 00000000..cefbc8a2 --- /dev/null +++ b/kilo/kilo/requests/views/tenants.py @@ -0,0 +1,75 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + +DEBUG=True + +class Tenants(APIView): + service = {'service_type': 'identity', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("projects", "tenants"), + ] + + def get(self, request, vimid=""): + logger.debug("Tenants--get::> %s" % request.data) + try: + #prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + + vim = VimDriverUtils.get_vim_info(vimid) + req_resouce = "/tenants" + + sess = VimDriverUtils.get_session(vim) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + } + content.update(vim_dict) + + VimDriverUtils.replace_key_by_mapping(content, + self.keys_mapping) + + if query: + _, tenantname = query.split('=') + if tenantname: + tmp=content["tenants"] + content["tenants"] = [] + # convert the key naming in hosts + for tenant in tmp: + if tenantname == tenant['name']: + content["tenants"].append(tenant) + + + return Response(data=content, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + diff --git a/kilo/kilo/requests/views/util.py b/kilo/kilo/requests/views/util.py new file mode 100644 index 00000000..003822f3 --- /dev/null +++ b/kilo/kilo/requests/views/util.py @@ -0,0 +1,76 @@ +# 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 + +from keystoneauth1.identity import v2 as keystone_v2 +from keystoneauth1.identity import v3 as keystone_v3 +from keystoneauth1 import session + +from kilo.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 get_query_part(request): + query = "" + full_path = request.get_full_path() + if '?' in full_path: + _, query = request.get_full_path().split('?') + return query + + @staticmethod + def get_session(vim, tenantid=None): + """ + get vim info from ESR and create auth plugin and session object + """ + auth = None + if '/v2' in vim["url"]: + auth = keystone_v2.Password(auth_url=vim["url"], + username=vim["userName"], + password=vim["password"], + tenant_name=vim["tenant"]) + elif '/v3' in vim["url"]: + auth = keystone_v3.Password(auth_url=vim["url"], + username=vim["userName"], + password=vim["password"], + project_name=vim["tenant"], + user_domain_id='default', + project_domain_id='default') + return session.Session(auth=auth) + + @staticmethod + def replace_a_key(dict_obj, keypair, reverse=False): + old_key, new_key = None, None + if reverse: + old_key, new_key = keypair[1], keypair[0] + else: + old_key, new_key = keypair[0], keypair[1] + + v = dict_obj.pop(old_key, None) + if v: + dict_obj[new_key] = v + + @staticmethod + def replace_key_by_mapping(dict_obj, mapping, reverse=False): + for k in mapping: + VimDriverUtils.replace_a_key(dict_obj, k, reverse) diff --git a/kilo/kilo/requests/views/volume.py b/kilo/kilo/requests/views/volume.py new file mode 100644 index 00000000..eea3537e --- /dev/null +++ b/kilo/kilo/requests/views/volume.py @@ -0,0 +1,159 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Volumes(APIView): + service = {'service_type': 'volumev2', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("project_id", "tenantId"), + ("created_at", "createTime"), + ("size", "volumeSize"), + ("volume_type", "volumeType"), + ("imageRef", "imageId"), + ("availability_zone", "availabilityZone"), + ("server_id", "serverId"), + ("attachment_id", "attachmentId"), + ] + + def get(self, request, vimid="", tenantid="", volumeid=""): + logger.debug("Volumes--get::> %s" % request.data) + try: + # prepare request resource to vim instance + query = VimDriverUtils.get_query_part(request) + content, status_code = self.get_volumes(query, vimid, tenantid, volumeid) + return Response(data=content, status=status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_volumes(self, query="", vimid="", tenantid="", volumeid=None): + logger.debug("Volumes--get_volumes::> %s,%s" % (tenantid, volumeid)) + + # prepare request resource to vim instance + req_resouce = "volumes" + if volumeid: + req_resouce += "/%s" % volumeid + else: + req_resouce += "/detail" + if query: + req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not volumeid: + # convert the key naming in volumes + for volume in content["volumes"]: + VimDriverUtils.replace_key_by_mapping(volume, + self.keys_mapping) + else: + # convert the key naming in the volume specified by id + volume = content.pop("volume", None) + VimDriverUtils.replace_key_by_mapping(volume, + self.keys_mapping) + content.update(volume) + + return content, resp.status_code + + def post(self, request, vimid="", tenantid="", volumeid=""): + logger.debug("Volumes--post::> %s" % request.data) + try: + #check if created already: check name + query = "name=%s" % request.data["name"] + content, status_code = self.get_volumes(query, vimid, tenantid) + existed = False + if status_code == 200: + for volume in content["volumes"]: + if volume["name"] == request.data["name"]: + existed = True + break + pass + if existed == True: + vim_dict = { + "returnCode": 0, + } + volume.update(vim_dict) + return Response(data=volume, status=status_code) + + # prepare request resource to vim instance + req_resouce = "volumes" + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + volume = request.data + VimDriverUtils.replace_key_by_mapping(volume, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"volume": volume}) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service, headers={"Content-Type": "application/json", + "Accept": "application/json" }) + resp_body = resp.json()["volume"] + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + return Response(data=resp_body, status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass + + def delete(self, request, vimid="", tenantid="", volumeid=""): + logger.debug("Volumes--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "volumes" + if volumeid: + req_resouce += "/%s" % volumeid + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass diff --git a/kilo/kilo/requests/views/vport.py b/kilo/kilo/requests/views/vport.py new file mode 100644 index 00000000..6738cab2 --- /dev/null +++ b/kilo/kilo/requests/views/vport.py @@ -0,0 +1,193 @@ +# 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from kilo.pub.exceptions import VimDriverKiloException + +from util import VimDriverUtils + +logger = logging.getLogger(__name__) + + +class Vports(APIView): + service = {'service_type': 'network', + 'interface': 'public', + 'region_name': 'RegionOne'} + keys_mapping = [ + ("project_id", "tenantId"), + ("network_id", "networkId"), + ("binding:vnic_type", "vnicType"), + ("security_groups", "securityGroups"), + ("mac_address", "macAddress"), + ("subnet_id", "subnetId"), + ("ip_address", "ip"), + ] + + def get(self, request, vimid="", tenantid="", portid=""): + logger.debug("Ports--get::> %s" % request.data) + try: + # prepare request resource to vim instance + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + + content, status_code = self.get_ports(sess, request, vim, tenantid, portid) + + return Response(data=content, status=status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + + def get_ports(self, sess, request, vim, tenantid, portid=""): + logger.debug("Ports--get_ports::> %s" % portid) + if sess: + # prepare request resource to vim instance + req_resouce = "v2.0/ports" + if portid: + req_resouce += "/%s" % portid + + query = VimDriverUtils.get_query_part(request) + if query: + req_resouce += "?%s" % query + resp = sess.get(req_resouce, endpoint_filter=self.service) + content = resp.json() + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + } + content.update(vim_dict) + + if not portid: + # convert the key naming in ports + for port in content["ports"]: + # use only 1st entry of fixed_ips + if port: + tmpips = port.pop("fixed_ips", None) + port.update(tmpips[0]) + VimDriverUtils.replace_key_by_mapping(port, + self.keys_mapping) + else: + # convert the key naming in the port specified by id + port = content.pop("port", None) + #use only 1st entry of fixed_ips + if port: + tmpips = port.pop("fixed_ips", None) + port.update(tmpips[0]) + + VimDriverUtils.replace_key_by_mapping(port, + self.keys_mapping) + content.update(port) + return content, resp.status_code + return {}, 500 + + def post(self, request, vimid="", tenantid="", portid=""): + logger.debug("Ports--post::> %s" % request.data) + try: + # prepare request resource to vim instance + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + + #check if already created: name + content, status_code = self.get_ports(sess, request, vim, tenantid) + existed = False + if status_code == 200: + for port in content["ports"]: + if port["name"] == request.data["name"]: + existed = True + break + pass + if existed == True: + vim_dict = { + "returnCode": 0, + } + port.update(vim_dict) + return Response(data=port, status=status_code) + + #otherwise create a new one + return self.create_port(sess, request, vim, tenantid) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def create_port(self, sess, request, vim, tenantid): + logger.debug("Ports--create::> %s" % request.data) + if sess: + # prepare request resource to vim instance + req_resouce = "v2.0/ports" + + port = request.data + #handle ip and subnetId + tmpip = port.pop("ip", None) + tmpsubnet = port.pop("subnetId", None) + if tmpip and tmpsubnet: + fixed_ip = { + "ip_address": tmpip, + "subnet_id": tmpsubnet, + } + port["fixed_ips"] = [] + port["fixed_ips"].append(fixed_ip) + + VimDriverUtils.replace_key_by_mapping(port, + self.keys_mapping, True) + req_body = json.JSONEncoder().encode({"port": port}) + resp = sess.post(req_resouce, data=req_body, + endpoint_filter=self.service) + resp_body = resp.json()["port"] + #use only 1 fixed_ip + tmpips = resp_body.pop("fixed_ips", None) + if tmpips: + resp_body.update(tmpips[0]) + + VimDriverUtils.replace_key_by_mapping(resp_body, self.keys_mapping) + vim_dict = { + "vimName": vim["name"], + "vimId": vim["vimId"], + "tenantId": tenantid, + "returnCode": 1, + } + resp_body.update(vim_dict) + return Response(data=resp_body, status=resp.status_code) + return {} + + def delete(self, request, vimid="", tenantid="", portid=""): + logger.debug("Ports--delete::> %s" % request.data) + try: + # prepare request resource to vim instance + req_resouce = "v2.0/ports" + if portid: + req_resouce += "/%s" % portid +# query = VimDriverUtils.get_query_part(request) +# if query: +# req_resouce += "?%s" % query + + vim = VimDriverUtils.get_vim_info(vimid) + sess = VimDriverUtils.get_session(vim, tenantid) + resp = sess.delete(req_resouce, endpoint_filter=self.service) + return Response(status=resp.status_code) + except VimDriverKiloException as e: + return Response(data={'error': e.content}, status=e.status_code) + except Exception as e: + return Response(data={'error': str(e)}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + pass diff --git a/kilo/kilo/swagger/multivim.image.swagger.json b/kilo/kilo/swagger/multivim.image.swagger.json index 6f8540d8..dcbba06b 100644 --- a/kilo/kilo/swagger/multivim.image.swagger.json +++ b/kilo/kilo/swagger/multivim.image.swagger.json @@ -262,26 +262,6 @@ "visibility": { "type": "string", "description": "public, private, shared, or community" - }, - "properties": { - "type": "array", - "description": "list of properties", - "items": { - "$ref": "#/definitions/VimImagePropertyInfo" - } - } - } - }, - "VimImagePropertyInfo": { - "type": "object", - "properties": { - "keyName": { - "type": "string", - "description": "property name" - }, - "value": { - "type": "string", - "description": "property value" } } }, diff --git a/kilo/kilo/swagger/multivim.volume.swagger.json b/kilo/kilo/swagger/multivim.volume.swagger.json index fe210441..c91deacb 100644 --- a/kilo/kilo/swagger/multivim.volume.swagger.json +++ b/kilo/kilo/swagger/multivim.volume.swagger.json @@ -364,9 +364,9 @@ "type": "string", "description": "volume UUID" }, - "deviceId": { + "device": { "type": "string", - "description": "device UUID" + "description": "device to be attached" }, "hostName": { "type": "string", diff --git a/kilo/kilo/urls.py b/kilo/kilo/urls.py index 1a561f95..2dfb95a2 100644 --- a/kilo/kilo/urls.py +++ b/kilo/kilo/urls.py @@ -10,11 +10,18 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from django.conf.urls import include, url -from kilo.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM +from kilo.pub.config.config \ + import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM + +from kilo.requests.views import tenants urlpatterns = [ url(r'^', include('kilo.swagger.urls')), url(r'^', include('kilo.samples.urls')), + url(r'^openoapi/multivim-kilo/v1/(?P<vimid>[0-9a-zA-Z_-]+)/tenants$', + tenants.Tenants.as_view()), + url(r'^openoapi/multivim-kilo/v1/(?P<vimid>[0-9a-zA-Z_-]+)/' + '(?P<tenantid>[0-9a-zA-Z_-]{8,})/', include('kilo.requests.urls')), ] #url(r'^', include('kilo.forward.urls')), @@ -22,4 +29,5 @@ urlpatterns = [ if REG_TO_MSB_WHEN_START: import json from kilo.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)) diff --git a/kilo/requirements.txt b/kilo/requirements.txt index 6d58957b..5976f5c9 100644 --- a/kilo/requirements.txt +++ b/kilo/requirements.txt @@ -2,20 +2,11 @@ Django==1.9.6 djangorestframework==3.3.3 -# redis cache -redis==2.10.5 - -# for access redis cache -redisco==0.1.4 -django-redis-cache==0.13.1 - # for call rest api httplib2==0.9.2 -# for call openstack api -python-keystoneclient==3.6.0 -python-glanceclient==2.5.0 -python-neutronclient==6.0.0 +# for call openstack auth and transport api +keystoneauth1==2.18.0 # for unit test coverage==4.2 |