diff options
author | Quan-Zhong <quanzhong@huawei.com> | 2017-07-04 17:30:27 +0800 |
---|---|---|
committer | Quan-Zhong <quanzhong@huawei.com> | 2017-07-04 17:30:27 +0800 |
commit | 308af7411c9f9cdb065da07b6d77e32f5911e7a1 (patch) | |
tree | 37e42ceddc177a6dc1a69bac096c80f506ea8d22 /gvnfmadapter | |
parent | 381a045ffca7ee96cf63a919220c619b0aafe8ed (diff) |
add gvnfm codes
NFVO-127
Change-Id: I447ff20ed2559d6b77041879f6abc15710acb501
Signed-off-by: Quan-Zhong <quanzhong@huawei.com>
Diffstat (limited to 'gvnfmadapter')
29 files changed, 1915 insertions, 0 deletions
diff --git a/gvnfmadapter/assembly.xml b/gvnfmadapter/assembly.xml new file mode 100644 index 0000000..30a2649 --- /dev/null +++ b/gvnfmadapter/assembly.xml @@ -0,0 +1,51 @@ +<!-- + Copyright 2017 ZTE Corporation. + + 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. +--> +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>drivers-vnfm-gvnfm-gvnfmadapter</id> + <formats> + <format>zip</format> + </formats> + <fileSets> + <fileSet> + <directory>driver</directory> + <outputDirectory>/driver</outputDirectory> + <includes> + <include>**/*.py</include> + <include>**/*.json</include> + </includes> + </fileSet> + <fileSet> + <directory>logs</directory> + <outputDirectory>/logs</outputDirectory> + <includes> + <include>*.txt</include> + </includes> + </fileSet> + <fileSet> + <directory>.</directory> + <outputDirectory>/</outputDirectory> + <includes> + <include>*.py</include> + <include>*.txt</include> + <include>*.sh</include> + <include>*.ini</include> + </includes> + </fileSet> + </fileSets> + <baseDirectory>nfvo/drivers/vnfm/gvnfm/gvnfmadapter</baseDirectory> +</assembly> diff --git a/gvnfmadapter/driver/__init__.py b/gvnfmadapter/driver/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/interfaces/__init__.py b/gvnfmadapter/driver/interfaces/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/interfaces/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/interfaces/tests.py b/gvnfmadapter/driver/interfaces/tests.py new file mode 100644 index 0000000..8b154af --- /dev/null +++ b/gvnfmadapter/driver/interfaces/tests.py @@ -0,0 +1,300 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 json +import mock +from django.test import Client +from django.test import TestCase +from rest_framework import status +from driver.pub.utils import restcall + + +class InterfacesTest(TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + @mock.patch.object(restcall, 'call_req') + def test_instantiate_vnf(self, mock_call_req): + vnfm_info = { + 'userName': 'admin', + 'vendor': 'ZTE', + 'name': 'ZTE_VNFM_237_62', + 'vimId': '516cee95-e8ca-4d26-9268-38e343c2e31e', + 'url': 'http://192.168.237.165:2324', + 'certificateUrl': '', + 'version': 'V1.0', + 'vnfmId': 'b0797c9b-3da9-459c-b25c-3813e9d8fd70', + 'password': 'admin', + 'type': 'ztevmanagerdriver', + 'createTime': '2016-10-31 11:08:39', + 'description': '' + } + job_info = { + "vnfInstanceId":"8", + "jobId":"NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9" + } + vnflcm_info = { + "vnfInstanceId":"8", + "vnfLcOpId":"NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9" + } + + r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"] + ret = [0, json.JSONEncoder().encode(job_info), '200'] + ret2 = [0, json.JSONEncoder().encode(vnflcm_info), '200'] + mock_call_req.side_effect = [r1, ret, r1, ret2] + req_data = { + 'vnfInstanceName': 'VFW_f88c0cb7-512a-44c4-bd09-891663f19367', + 'vnfPackageId': 'd852e1be-0aac-48f1-b1a4-cd825f6cdf9a', + 'vnfDescriptorId': 'vcpe_vfw_zte_1_0', + 'additionalParam': { + 'sdncontroller': 'e4d637f1-a4ec-4c59-8b20-4e8ab34daba9', + 'NatIpRange': '192.167.0.10-192.168.0.20', + 'm6000_mng_ip': '192.168.11.11', + 'externalPluginManageNetworkName': 'plugin_net_2014', + 'location': '516cee95-e8ca-4d26-9268-38e343c2e31e', + 'externalManageNetworkName': 'mng_net_2017', + 'sfc_data_network': 'sfc_data_net_2016', + 'externalDataNetworkName': 'Flow_out_net', + 'inputs':{} + } + } + response = self.client.post("/openoapi/ztevnfm/v1/1/vnfs", + data=json.dumps(req_data), content_type="application/json") + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + print job_info + print response.data + self.assertEqual(job_info, response.data) + + + @mock.patch.object(restcall, 'call_req') + def test_terminate_vnf(self, mock_call_req): + vnfm_info = { + "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee", + "name": "g_vnfm", + "type": "vnfm", + "vimId": "", + "vendor": "ZTE", + "version": "v1.0", + "description": "vnfm", + "certificateUrl": "", + "url": "http://10.74.44.11", + "userName": "admin", + "password": "admin", + "createTime": "2016-07-06 15:33:18" + } + job_info = {"vnfInstanceId": "1", "vnfLcOpId": "1"} + job_status_info = {"VnfLcOpResponseDescriptor":{"progress":"100"}} + r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"] + r2 = [0, json.JSONEncoder().encode(job_info), "200"] + job_ret = [0, json.JSONEncoder().encode(job_status_info), "200"] + mock_call_req.side_effect = [r1, r2, r1, job_ret, r1, r2] + response = self.client.post("/openoapi/ztevnfm/v1/ztevnfmid/vnfs/2/terminate") + self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code) + self.assertEqual(job_info, response.data) + + + @mock.patch.object(restcall, 'call_req') + def test_query_vnf(self, mock_call_req): + vnfm_info = { + "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee", + "name": "g_vnfm", + "type": "vnfm", + "vimId": "", + "vendor": "ZTE", + "version": "v1.0", + "description": "vnfm", + "certificateUrl": "", + "url": "http://10.74.44.11", + "userName": "admin", + "password": "admin", + "createTime": "2016-07-06 15:33:18" + } + job_info = {"ResponseInfo": {"vnfInstanceId":"88","instantiationState":"INSTANTIATED","vnfSoftwareVersion":"v1.2.3"}} + r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"] + r2 = [0, json.JSONEncoder().encode(job_info), "200"] + mock_call_req.side_effect = [r1, r2] + response = self.client.get("/openoapi/ztevnfm/v1/19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee/vnfs/88") + self.assertEqual(status.HTTP_200_OK, response.status_code) + expect_resp_data = {"vnfInfo": {"vnfInstanceId": "88", "vnfStatus": "ACTIVE","version":"v1.2.3"}} + self.assertEqual(expect_resp_data, response.data) + + + @mock.patch.object(restcall, 'call_req') + def test_operation_status(self, mock_call_req): + vnfm_info = { + 'userName': 'admin', + 'vendor': 'ZTE', + 'name': 'ZTE_VNFM_237_62', + 'vimId': '516cee95-e8ca-4d26-9268-38e343c2e31e', + 'url': 'http://192.168.237.165:2324', + 'certificateUrl': '', + 'version': 'V1.0', + 'vnfmId': 'b0797c9b-3da9-459c-b25c-3813e9d8fd70', + 'password': 'admin', + 'type': 'ztevmanagerdriver', + 'createTime': '2016-10-31 11:08:39', + 'description': '' + } + expected_body = { + "jobId": "NF-CREATE-11-ec6c2f2a-9f48-11e6-9405-fa163e91c2f9", + "responseDescriptor":{ + "responseId": 3, + "progress": 40, + "status": "PROCESSING", + "statusDescription": "OMC VMs are decommissioned in VIM", + "errorCode": "null", + "responseHistoryList": [ + { + "status": "error", + "progress": 255, + "errorcode": "", + "responseid": 20, + "statusdescription": "'JsonParser' object has no attribute 'parser_info'" + } + ] + } + } + resp_body = { + "ResponseInfo": { + "vnfLcOpId":"NF-CREATE-11-ec6c2f2a-9f48-11e6-9405-fa163e91c2f9", + "responseDescriptor":{ + "responseId": 3, + "progress": 40, + "lcmOperationStatus": "PROCESSING", + "statusDescription": "OMC VMs are decommissioned in VIM", + "errorCode": "null", + "responseHistoryList": [ + {"status": "error", + "progress": 255, + "errorcode": "", + "responseid": 20, + "statusdescription": "'JsonParser' object has no attribute 'parser_info'"}] + } + } + } + r1 = [0, json.JSONEncoder().encode(vnfm_info), '200'] + r2 = [0, json.JSONEncoder().encode(resp_body), '200'] + mock_call_req.side_effect = [r1, r2] + response = self.client.get("/openoapi/gvnfmadapter/v1/{vnfmid}/jobs/{jobid}?responseId={responseId}". + format(vnfmid=vnfm_info["vnfmId"],jobid=resp_body["ResponseInfo"]["vnfLcOpId"], + responseId=resp_body["ResponseInfo"]["responseDescriptor"]["responseId"])) + self.assertEqual(status.HTTP_200_OK, response.status_code) + print "========" + print response.data + self.assertDictEqual(expected_body, response.data) + + + @mock.patch.object(restcall, 'call_req') + def test_grantvnf(self, mock_call_req): + vim_info = {"vim":{"accessinfo":{"tenant":"admin"},"vimid":"516cee95-e8ca-4d26-9268-38e343c2e31e"}} + req_data = { + "vnfmid": "13232222", + "nfvoid": "03212234", + "vimid": "12345678", + "exvimidlist ":["exvimid"], + "tenant": " tenant1", + "vnfistanceid": "1234", + "operationright": "0", + "vmlist": [ + { + "vmflavor": "SMP", + "vmnumber": "3"}, + { + "vmflavor": "CMP", + "vmnumber": "3"} + ] + } + mock_call_req.return_value = [0, json.JSONEncoder().encode(vim_info), '201'] + response = self.client.put("/openoapi/gvnfmadapter/v1/resource/grant", + data=json.dumps(req_data), content_type='application/json') + self.assertEqual(str(status.HTTP_201_CREATED), response.status_code) + expect_resp_data = {"vimid": "516cee95-e8ca-4d26-9268-38e343c2e31e", "tenant": "admin"} + self.assertDictEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_notify(self, mock_call_req): + vim_info = { + "vim":{ + "vimInfoId": "111111", + "vimId": "12345678", + "interfaceInfo": { + "vimType": "vnf", + "apiVersion": "v1", + "protocolType": "None"}, + "accessInfo": { + "tenant": "tenant1", + "username": "admin", + "password": "password"}, + "interfaceEndpoint": "http://127.0.0.1/api/v1" + }, + "zone": "", + "addResource": { + "resourceDefinitionId": "xxxxx", + "vimId": "12345678", + "zoneId": "000"}, + "removeResource": "", + "vimAssets": { + "computeResourceFlavour": { + "vimId": "12345678", + "vduId": "sdfasdf", + "vimFlavourId": "12"}, + "softwareImage": { + "vimId": "12345678", + "imageName": "AAA", + "vimImageId": ""}}, + "additionalParam": "" + } + r2 = [0, json.JSONEncoder().encode(vim_info), "200"] + mock_call_req.side_effect = [r2] + req_data = { + "nfvoid": "1", + "vnfmid": "876543211", + "vimid": "6543211", + "timestamp": "1234567890", + "vnfinstanceid": "1", + "eventtype": "0", + "vmlist": + [ + { + "vmflavor": "SMP", + "vmnumber": "3", + "vmidlist ": ["vmuuid"]}, + { + "vmflavor": "CMP", + "vmnumber": "3", + "vmidlist ": ["vmuuid"]} + ] + } + response = self.client.post("/openoapi/gvnfmadapter/v1/vnfs/lifecyclechangesnotification", + data=json.dumps(req_data), content_type='application/json') + self.assertEqual(str(status.HTTP_200_OK), response.status_code) + expect_resp_data = None + self.assertEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_get_vnfpkgs(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "csars": [{ + "csarId": "1", + "vnfdId": "2" + }] + }), '200'] + resp = self.client.get("/openoapi/gvnfmadapter/v1/vnfpackages") + self.assertEqual(status.HTTP_200_OK, resp.status_code) + self.assertEqual(1, len(resp.data["csars"])) + self.assertEqual("1", resp.data["csars"][0]["csarId"]) + self.assertEqual("2", resp.data["csars"][0]["vnfdId"]) diff --git a/gvnfmadapter/driver/interfaces/urls.py b/gvnfmadapter/driver/interfaces/urls.py new file mode 100644 index 0000000..8c1bfc7 --- /dev/null +++ b/gvnfmadapter/driver/interfaces/urls.py @@ -0,0 +1,29 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 driver.interfaces import views + +urlpatterns = [ + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs$', views.instantiate_vnf, + name='instantiate_vnf'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<vnfInstanceId>' + r'[0-9a-zA-Z\-\_]+)/terminate$',views.terminate_vnf, name='terminate_vnf'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<vnfInstanceId>' + r'[0-9a-zA-Z\-\_]+)$',views.query_vnf, name='query_vnf'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/jobs/(?P<jobid>[0-9a-zA-Z\-\_]+)$', + views.operation_status, name='operation_status'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfpackages$', views.get_vnfpkgs, name='get_vnfpkgs'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/resource/grant$', views.grantvnf, name='grantvnf'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfs/lifecyclechangesnotification$', views.notify, name='notify'),] diff --git a/gvnfmadapter/driver/interfaces/views.py b/gvnfmadapter/driver/interfaces/views.py new file mode 100644 index 0000000..c903ab9 --- /dev/null +++ b/gvnfmadapter/driver/interfaces/views.py @@ -0,0 +1,441 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 inspect +import json +import logging +import time +from rest_framework.decorators import api_view +from rest_framework.response import Response +from driver.pub.utils import restcall +from driver.pub.utils.restcall import req_by_msb +from rest_framework import status + +# ================================================== +vnf_create_url = "openoapi/vnflcm/v1/vnf_instances" +vnf_inst_url = "openoapi/vnflcm/v1/vnf_instances/%s/instantiate" +vnf_delete_url = "openoapi/vnflcm/v1/vnf_instances/%s" +vnf_terminate_url = "openoapi/vnflcm/v1/vnf_instances/%s/terminate" +operation_status_url = "openoapi/vnflcm/v1/vnf_lc_ops/%s?responseId=%s" +vnf_detail_url = "openoapi/vnflcm/v1/vnf_instances/%s" +EXTSYS_GET_VNFM = "openoapi/extsys/v1/vnfms/%s" +vnf_query_url = "openoapi/vnflcm/v1/vnf_instances/%s" +notify_url = 'openoapi/nslcm/v1/vnfs/{vnfInstanceId}/Notify' + +query_vnf_resp_mapping = { + "vnfInstanceId": "", + "vnfInstanceName": "", + "vnfInstanceDescription": "", + "vnfdId": "", + "vnfPackageId":"", + "version":"", + "vnfProvider":"", + "vnfType":"", + "vnfStatus":"" +} + + +logger = logging.getLogger(__name__) + + +def mapping_conv(keyword_map, rest_return): + resp_data = {} + for param in keyword_map: + if keyword_map[param]: + if isinstance(keyword_map[param], dict): + resp_data[param] = mapping_conv(keyword_map[param], ignorcase_get(rest_return, param)) + else: + resp_data[param] = ignorcase_get(rest_return, param) + return resp_data + +def fun_name(): + return "=================%s==================" % inspect.stack()[1][3] + +def ignorcase_get(args, key): + if not key: + return "" + if not args: + return "" + if key in args: + return args[key] + for old_key in args: + if old_key.upper() == key.upper(): + return args[old_key] + return "" + +def set_createvnf_params(data): + input_data = {} + input_data["vnfdId"] = ignorcase_get(data,"vnfDescriptorId") + input_data["vnfInstanceName"] = ignorcase_get(data, "vnfInstanceName") + input_data["vnfInstanceDescription"] = ignorcase_get(data, "vnfInstanceDescription") + + return input_data + +def set_instantvnf_params(data): + input_data = {} + input_data["flavourId"] = ignorcase_get(data, "flavourId") + input_data["extVirtualLinks"] = ignorcase_get(data, "extVirtualLink") + input_data["additionalParams"] = ignorcase_get(data,"additionalParams") + input_data["flavourId"] = ignorcase_get(data,"flavourId") + + return input_data + +def set_terminatevnf_params(data): + input_data = {} + input_data["terminationType"] = ignorcase_get(data,"terminationType") + input_data["gracefulTerminationTimeout"] = ignorcase_get(data,"gracefulTerminationTimeout") + + return input_data + +def set_deletevnf_params(data): + pass + + +def get_inst_levelId(vnfdId): + inst_levelId = 0 + + return inst_levelId + +def get_vnfm_info(vnfm_id): + ret = req_by_msb((EXTSYS_GET_VNFM) % vnfm_id, "GET") + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + vnfm_info = json.JSONDecoder().decode(ret[1]) + logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info) + return 0, vnfm_info + +def call_vnfm_rest(vnfm_info, input_data, res_url, call_method = "post"): + ret = restcall.call_req( + base_url=ignorcase_get(vnfm_info, "url"), + user=ignorcase_get(vnfm_info, "userName"), + passwd=ignorcase_get(vnfm_info, "password"), + auth_type=restcall.rest_no_auth, + resource=res_url, + method=call_method, + content=json.JSONEncoder().encode(input_data)) + + return ret + +def call_vnfm_createvnf(vnfm_info, input_data): + return call_vnfm_rest(vnfm_info, input_data, vnf_create_url) + +def call_vnfm_instvnf(vnfm_info, input_data, vnfInstanceId): + return call_vnfm_rest(vnfm_info, input_data, vnf_inst_url % vnfInstanceId, "post") + +def call_vnfm_terminatevnf(vnfm_info, input_data, vnfInstanceId): + return call_vnfm_rest(vnfm_info, input_data, vnf_terminate_url % vnfInstanceId, "post") + +def call_vnfm_deletevnf(vnfm_info, vnfInstanceId): + return call_vnfm_rest(vnfm_info, None, vnf_delete_url % vnfInstanceId, "delete") + +def call_vnfm_queryvnf(vnfm_info,vnfInstanceId): + return call_vnfm_rest(vnfm_info, None, vnf_query_url % vnfInstanceId, "get") + +def call_vnfm_operation_status(vnfm_info, jobId, responseId = None): + return call_vnfm_rest(vnfm_info, None, operation_status_url % (jobId, responseId), "get") + +def wait4job(vnfm_id,jobId,gracefulTerminationTimeout): + + begin_time = time.time() + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return 255, Response(data={"error":"Fail to get VNFM!"}, status=status.HTTP_412_PRECONDITION_FAILED) + + responseId = None + while ret == 0: + cur_time = time.time() + if gracefulTerminationTimeout and (cur_time - begin_time > gracefulTerminationTimeout): + return 255, Response(data={"error":"Fail to terminate VNF!"}, status=status.HTTP_408_REQUEST_TIMEOUT) + + ret = call_vnfm_operation_status(vnfm_info,jobId,responseId) + if ret[0] != 0: + return 255, Response(data={"error":"Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED) + if json.JSONDecoder().decode(ret[2]) != 200: + return 255, Response(data={"error":"Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED) + job_info = json.JSONDecoder().decode(ret[1]) + responseId = ignorcase_get(ignorcase_get(job_info, "VnfLcOpResponseDescriptor"), "responseId") + progress = ignorcase_get(ignorcase_get(job_info, "VnfLcOpResponseDescriptor"), "progress") + if progress == "100": + return 0, Response(data={"success":"success"}, status=status.HTTP_204_NO_CONTENT) + except Exception as e: + logger.error("Error occurred when do_createvnf") + return 255, Response(data={"error":"Exception caught! Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED) + + +def do_createvnf(request, data, vnfm_id): + logger.debug("[%s] request.data=%s", fun_name(), request.data) + + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return ret, vnfm_info + + ret = call_vnfm_createvnf(vnfm_info, data) + logger.debug("[%s] call_req ret=%s", fun_name(), ret) + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + except Exception as e: + logger.error("Error occurred when do_createvnf") + raise e + + return 0, resp + +def do_instvnf(vnfInstanceId, request, data, vnfm_id): + logger.debug("[%s] request.data=%s", fun_name(), request.data) + + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return ret, vnfm_info + + ret = call_vnfm_instvnf(vnfm_info,data, vnfInstanceId) + logger.debug("[%s] call_req ret=%s", fun_name(), ret) + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + except Exception as e: + logger.error("Error occurred when do_instvnf") + raise e + + return 0, resp + +def do_terminatevnf(request, data, vnfm_id, vnfInstanceId): + logger.debug("[%s] request.data=%s", fun_name(), request.data) + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return ret,vnfm_info + + ret = call_vnfm_terminatevnf(vnfm_info, data, vnfInstanceId) + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + resp_data = json.JSONDecoder().decode(ret[1]) + logger.debug("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when do_terminatevnf") + raise e + + return 0, resp_data + +def do_deletevnf(request, vnfm_id, vnfInstanceId): + logger.debug("[%s] request.data=%s", fun_name(), request.data) + input_data = set_deletevnf_params(request.data) + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return ret, vnfm_info + + ret = call_vnfm_deletevnf(vnfm_info, vnfInstanceId) + + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + resp_data = json.JSONDecoder().decode(ret[1]) + logger.debug("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when do_deletevnf") + raise e + return 0, resp_data + +def do_queryvnf(request, vnfm_id, vnfInstanceId): + logger.debug("[%s] request.data=%s", fun_name(), request.data) + try: + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return ret, vnfm_info + + ret = call_vnfm_queryvnf(vnfm_info, vnfInstanceId) + + if ret[0] != 0: + return 255, Response(data={'error': ret[1]}, status=ret[2]) + resp_data = json.JSONDecoder().decode(ret[1]) + logger.debug("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when do_query vnf") + raise e + return 0, resp_data + +@api_view(http_method_names=['POST']) +def instantiate_vnf(request, *args, **kwargs): + try: + input_data = set_createvnf_params(request.data) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + ret, resp = do_createvnf(request, input_data, vnfm_id) + if ret != 0: + return resp + + logger.info("[%s]resp_data=%s", fun_name(), resp) + vnfInstanceId = resp["vnfInstanceId"] + logger.info("[%s]vnfInstanceId=%s", fun_name(), vnfInstanceId) + + input_data = set_instantvnf_params(request.data) + ret, resp = do_instvnf(vnfInstanceId, request, input_data, vnfm_id) + if ret != 0: + return resp + + resp_data = {"jobId":"", "vnfInstanceId":""} + resp_data["vnfInstanceId"] = vnfInstanceId + resp_data["jobId"] = resp["vnfLcOpId"] + except Exception as e: + logger.error("Error occurred when instantiating VNF") + raise e + + return Response(data=resp_data, status=status.HTTP_201_CREATED) + + +@api_view(http_method_names=['POST']) +def terminate_vnf(request, *args, **kwargs): + vnfm_id = ignorcase_get(kwargs, "vnfmid") + vnfInstanceId = ignorcase_get(kwargs, "vnfInstanceId") + try: + input_data = set_terminatevnf_params(request.data) + ret, resp = do_terminatevnf(request, input_data, vnfm_id, vnfInstanceId) + if ret != 0: + return resp + + jobId = ignorcase_get(resp, "vnfLcOpId") + gracefulTerminationTimeout = ignorcase_get(request.data, "gracefulTerminationTimeout") + ret, response = wait4job(vnfm_id,jobId,gracefulTerminationTimeout) + if ret != 0: + return response + + ret, resp = do_deletevnf(request, vnfm_id, vnfInstanceId) + if ret != 0: + return resp + + except Exception as e: + logger.error("Error occurred when terminating VNF") + raise e + + return Response(data=resp, status=status.HTTP_204_NO_CONTENT) + +@api_view(http_method_names=['GET']) +def query_vnf(request, *args, **kwargs): + vnfm_id = ignorcase_get(kwargs, "vnfmid") + vnfInstanceId = ignorcase_get(kwargs, "vnfInstanceId") + try: + logger.debug("[%s] request.data=%s", fun_name(), request.data) + ret, resp = do_queryvnf(request, vnfm_id, vnfInstanceId) + if ret != 0: + return resp + + resp_response_data = mapping_conv(query_vnf_resp_mapping, ignorcase_get(resp, "ResponseInfo")) + resp_data = { + "vnfInfo":resp_response_data + } + #Handle vnfSoftwareVersion and vnfStatus specially + resp_data["vnfInfo"]["version"] = ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfSoftwareVersion") + if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "instantiationState"): + if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "instantiationState") == "INSTANTIATED": + resp_data["vnfInfo"]["vnfStatus"] = "ACTIVE" + if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfInstanceId"): + resp_data["vnfInfo"]["vnfInstanceId"] = ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfInstanceId") + logger.debug("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when querying VNF information.") + raise e + return Response(data=resp_data, status=status.HTTP_200_OK) + +# ================================================== + + +@api_view(http_method_names=['GET']) +def operation_status(request, *args, **kwargs): + data = {} + try: + logger.debug("[%s] request.data=%s", fun_name(), request.data) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + jobId = ignorcase_get(kwargs, "jobId") + responseId = ignorcase_get(kwargs, "responseId") + + ret, vnfm_info = get_vnfm_info(vnfm_id) + if ret != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info) + + ret = call_vnfm_operation_status(vnfm_info, jobId, responseId) + + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + resp_data = json.JSONDecoder().decode(ret[1]) + logger.info("[%s]resp_data=%s", fun_name(), resp_data) + ResponseInfo = ignorcase_get(resp_data, "ResponseInfo") + operation_data = {} + operation_data["jobId"] = ignorcase_get(ResponseInfo, "vnfLcOpId") + operation_data["responseDescriptor"] = {} + operation_data["responseDescriptor"]["status"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"lcmOperationStatus") + operation_data["responseDescriptor"]["progress"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"progress") + operation_data["responseDescriptor"]["statusDescription"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"statusDescription") + operation_data["responseDescriptor"]["errorCode"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"errorCode") + operation_data["responseDescriptor"]["responseId"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"responseId") + operation_data["responseDescriptor"]["responseHistoryList"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"responseHistoryList") + + except Exception as e: + logger.error("Error occurred when getting operation status information.") + raise e + return Response(data=operation_data, status=status.HTTP_200_OK) + + +# ================================================== +grant_vnf_url = 'openoapi/nslcm/v1/grantvnf' + +@api_view(http_method_names=['PUT']) +def grantvnf(request, *args, **kwargs): + logger.info("=====grantvnf=====") + try: + resp_data = {} + logger.info("req_data = %s", request.data) + ret = req_by_msb(grant_vnf_url, "POST", content=json.JSONEncoder().encode(request.data)) + logger.info("ret = %s", ret) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + resp_data['vimid'] = ignorcase_get(resp['vim'], 'vimid') + resp_data['tenant'] = ignorcase_get(ignorcase_get(resp['vim'], 'accessinfo'), 'tenant') + logger.info("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred in Grant VNF.") + raise e + return Response(data=resp_data, status=ret[2]) + + +# ================================================== + + +@api_view(http_method_names=['POST']) +def notify(request, *args, **kwargs): + try: + logger.info("[%s]req_data = %s", fun_name(), request.data) + ret = req_by_msb(notify_url.format(vnfmid=ignorcase_get(request.data, 'VNFMID'), + vnfInstanceId=ignorcase_get(request.data, 'vnfinstanceid')), + "POST", content=json.JSONEncoder().encode(request.data)) + logger.info("[%s]data = %s", fun_name(), ret) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + except Exception as e: + logger.error("Error occurred in LCM notification.") + raise e + return Response(data=None, status=ret[2]) + +@api_view(http_method_names=['GET']) +def get_vnfpkgs(request, *args, **kwargs): + logger.info("Enter %s", fun_name()) + ret = req_by_msb("openoapi/nslcm/v1/vnfpackage", "GET") + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + resp = json.JSONDecoder().decode(ret[1]) + return Response(data=resp, status=status.HTTP_200_OK) diff --git a/gvnfmadapter/driver/pub/__init__.py b/gvnfmadapter/driver/pub/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/pub/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/pub/config/__init__.py b/gvnfmadapter/driver/pub/config/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/pub/config/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/pub/config/config.py b/gvnfmadapter/driver/pub/config/config.py new file mode 100644 index 0000000..d760979 --- /dev/null +++ b/gvnfmadapter/driver/pub/config/config.py @@ -0,0 +1,33 @@ +# Copyright 2017 ZTE Corporation. +# +# 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. + +# [MSB] +MSB_SERVICE_IP = '127.0.0.1' +MSB_SERVICE_PORT = '80' + +# [register] +REG_TO_MSB_WHEN_START = True +REG_TO_MSB_REG_URL = "/openoapi/microservices/v1/services" +REG_TO_MSB_REG_PARAM = { + "serviceName": "gvnfmdriver", + "version": "v1", + "url": "/openoapi/gvnfmdriver/v1", + "protocol": "REST", + "visualRange": "1", + "nodes": [{ + "ip": "127.0.0.1", + "port": "8484", + "ttl": 0 + }] +} diff --git a/gvnfmadapter/driver/pub/database/__init__.py b/gvnfmadapter/driver/pub/database/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/pub/database/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/pub/database/models.py b/gvnfmadapter/driver/pub/database/models.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/pub/database/models.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/pub/utils/__init__.py b/gvnfmadapter/driver/pub/utils/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/pub/utils/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/pub/utils/restcall.py b/gvnfmadapter/driver/pub/utils/restcall.py new file mode 100644 index 0000000..08f4cf3 --- /dev/null +++ b/gvnfmadapter/driver/pub/utils/restcall.py @@ -0,0 +1,95 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 sys +import traceback +import logging +import urllib2 +import uuid +import httplib2 + +from driver.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' + +logger = logging.getLogger(__name__) + + +def call_req(base_url, user, passwd, auth_type, resource, method, 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)) + ret = None + resp_status = '' + try: + full_url = combine_url(base_url, resource) + headers = {'content-type': 'application/json', 'accept': 'application/json'} + if user: + 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)) + 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)) + 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 urllib2.URLError as err: + ret = [2, str(err), resp_status] + 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] + except: + logger.error(traceback.format_exc()) + ret = [4, str(sys.exc_info()), resp_status] + + 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) + + +def combine_url(base_url, resource): + full_url = None + if base_url.endswith('/') and resource.startswith('/'): + full_url = base_url[:-1] + resource + elif base_url.endswith('/') and not resource.startswith('/'): + full_url = base_url + resource + elif not base_url.endswith('/') and resource.startswith('/'): + full_url = base_url + resource + else: + full_url = base_url + '/' + resource + return full_url diff --git a/gvnfmadapter/driver/settings.py b/gvnfmadapter/driver/settings.py new file mode 100644 index 0000000..33da552 --- /dev/null +++ b/gvnfmadapter/driver/settings.py @@ -0,0 +1,127 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 os + +import sys + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '3o-wney!99y)^h3v)0$j16l9=fdjxcb+a8g+q3tfbahcnu2b0o' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'driver.pub.database', + 'driver.interfaces' + ] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + ] + +ROOT_URLCONF = 'driver.urls' + +WSGI_APPLICATION = 'driver.wsgi.application' + + +REST_FRAMEWORK = { + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer',), + + 'DEFAULT_PARSER_CLASSES': ( + 'rest_framework.parsers.MultiPartParser', + 'rest_framework.parsers.JSONParser')} +""" +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'vmanager', + 'HOST': 'localhost', + 'USER': 'root', + 'PASSWORD':'password', + }, +} + +redis_client = redis.StrictRedis(host='127.0.0.1', port=6379, password='', db=1) +""" +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }} + +TIME_ZONE = 'UTC' + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.6/howto/static-files/ + +STATIC_URL = '/static/' + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'standard': { + 'format': '%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s', }, }, + 'filters': {}, + 'handlers': { + 'driver_handler': { + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/runtime_driver.log'), + 'formatter': 'standard', + 'maxBytes': 1024 * 1024 * 50, + 'backupCount': 5, }, }, + + 'loggers': { + 'driver': { + 'handlers': ['driver_handler'], + 'level': 'DEBUG', + 'propagate': False}, }} + +if 'test' in sys.argv: + from driver.pub.config import config + config.REG_TO_MSB_WHEN_START = False + + import platform + if platform.system() == 'Linux': + TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' + TEST_OUTPUT_VERBOSE = True + TEST_OUTPUT_DESCRIPTIONS = True + TEST_OUTPUT_DIR = 'test-reports' diff --git a/gvnfmadapter/driver/swagger/__init__.py b/gvnfmadapter/driver/swagger/__init__.py new file mode 100644 index 0000000..c7b6818 --- /dev/null +++ b/gvnfmadapter/driver/swagger/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 ZTE Corporation. +# +# 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/gvnfmadapter/driver/swagger/swagger.json b/gvnfmadapter/driver/swagger/swagger.json new file mode 100644 index 0000000..760cc91 --- /dev/null +++ b/gvnfmadapter/driver/swagger/swagger.json @@ -0,0 +1,468 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "GVNFM Driver Service rest API" + }, + "basePath": "/openoapi/{vnfmtype}/v1", + "tags": [ + { + "name": "gvnfmdriver" + } + ], + "paths": { + "/{vnfmid}/vnfs": { + "post": { + "tags": [ + "vnf instantiate" + ], + "summary": "instantiate the vnf", + "description": "", + "operationId": "instantiate_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfmid", + "in": "path", + "description": "vnfm instance id", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "description": "request parameters", + "required": true, + "schema": { + "$ref": "#/definitions/VnfRequestParams" + } + } + ], + "responses": { + "200": { + "description": "successful instantiate", + "schema": { + "$ref": "#/definitions/VnfResult" + } + }, + "404": { + "description": "the vnfm instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/{vnfmid}/vnfs/{vnfInstanceId}/terminate": { + "post": { + "tags": [ + "vnf terminate" + ], + "summary": "terminate the vnf", + "description": "", + "operationId": "terminate_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfmid", + "in": "path", + "description": "vnfm instance id", + "required": true, + "type": "string" + }, + { + "name": "vnfInstanceId", + "in": "path", + "description": "vnf instance id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful terminate", + "schema": { + "$ref": "#/definitions/VnfResult" + } + }, + "404": { + "description": "the vnfmid and vnfInstanceId are wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/{vnfmid}/vnfs/{vnfInstanceId}": { + "get": { + "tags": [ + "query vnf" + ], + "summary": "query the vnf", + "description": "", + "operationId": "query_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfmid", + "in": "path", + "description": "vnfm instance id", + "required": true, + "type": "string" + }, + { + "name": "vnfInstanceId", + "in": "path", + "description": "vnf instance id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful query", + "schema": { + "$ref": "#/definitions/returnVnfInfo" + } + }, + "404": { + "description": "the vnfmid and vnfInstanceId are wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/{vnfmid}/jobs/{jobid}": { + "get": { + "tags": [ + "operation status" + ], + "summary": "operation status", + "description": "", + "operationId": "operation_status", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfmid", + "in": "path", + "description": "vnfm instance id", + "required": true, + "type": "string" + }, + { + "name": "jobid", + "in": "path", + "description": "vnf job id", + "required": true, + "type": "string" + }, + { + "name": "responseId", + "in": "path", + "description": "vnf response id", + "required": true, + "type": "integer" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/OperationStatusInfo" + } + }, + "404": { + "description": "the vnfmid ,jobid and responseId are wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/resource/grant": { + "put": { + "tags": [ + "grant vnf" + ], + "summary": "grant the vnf", + "description": "", + "operationId": "grant_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "in": "body", + "description": "request data for grant the vnf", + "required": true, + "schema": { + "$ref": "#/definitions/RequestGrantParams" + } + } + ], + "responses": { + "201": { + "description": "successful grant", + "schema": { + "$ref": "#/definitions/responseGrantResult" + } + }, + "404": { + "description": "the request body is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/vnfs/lifecyclechangesnotification": { + "post": { + "tags": [ + "life cycle changes notification" + ], + "summary": "life cycle changes notification", + "description": "", + "operationId": "lifecyclechangesnotification", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "body", + "in": "body", + "description": "request data for grant the vnf", + "required": true, + "schema": { + "$ref": "#/definitions/RequestNotifyParams" + } + } + ], + "responses": { + "201": { + "description": "successful Notify", + "schema": { + "$ref": "#/definitions/ResponseNotifyResult" + } + }, + "404": { + "description": "the request body is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + } + }, + "definitions": { + "VnfRequestParams": { + "type": "object", + "properties": { + "vnfInstanceName": { + "type": "string" + }, + "vnfPackageId": { + "type": "string" + }, + "vnfDescriptorId": { + "type": "string" + }, + "additionalParam": { + "type": "object", + "properties": { + "sdncontroller": { + "type": "string" + }, + "NatIpRange": { + "type": "string" + }, + "m6000_mng_ip": { + "type": "string" + }, + "externalPluginManageNetworkName": { + "type": "string" + }, + "location": { + "type": "string" + }, + "externalManageNetworkName": { + "type": "string" + }, + "sfc_data_network": { + "type": "string" + }, + "externalDataNetworkName": { + "type": "string" + }, + "inputs": { + "type": "object" + } + } + } + } + }, + "VnfResult": { + "type": "object", + "properties": { + "vnfInstanceId": { + "type": "string" + }, + "jobId": { + "type": "string" + } + } + }, + "returnVnfInfo": { + "type": "object", + "properties": { + "vnfInfo": { + "type": "object", + "properties": { + "nfInstanceId": { + "type": "string" + }, + "vnfStatus": { + "type": "string" + }, + "version": { + "type": "string" + } + } + } + } + }, + "OperationStatusInfo": { + "type": "object", + "properties": { + "responsedescriptor": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "responsehistorylist": { + "type": "array" + }, + "responseid": { + "type": "integer" + }, + "errorcode": { + "type": "string" + }, + "progress": { + "type": "integer" + }, + "statusdescription": { + "type": "string" + } + } + }, + "jobid": { + "type": "string" + } + } + }, + "RequestGrantParams": { + "type": "object", + "properties": { + "vnfmid": { + "type": "string" + }, + "nfvoid": { + "type": "string" + }, + "vimid": { + "type": "string" + }, + "exvimidlist": { + "type": "array" + }, + "tenant": { + "type": "string" + }, + "vnfistanceid": { + "type": "string" + }, + "operationright": { + "type": "string" + }, + "vmlist": { + "type": "array" + } + } + }, + "responseGrantResult": { + "type": "object", + "properties": { + "vimid": { + "type": "string" + }, + "tenant": { + "type": "string" + } + } + }, + "RequestNotifyParams": { + "type": "object", + "properties": { + "nfvoid": { + "type": "string" + }, + "vnfmid": { + "type": "string" + }, + "vimid": { + "type": "string" + }, + "timestamp": { + "type": "string" + }, + "vnfinstanceid": { + "type": "string" + }, + "eventtype": { + "type": "string" + }, + "vmlist": { + "type": "array" + } + } + }, + "ResponseNotifyResult": { + "type": "object" + } + } +}
\ No newline at end of file diff --git a/gvnfmadapter/driver/swagger/tests.py b/gvnfmadapter/driver/swagger/tests.py new file mode 100644 index 0000000..a2e0b9d --- /dev/null +++ b/gvnfmadapter/driver/swagger/tests.py @@ -0,0 +1,31 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 unittest +import json +from django.test import Client +from rest_framework import status + + +class SwaggerViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_sample(self): + response = self.client.get("/openoapi/gvnfmdriver/v1/swagger.json") + self.assertEqual(status.HTTP_200_OK, response.status_code, response.content) + diff --git a/gvnfmadapter/driver/swagger/urls.py b/gvnfmadapter/driver/swagger/urls.py new file mode 100644 index 0000000..d482557 --- /dev/null +++ b/gvnfmadapter/driver/swagger/urls.py @@ -0,0 +1,20 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 driver.swagger import views + +urlpatterns = [ + url(r'^openoapi/gvnfmdriver/v1/swagger.json$', views.SwaggerView.as_view()), +] diff --git a/gvnfmadapter/driver/swagger/views.py b/gvnfmadapter/driver/swagger/views.py new file mode 100644 index 0000000..e9c9604 --- /dev/null +++ b/gvnfmadapter/driver/swagger/views.py @@ -0,0 +1,29 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 os +import json +from rest_framework.views import APIView +from rest_framework.response import Response + + +class SwaggerView(APIView): + """ + Show rest api swagger. + """ + def get(self, request, format=None): + json_file = os.path.join(os.path.dirname(__file__), 'swagger.json') + f = open(json_file) + json_data = json.JSONDecoder().decode(f.read()) + f.close() + return Response(json_data) diff --git a/gvnfmadapter/driver/urls.py b/gvnfmadapter/driver/urls.py new file mode 100644 index 0000000..86f1fc3 --- /dev/null +++ b/gvnfmadapter/driver/urls.py @@ -0,0 +1,26 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 driver.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM +from django.conf.urls import include, url +urlpatterns = [ + url(r'^', include('driver.interfaces.urls')), + url(r'^', include('driver.swagger.urls')), +] + +# regist to MSB when startup +if REG_TO_MSB_WHEN_START: + import json + from driver.pub.utils.restcall import req_by_msb + req_by_msb(REG_TO_MSB_REG_URL, "POST", json.JSONEncoder().encode(REG_TO_MSB_REG_PARAM)) diff --git a/gvnfmadapter/driver/wsgi.py b/gvnfmadapter/driver/wsgi.py new file mode 100644 index 0000000..1008e32 --- /dev/null +++ b/gvnfmadapter/driver/wsgi.py @@ -0,0 +1,22 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "driver.settings") + +application = get_wsgi_application() diff --git a/gvnfmadapter/initialize.sh b/gvnfmadapter/initialize.sh new file mode 100644 index 0000000..7ace382 --- /dev/null +++ b/gvnfmadapter/initialize.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright 2017 ZTE Corporation. +# +# 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. +pip install -r requirements.txt diff --git a/gvnfmadapter/logs/empty.txt b/gvnfmadapter/logs/empty.txt new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/gvnfmadapter/logs/empty.txt diff --git a/gvnfmadapter/manage.py b/gvnfmadapter/manage.py new file mode 100644 index 0000000..383c71a --- /dev/null +++ b/gvnfmadapter/manage.py @@ -0,0 +1,23 @@ +# Copyright 2017 ZTE Corporation. +# +# 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 os +import sys + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "driver.settings") + +# load initial configuration +if __name__ == "__main__": + from django.core.management import execute_from_command_line + execute_from_command_line(sys.argv) diff --git a/gvnfmadapter/pom.xml b/gvnfmadapter/pom.xml new file mode 100644 index 0000000..6424504 --- /dev/null +++ b/gvnfmadapter/pom.xml @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<!-- + Copyright 2017 ZTE Corporation. + + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.openo.nfvo</groupId> + <artifactId>nfvo-root</artifactId> + <version>1.1.0-SNAPSHOT</version> + <relativePath>../../../../pom.xml</relativePath> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.openo.nfvo</groupId> + <artifactId>drivers-vnfm-gvnfm-gvnfmadapter</artifactId> + <version>1.1.0-SNAPSHOT</version> + <packaging>pom</packaging> + <name>nfvo/drivers/vnfm/gvnfm/gvnfmadapter</name> + <description>nfvo drivers-vnfm-gvnfm-gvnfmadapter</description> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <appendAssemblyId>false</appendAssemblyId> + <descriptors> + <descriptor>assembly.xml</descriptor> + </descriptors> + </configuration> + <executions> + <execution> + <id>make-assembly</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/gvnfmadapter/requirements.txt b/gvnfmadapter/requirements.txt new file mode 100644 index 0000000..49f0e42 --- /dev/null +++ b/gvnfmadapter/requirements.txt @@ -0,0 +1,11 @@ +# rest framework +Django==1.9.6 +djangorestframework==3.3.3 + +# for call rest api +httplib2==0.9.2 + +# for unit test +coverage==4.2 +mock==2.0.0 +unittest_xml_reporting==1.12.0 diff --git a/gvnfmadapter/run.sh b/gvnfmadapter/run.sh new file mode 100644 index 0000000..7600abb --- /dev/null +++ b/gvnfmadapter/run.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright 2017 ZTE Corporation. +# +# 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. +nohup python manage.py runserver 127.0.0.1:8484 > /dev/null & diff --git a/gvnfmadapter/stop.sh b/gvnfmadapter/stop.sh new file mode 100644 index 0000000..26efe30 --- /dev/null +++ b/gvnfmadapter/stop.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Copyright 2017 ZTE Corporation. +# +# 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. +ps auxww | grep 'manage.py runserver 127.0.0.1:8484' | awk '{print $2}' | xargs kill -9 diff --git a/gvnfmadapter/tox.ini b/gvnfmadapter/tox.ini new file mode 100644 index 0000000..4e62baf --- /dev/null +++ b/gvnfmadapter/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py27 +skipsdist = true + +[testenv] +deps = -r{toxinidir}/requirements.txt +commands = coverage run --branch manage.py test |