diff options
30 files changed, 2461 insertions, 0 deletions
diff --git a/zte/vmanager/.idea/workspace.xml b/zte/vmanager/.idea/workspace.xml new file mode 100644 index 00000000..68e827d7 --- /dev/null +++ b/zte/vmanager/.idea/workspace.xml @@ -0,0 +1,262 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ChangeListManager"> + <list default="true" id="6d1646d8-eb46-431d-9ff7-452938d22ec7" name="Default" comment="" /> + <ignored path="vmanager.iws" /> + <ignored path=".idea/workspace.xml" /> + <ignored path=".idea/dataSources.local.xml" /> + <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" /> + <option name="TRACKING_ENABLED" value="true" /> + <option name="SHOW_DIALOG" value="false" /> + <option name="HIGHLIGHT_CONFLICTS" value="true" /> + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> + <option name="LAST_RESOLUTION" value="IGNORE" /> + </component> + <component name="ChangesViewManager" flattened_view="true" show_ignored="false" /> + <component name="CreatePatchCommitExecutor"> + <option name="PATCH_PATH" value="" /> + </component> + <component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> + <component name="FavoritesManager"> + <favorites_list name="vmanager" /> + </component> + <component name="FileEditorManager"> + <leaf> + <file leaf-file-name="views.py" pinned="false" current-in-tab="true"> + <entry file="file://$PROJECT_DIR$/driver/interfaces/views.py"> + <provider selected="true" editor-type-id="text-editor"> + <state relative-caret-position="210"> + <caret line="120" column="37" selection-start-line="120" selection-start-column="37" selection-end-line="120" selection-end-column="37" /> + <folding /> + </state> + </provider> + </entry> + </file> + </leaf> + </component> + <component name="IdeDocumentHistory"> + <option name="CHANGED_PATHS"> + <list> + <option value="$PROJECT_DIR$/driver/interfaces/views.py" /> + </list> + </option> + </component> + <component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" /> + <component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER" /> + <component name="JsGulpfileManager"> + <detection-done>true</detection-done> + <sorting>DEFINITION_ORDER</sorting> + </component> + <component name="ProjectFrameBounds"> + <option name="x" value="-8" /> + <option name="y" value="-8" /> + <option name="width" value="1612" /> + <option name="height" value="876" /> + </component> + <component name="ProjectLevelVcsManager" settingsEditedManually="false"> + <OptionsSetting value="true" id="Add" /> + <OptionsSetting value="true" id="Remove" /> + <OptionsSetting value="true" id="Checkout" /> + <OptionsSetting value="true" id="Update" /> + <OptionsSetting value="true" id="Status" /> + <OptionsSetting value="true" id="Edit" /> + <ConfirmationsSetting value="0" id="Add" /> + <ConfirmationsSetting value="0" id="Remove" /> + </component> + <component name="ProjectView"> + <navigator currentView="ProjectPane" proportions="" version="1"> + <flattenPackages /> + <showMembers /> + <showModules /> + <showLibraryContents /> + <hideEmptyPackages /> + <abbreviatePackageNames /> + <autoscrollToSource /> + <autoscrollFromSource /> + <sortByType /> + <manualOrder /> + <foldersAlwaysOnTop value="true" /> + </navigator> + <panes> + <pane id="Scratches" /> + <pane id="Scope" /> + <pane id="ProjectPane"> + <subPane> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="driver" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + </PATH> + <PATH> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="vmanager" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="driver" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + <PATH_ELEMENT> + <option name="myItemId" value="interfaces" /> + <option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> + </PATH_ELEMENT> + </PATH> + </subPane> + </pane> + </panes> + </component> + <component name="PropertiesComponent"> + <property name="options.splitter.main.proportions" value="0.3" /> + <property name="options.lastSelected" value="preferences.sourceCode" /> + <property name="options.splitter.details.proportions" value="0.2" /> + <property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" /> + <property name="settings.editor.splitter.proportion" value="0.2" /> + <property name="last_opened_file_path" value="$PROJECT_DIR$" /> + <property name="WebServerToolWindowFactoryState" value="false" /> + </component> + <component name="RunManager" selected="Django server.vmanager"> + <configuration default="true" type="Python.DjangoServer" factoryName="Django server"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs> + <env name="PYTHONUNBUFFERED" value="1" /> + </envs> + <option name="SDK_HOME" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="IS_MODULE_SDK" value="false" /> + <option name="ADD_CONTENT_ROOTS" value="true" /> + <option name="ADD_SOURCE_ROOTS" value="true" /> + <module name="vmanager" /> + <option name="launchJavascriptDebuger" value="false" /> + <option name="port" value="8000" /> + <option name="host" value="" /> + <option name="additionalOptions" value="" /> + <option name="browserUrl" value="" /> + <option name="runTestServer" value="false" /> + <option name="runNoReload" value="false" /> + <option name="useCustomRunCommand" value="false" /> + <option name="customRunCommand" value="" /> + <method /> + </configuration> + <configuration default="false" name="vmanager" type="Python.DjangoServer" factoryName="Django server"> + <option name="INTERPRETER_OPTIONS" value="" /> + <option name="PARENT_ENVS" value="true" /> + <envs> + <env name="PYTHONUNBUFFERED" value="1" /> + </envs> + <option name="SDK_HOME" value="" /> + <option name="WORKING_DIRECTORY" value="" /> + <option name="IS_MODULE_SDK" value="false" /> + <option name="ADD_CONTENT_ROOTS" value="true" /> + <option name="ADD_SOURCE_ROOTS" value="true" /> + <module name="vmanager" /> + <option name="launchJavascriptDebuger" value="false" /> + <option name="port" value="8000" /> + <option name="host" value="" /> + <option name="additionalOptions" value="" /> + <option name="browserUrl" value="" /> + <option name="runTestServer" value="false" /> + <option name="runNoReload" value="false" /> + <option name="useCustomRunCommand" value="false" /> + <option name="customRunCommand" value="" /> + <method /> + </configuration> + <list size="1"> + <item index="0" class="java.lang.String" itemvalue="Django server.vmanager" /> + </list> + </component> + <component name="ShelveChangesManager" show_recycled="false"> + <option name="remove_strategy" value="false" /> + </component> + <component name="SvnConfiguration" cleanupOnStartRun="true"> + <configuration>C:\Users\10191177\AppData\Roaming\Subversion</configuration> + </component> + <component name="TaskManager"> + <task active="true" id="Default" summary="Default task"> + <changelist id="6d1646d8-eb46-431d-9ff7-452938d22ec7" name="Default" comment="" /> + <created>1491809875990</created> + <option name="number" value="Default" /> + <option name="presentableId" value="Default" /> + <updated>1491809875990</updated> + </task> + <servers /> + </component> + <component name="ToolWindowManager"> + <frame x="-8" y="-8" width="1612" height="876" extended-state="1" /> + <editor active="false" /> + <layout> + <window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.25322166" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> + <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> + <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> + <window_info id="Python Console" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> + <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> + <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="false" content_ui="tabs" /> + <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="-1" side_tool="true" content_ui="tabs" /> + <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> + <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" /> + <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="SLIDING" type="SLIDING" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> + <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> + <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> + <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> + <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> + </layout> + </component> + <component name="Vcs.Log.UiProperties"> + <option name="RECENTLY_FILTERED_USER_GROUPS"> + <collection /> + </option> + <option name="RECENTLY_FILTERED_BRANCH_GROUPS"> + <collection /> + </option> + </component> + <component name="VcsContentAnnotationSettings"> + <option name="myLimit" value="2678400000" /> + </component> + <component name="XDebuggerManager"> + <breakpoint-manager /> + <watches-manager /> + </component> + <component name="editorHistoryManager"> + <entry file="file://$PROJECT_DIR$/driver/interfaces/views.py"> + <provider selected="true" editor-type-id="text-editor"> + <state relative-caret-position="210"> + <caret line="120" column="37" selection-start-line="120" selection-start-column="37" selection-end-line="120" selection-end-column="37" /> + <folding /> + </state> + </provider> + </entry> + </component> +</project>
\ No newline at end of file diff --git a/zte/vmanager/assembly.xml b/zte/vmanager/assembly.xml new file mode 100644 index 00000000..cd9eda87 --- /dev/null +++ b/zte/vmanager/assembly.xml @@ -0,0 +1,51 @@ +<!-- + Copyright 2016 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-svnfm-zte-vmanager</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/svnfm/zte/vmanager</baseDirectory> +</assembly> diff --git a/zte/vmanager/driver/__init__.py b/zte/vmanager/driver/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/interfaces/__init__.py b/zte/vmanager/driver/interfaces/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/interfaces/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/interfaces/tests.py b/zte/vmanager/driver/interfaces/tests.py new file mode 100644 index 00000000..0fc179fd --- /dev/null +++ b/zte/vmanager/driver/interfaces/tests.py @@ -0,0 +1,425 @@ +# Copyright 2016-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_001(self, mock_call_req): + """ + Initate_VNF + """ + vnfm_info = {u'userName': u'admin', + u'vendor': u'ZTE', + u'name': u'ZTE_VNFM_237_62', + u'vimId': u'516cee95-e8ca-4d26-9268-38e343c2e31e', + u'url': u'http://192.168.237.165:2324', + u'certificateUrl': u'', + u'version': u'V1.0', + u'vnfmId': u'b0797c9b-3da9-459c-b25c-3813e9d8fd70', + u'password': u'admin', + u'type': u'ztevmanagerdriver', + u'createTime': u'2016-10-31 11:08:39', + u'description': u''} + vnfd_info = {u'vnfInstanceInfo': [{u'vnfInstanceId': u'59b79a9f-9e37-4f6c-acaf-5f41d9cb3f76', + u'vnfInstanceName': u'VFW_59b79a9f-9e37-4f6c-acaf-5f41d9cb3f76'}, + {u'vnfInstanceId': u'6c5e4bd3-e8a6-42d8-a7a4-53a8ef74c6ac', + u'vnfInstanceName': u'VFW_6c5e4bd3-e8a6-42d8-a7a4-53a8ef74c6ac'}, + {u'vnfInstanceId': u'930de5c9-8043-41df-ace8-ede2262a3713', + u'vnfInstanceName': u'VFW_930de5c9-8043-41df-ace8-ede2262a3713'}, + {u'vnfInstanceId': u'c677a305-a7bd-4baf-9eee-c383c588bb3c', + u'vnfInstanceName': u'VFW_c677a305-a7bd-4baf-9eee-c383c588bb3c'}, + {u'vnfInstanceId': u'e708e5c5-bdf4-436a-b928-826887806d82', + u'vnfInstanceName': u'VFW_e708e5c5-bdf4-436a-b928-826887806d82'}], + u'csarId': u'd852e1be-0aac-48f1-b1a4-cd825f6cdf9a', + u'imageInfo': [ + {u'status': u'Enable', u'index': u'0', + u'vimId': u'516cee95-e8ca-4d26-9268-38e343c2e31e', + u'fileName': u'VFW_IMAGE_VCPE_ZTE', + u'vimUser': u'admin', + u'imageId': u'd2b73154-0414-466a-a1e6-51b9461b753a', + u'tenant': u'admin'}], + u'packageInfo': {u'usageState': u'NotInUse', + u'onBoardState': u'onBoarded', + u'name': u'VFW', + u'format': u'yaml', + u'provider': u'ZTE', + u'vnfdProvider': u'zte', + u'vnfdId': u'vcpe_vfw_zte_1_0', + u'deletionPending': False, + u'version': u'v1.0', + u'vnfVersion': u'1.0', + u'vnfdVersion': u'1.0.0', + u'processState': u'normal', + u'modifyTime': u'2016-10-31 16:21:32', + u'downloadUri': u'http://192.168.233.226:80/', + u'operationalState': u'Disabled', + u'createTime': u'2016-10-31 16:21:11', + u'size': u'12.1 MB'}} + packageInfo = {u'usageState': u'NotInUse', + u'onBoardState': u'onBoarded', + u'name': u'VFW', + u'format': u'yaml', + u'provider': u'ZTE', + u'vnfdProvider': u'zte', + u'vnfdId': u'vcpe_vfw_zte_1_0', + u'deletionPending': False, + u'version': u'v1.0', + u'vnfVersion': u'1.0', + u'vnfdVersion': u'1.0.0', + u'processState': u'normal', + u'modifyTime': u'2016-10-31 16:21:32', + u'downloadUri': u'http://192.168.233.226:80/files/catalog-http/NFAR/ZTE/VFW/v1.0/VFW.csar', + u'operationalState': u'Disabled', + u'createTime': u'2016-10-31 16:21:11', u'size': u'12.1 MB'} + + ret = [0, json.JSONEncoder().encode({"vnfInstanceId":"8", + "jobid":"NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9"}), + '200'] + + r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"] + + r2 = [0, json.JSONEncoder().encode(vnfd_info), "200"] + + r3 = [0, json.JSONEncoder().encode(packageInfo), "200"] + + + mock_call_req.side_effect = [r1, r2, r3, ret] + + 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/ztevnfmid/vnfs", + data=json.dumps(req_data), content_type="application/json") + self.assertEqual(str(status.HTTP_200_OK), response.status_code) + expect_resp_data = {"jobid": "NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9", "vnfInstanceId": "8"} + self.assertEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_terminate_vnf__002(self, mock_call_req): + """ + Terminate_VNF + """ + r1 = [0, json.JSONEncoder().encode({ + "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"}), "200"] + + r2 = [0, json.JSONEncoder().encode({"vnfInstanceId": "1", "JobId": "1"}), "200"] + mock_call_req.side_effect = [r1, r2] + + response = self.client.post("/openoapi/ztevnfm/v1/ztevnfmid/vnfs/vbras_innstance_id/terminate") + + self.assertEqual(str(status.HTTP_200_OK), response.status_code) + expect_resp_data = {"jobid": "1", "vnfInstanceId": "1"} + self.assertEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_query_vnf_003(self, mock_call_req): + """ + Query_VNF + """ + r1 = [0, json.JSONEncoder().encode({ + "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"}), "200"] + + r2 = [0, json.JSONEncoder().encode({"vnfinstancestatus": "1"}), "200"] + mock_call_req.side_effect = [r1, r2] + + response = self.client.get("/openoapi/ztevnfm/v1/ztevnfmid/vnfs/vbras_innstance_id") + + self.assertEqual(str(status.HTTP_200_OK), response.status_code) + + expect_resp_data = {"vnfInfo": {"vnfStatus": "1"}} + self.assertEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_operation_status_004(self, mock_call_req): + """ + Operation_status + """ + vnfm_info = {u'userName': u'admin', + u'vendor': u'ZTE', + u'name': u'ZTE_VNFM_237_62', + u'vimId': u'516cee95-e8ca-4d26-9268-38e343c2e31e', + u'url': u'http://192.168.237.165:2324', + u'certificateUrl': u'', + u'version': u'V1.0', + u'vnfmId': u'b0797c9b-3da9-459c-b25c-3813e9d8fd70', + u'password': u'admin', + u'type': u'ztevmanagerdriver', + u'createTime': u'2016-10-31 11:08:39', + u'description': u''} + resp_body = {"responsedescriptor": + {"status": "processing", "responsehistorylist": [ + {"status": "error", + "progress": 255, + "errorcode": "", + "responseid": 20, + "statusdescription": "'JsonParser' object has no attribute 'parser_info'"}], + "responseid": 21, + "errorcode": "", + "progress": 40, + "statusdescription": "Create nf apply resource failed"}, + "jobid": "NF-CREATE-11-ec6c2f2a-9f48-11e6-9405-fa163e91c2f9"} + 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/ztevmanagerdriver/v1/{vnfmid}/jobs/{jobid}?responseId={responseId}". + format( + vnfmid=vnfm_info["vnfmId"], + jobid=resp_body["jobid"], + responseId=resp_body["responsedescriptor"]["responseid"])) + + self.assertEqual(str(status.HTTP_200_OK), response.status_code) + + expect_resp_data = resp_body + self.assertDictEqual(expect_resp_data, response.data) + + @mock.patch.object(restcall, 'call_req') + def test_grantvnf_005(self, mock_call_req): + """ + Grant_VNF + """ + ret = [0, + '{"vim":{"accessinfo":{"tenant":"admin"},"vimid":"516cee95-e8ca-4d26-9268-38e343c2e31e"}}', + '201'] + + 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 = ret + response = self.client.put("/openoapi/ztevmanagerdriver/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_006(self, mock_call_req): + """ + Notification + """ + r1 = [0, json.JSONEncoder().encode({ + "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"}), "200"] + + r2 = [0, json.JSONEncoder().encode( + {"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": "" + }), "200"] + + 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"]}]} + mock_call_req.side_effect = [r1, r2] + response = self.client.post("/openoapi/ztevmanagerdriver/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_scale(self,mock_call_req): + job_info = {"jobid":"801","nfInstanceId":"101"} + vnfm_info = {u'userName': u'admin', + u'vendor': u'ZTE', + u'name': u'ZTE_VNFM_237_62', + u'vimId': u'516cee95-e8ca-4d26-9268-38e343c2e31e', + u'url': u'http://192.168.237.165:2324', + u'certificateUrl': u'', + u'version': u'V1.0', + u'vnfmId': u'b0797c9b-3da9-459c-b25c-3813e9d8fd70', + u'password': u'admin', + u'type': u'ztevmanagerdriver', + u'createTime': u'2016-10-31 11:08:39', + u'description': u''} + + ret = [0, json.JSONEncoder().encode(job_info), "202"] + ret_vnfm = [0,json.JSONEncoder().encode(job_info), "200"] + mock_call_req.side_effect = [ret_vnfm, ret] + + vnfd_info = { + "vnf_flavours":[ + { + "flavour_id":"flavour1", + "description":"", + "vdu_profiles":[ + { + "vdu_id":"vdu1Id", + "instances_minimum_number": 1, + "instances_maximum_number": 4, + "local_affinity_antiaffinity_rule":[ + { + "affinity_antiaffinity":"affinity", + "scope":"node", + } + ] + } + ], + "scaling_aspects":[ + { + "id": "demo_aspect", + "name": "demo_aspect", + "description": "demo_aspect", + "associated_group": "elementGroup1", + "max_scale_level": 5 + } + ] + } + ], + "element_groups": [ + { + "group_id": "elementGroup1", + "description": "", + "properties":{ + "name": "elementGroup1", + }, + "members": ["gsu_vm","pfu_vm"], + } + ] + } + + scale_vnf_data = { + "type":"SCALE_OUT", + "aspectId":"demo_aspect", + "numberOfSteps":"3", + "additionalParam":{ + "vnfdModel":vnfd_info + } + } + + + response = self.client.post("/openoapi/ztevnfm/v1/vnfmid/vnfs/101/scale", + data=json.dumps(scale_vnf_data), content_type='application/json') + + self.assertEqual(str(status.HTTP_202_ACCEPTED), response.status_code) + + expect_resp_data = {"jobid":"801","nfInstanceId":"101"} + self.assertDictEqual(expect_resp_data, response.data) + ''' diff --git a/zte/vmanager/driver/interfaces/urls.py b/zte/vmanager/driver/interfaces/urls.py new file mode 100644 index 00000000..43eb40a3 --- /dev/null +++ b/zte/vmanager/driver/interfaces/urls.py @@ -0,0 +1,36 @@ +# Copyright 2016-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/resource/grant$', views.grantvnf, name='grantvnf'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfs/lifecyclechangesnotification$', views.notify, name='notify'), + url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<nfInstanceId>[0-9a-zA-Z\-\_]+)/scale$', + views.scale, name='scale'), + url(r'^samples/$', views.samples, name='samples'), ] diff --git a/zte/vmanager/driver/interfaces/views.py b/zte/vmanager/driver/interfaces/views.py new file mode 100644 index 00000000..af316f01 --- /dev/null +++ b/zte/vmanager/driver/interfaces/views.py @@ -0,0 +1,516 @@ +# Copyright 2016-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 traceback + +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 + +logger = logging.getLogger(__name__) + + +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 mapping_conv(keyword_map, rest_return): + resp_data = {} + for param in keyword_map: + if keyword_map[param]: + resp_data[keyword_map[param]] = ignorcase_get(rest_return, param) + return resp_data + + +query_vnfd_url = "openoapi/nslcm/v1/vnfpackage/%s" +query_vnfm_url = "openoapi/extsys/v1/vnfms/%s" +query_package_url = "openoapi/nslcm/v1/vnfpackage/%s" + + +# Query VNFM by VNFMID +def vnfm_get(vnfmid): + ret = req_by_msb("openoapi/extsys/v1/vnfms/%s" % vnfmid, "GET") + return ret + + +def vnfd_get(vnfpackageid): + ret = req_by_msb("openoapi/nslcm/v1/vnfpackage/%s" % vnfpackageid, "GET") + return ret + + +def vnfpackage_get(csarid): + ret = req_by_msb("openoapi/nslcm/v1/vnfpackage/%s" % csarid, "GET") + return ret + + +# ================================================== +create_vnf_url = "v1/vnfs" +create_vnf_param_mapping = { + "packageUrl": "", + "instantiateUrl": "", + "instantiationLevel": "", + "vnfInstanceName": "", + "vnfPackageId": "", + "vnfDescriptorId": "", + "flavorId": "", + "vnfInstanceDescription": "", + "extVirtualLink": "", + "additionalParam": ""} +create_vnf_resp_mapping = { + "VNFInstanceID": "vnfInstanceId", + "JobId": "jobid",} + + +@api_view(http_method_names=['POST']) +def instantiate_vnf(request, *args, **kwargs): + try: + logger.debug("[%s] request.data=%s", fun_name(), request.data) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + ret = vnfm_get(vnfm_id) + if ret[0] != 0: + return 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) + vnf_package_id = ignorcase_get(request.data, "vnfPackageId") + ret = vnfd_get(vnf_package_id) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + vnfd_info = json.JSONDecoder().decode(ret[1]) + logger.debug("[%s] vnfd_info=%s", fun_name(), vnfd_info) + csar_id = ignorcase_get(vnfd_info, "csarId") + ret = vnfpackage_get(csar_id) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + vnf_package_info = json.JSONDecoder().decode(ret[1]) + packageInfo = ignorcase_get(vnf_package_info, "packageInfo") + logger.debug("[%s] packageInfo=%s", fun_name(), packageInfo) + data = {} + data["NFVOID"] = 1 + data["VNFMID"] = vnfm_id + # data["VNFD"] = ignorcase_get(packageInfo, "downloadUri") + # data["VNFURL"] = ignorcase_get(packageInfo, "downloadUri") + vnfdId = ignorcase_get(packageInfo, "vnfdId") + from urlparse import urlparse + vnfm_ip = urlparse(ignorcase_get(vnfm_info, "url")).netloc.split(':')[0] + if "vCSCF_NF_ZTE" == vnfdId: + data["VNFD"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/CSCF" + data["VNFURL"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/CSCF" + elif "vPCRF_NF_ZTE" == vnfdId: + data["VNFD"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/PCRF" + data["VNFURL"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/PCRF" + elif "vTAS_NF_ZTE" == vnfdId: + data["VNFD"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/SSS" + data["VNFURL"] = "ftp://VMVERSION:vmversion@" + "192.168.128.159" + ":21/SSS" + elif "vSPGW_NF_ZTE" == vnfdId: + data["VNFD"] = "ftp://VMVERSION:vmversion@" + "192.168.237.159" + ":21/vgw31" + data["VNFURL"] = "ftp://VMVERSION:vmversion@" + "192.168.237.159" + ":21/vgw31" + else: + data["VNFD"] = ignorcase_get(packageInfo, "downloadUri") + data["VNFURL"] = ignorcase_get(packageInfo, "downloadUri") + + data["extension"] = {} + inputs = [] + for name, value in ignorcase_get(ignorcase_get(request.data, "additionalParam"), "inputs").items(): + inputs.append({"name": name, "value": value}) + + logger.info( + "ignorcase_get(request.data, \"additionalParam\") = %s" % ignorcase_get(request.data, "additionalParam")) + data["extension"]["inputs"] = json.dumps(inputs) + data["extension"]["extVirtualLinks"] = ignorcase_get( + ignorcase_get(request.data, "additionalParam"), "extVirtualLinks") + data["extension"]["vnfinstancename"] = ignorcase_get(request.data, "vnfInstanceName") + data["extension"]["vnfid"] = data["VNFD"] + data["extension"]["multivim"] = 0 + logger.debug("[%s] call_req data=%s", fun_name(), data) + 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=create_vnf_url, + method='post', + content=json.JSONEncoder().encode(data)) + logger.debug("[%s] call_req ret=%s", fun_name(), ret) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + resp_data = mapping_conv(create_vnf_resp_mapping, resp) + logger.info("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when instantiating VNF") + raise e + return Response(data=resp_data, status=ret[2]) + + +# ================================================== +vnf_delete_url = "v1/vnfs/%s" +vnf_delete_param_mapping = { + "terminationType": "terminationType", + "gracefulTerminationTimeout": "gracefulTerminationTimeout"} +vnf_delete_resp_mapping = { + "vnfInstanceId": "vnfInstanceId", + "JobId": "jobid"} + + +@api_view(http_method_names=['POST']) +def terminate_vnf(request, *args, **kwargs): + try: + logger.debug("[%s] request.data=%s", fun_name(), request.data) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + ret = vnfm_get(vnfm_id) + if ret[0] != 0: + return 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) + data = {} + logger.debug("[%s]req_data=%s", fun_name(), data) + 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=vnf_delete_url % (ignorcase_get(kwargs, "vnfInstanceID")), + method='delete', + content=json.JSONEncoder().encode(data)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + resp_data = mapping_conv(vnf_delete_resp_mapping, resp) + logger.debug("[%s]resp_data=%s", fun_name(), resp_data) + except Exception as e: + logger.error("Error occurred when terminating VNF") + raise e + return Response(data=resp_data, status=ret[2]) + + +# ================================================== + + +vnf_detail_url = "v1/vnfs/%s" +vnf_detail_resp_mapping = { + "VNFInstanseStatus": "status",} + + +@api_view(http_method_names=['GET']) +def query_vnf(request, *args, **kwargs): + try: + logger.debug("[%s] request.data=%s", fun_name(), request.data) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + ret = vnfm_get(vnfm_id) + if ret[0] != 0: + return 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) + data = {} + 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=vnf_detail_url % (ignorcase_get(kwargs, "vnfInstanceID")), + method='get', + content=json.JSONEncoder().encode(data)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + resp = json.JSONDecoder().decode(ret[1]) + vnf_status = ignorcase_get(resp, "vnfinstancestatus") + resp_data = {"vnfInfo": {"vnfStatus": vnf_status}} + 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=ret[2]) + + +# Get Operation Status +operation_status_url = '/v1/jobs/{jobId}?NFVOID={nfvoId}&VNFMID={vnfmId}&ResponseID={responseId}' +operation_status_resp_map = { + "JobId": "jobId", + "Status": "status", + "Progress": "progress", + "StatusDescription": "currentStep", + "ErrorCode": "errorCode", + "ResponseId": "responseId", + "ResponseHistoryList": "responseHistoryList", + "ResponseDescriptor": "responseDescriptor",} + + +@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") + ret = vnfm_get(vnfm_id) + if ret[0] != 0: + return 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) + 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=operation_status_url.format(jobId=ignorcase_get(kwargs, 'jobid'), nfvoId=1, + vnfmId=ignorcase_get(kwargs, 'vnfmid'), + responseId=ignorcase_get(request.GET, 'responseId')), + method='get', + content=json.JSONEncoder().encode(data)) + + 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) + except Exception as e: + logger.error("Error occurred when getting operation status information.") + raise e + return Response(data=resp_data, status=ret[2]) + + +# Grant VNF Lifecycle Operation +grant_vnf_url = 'openoapi/nslcm/v1/ns/grantvnf' +grant_vnf_param_map = { + "VNFMID": "", + "NFVOID": "", + "VIMID": "", + "ExVIMIDList": "", + "ExVIMID": "", + "Tenant": "", + "VNFInstanceID": "vnfInstanceId", + "OperationRight": "", + "VMList": "", + "VMFlavor": "", + "VMNumber": ""} + + +@api_view(http_method_names=['PUT']) +def grantvnf(request, *args, **kwargs): + logger.info("=====grantvnf=====") + try: + resp_data = {} + logger.info("req_data = %s", request.data) + data = mapping_conv(grant_vnf_param_map, request.data) + logger.info("grant_vnf_url = %s", grant_vnf_url) + data["vnfDescriptorId"] = "" + if ignorcase_get(request.data, "operationright") == 0: + data["lifecycleOperation"] = "Instantiate" + data["addresource"] = [] + for vm in ignorcase_get(request.data, "vmlist"): + for i in range(int(ignorcase_get(vm, "vmnumber"))): + data["addresource"].append( + {"type": "vdu", + "resourceDefinitionId": i, + "vdu": ignorcase_get(vm, "vmflavor"), + "vimid": ignorcase_get(vm, "vimid"), + "tenant": ignorcase_get(vm, "tenant") + }) + + data["additionalparam"] = {} + data["additionalparam"]["vnfmid"] = ignorcase_get(request.data, "vnfmid") + data["additionalparam"]["vimid"] = ignorcase_get(request.data, "vimid") + data["additionalparam"]["tenant"] = ignorcase_get(request.data, "tenant") + + logger.info("data = %s", data) + ret = req_by_msb(grant_vnf_url, "POST", content=json.JSONEncoder().encode(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]) + + +# Notify LCM Events +notify_url = 'openoapi/nslcm/v1/ns/{vnfmid}/vnfs/{vnfInstanceId}/Notify' +notify_param_map = { + "NFVOID": "", + "VNFMID": "VNFMID", + "VIMID": "vimid", + "VNFInstanceID": "vnfInstanceId", + "TimeStamp": "", + "EventType": "operation", + "VMList": "", + "VMFlavor": "", + "VMNumber": "", + "VMIDlist": "", + "VMUUID": "",} + + +@api_view(http_method_names=['POST']) +def notify(request, *args, **kwargs): + try: + logger.info("[%s]req_data = %s", fun_name(), request.data) + data = mapping_conv(notify_param_map, request.data) + logger.info("[%s]data = %s", fun_name(), data) + + data["status"] = "result" + data["jobId"] = "notMust" + data["affectedVnfc"] = [] + data["affectedVl"] = [] + data["affectedVirtualStorage"] = [] + data["affectedCp"] = [] + + affectedvnfcs = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedvnfc") + affectedvls = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedvl") + affectedcps = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedcp") + vnfdmodule = ignorcase_get(ignorcase_get(request.data, "extension"), "vnfdmodule") + + data["vnfdmodule"] = vnfdmodule + + for affectedvnfc in affectedvnfcs: + data["affectedVnfc"].append({ + "vnfcInstanceId": ignorcase_get(affectedvnfc, "vnfcinstanceid"), + "vduId": ignorcase_get(affectedvnfc, "vduId"), + "changeType": ignorcase_get(affectedvnfc, "changeType"), + "vimid": ignorcase_get(ignorcase_get(affectedvnfc, "computeresource"), "vimid"), + "vmId": ignorcase_get(ignorcase_get(affectedvnfc, "computeresource"), "resourceid"), + "vmName": ignorcase_get(ignorcase_get(affectedvnfc, "computeresource"), "resourcename") + }) + + for affectedvl in affectedvls: + data["affectedVl"].append({ + "vlInstanceId": ignorcase_get(affectedvl, "virtuallinkinstanceid"), + "vimid": ignorcase_get(ignorcase_get(affectedvl, "networkresource"), "vimid"), + "vldid": ignorcase_get(affectedvl, "virtuallinkdescid"), + "vllid": ignorcase_get(ignorcase_get(affectedvl, "networkresource"), "resourceid"), + "vlName": ignorcase_get(ignorcase_get(affectedvl, "networkresource"), "resourcename") + }) + + for affectedcp in affectedcps: + data["affectedCp"].append(affectedcp) + # { + # "virtualLinkInstanceId": ignorcase_get(affectedcp, "virtuallinkinstanceid"), + # "ownerId": ignorcase_get(affectedcp, "ownerId"), + # "ownerType": ignorcase_get(affectedcp, "ownerType") + # } + ret = req_by_msb(notify_url.format(vnfmid=ignorcase_get(data, 'VNFMID'), + vnfInstanceId=ignorcase_get(data, 'vnfinstanceid')), + "POST", content=json.JSONEncoder().encode(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]) + + +nf_scaling_url = '/v1/vnfs/{vnfInstanceID}/scale' + + +@api_view(http_method_names=['POST']) +def scale(request, *args, **kwargs): + logger.info("====scale_vnf===") + try: + logger.info("request.data = %s", request.data) + logger.info("requested_url = %s", request.get_full_path()) + vnfm_id = ignorcase_get(kwargs, "vnfmid") + nf_instance_id = ignorcase_get(kwargs, "nfInstanceId") + ret = vnfm_get(vnfm_id) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=ret[2]) + vnfm_info = json.JSONDecoder().decode(ret[1]) + scale_type = ignorcase_get(request.data, "type") + aspect_id = ignorcase_get(request.data, "aspectId") + number_of_steps = ignorcase_get(request.data, "numberOfSteps") + extension = ignorcase_get(request.data, "additionalParam") + vnfd_model = ignorcase_get(extension, "vnfdModel") + data = { + 'vnfmid': vnfm_id, + 'nfvoid': 1, + 'scaletype': '0' if scale_type == 'SCALE_OUT' else '1', + 'vmlist': [{'VMNumber':number_of_steps,'VMFlavor':aspect_id}], + 'extension':'' + } + ''' + for vdu_id in get_vdus(vnfd_model, aspect_id): + data['vmlist'].append({ + "VMFlavor": vdu_id, + "VMNumber": number_of_steps + }) + ''' + logger.info("data = %s", data) + 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=nf_scaling_url.format(vnfInstanceID=nf_instance_id), + method='put', # POST + content=json.JSONEncoder().encode(data)) + logger.info("ret=%s", ret) + if ret[0] != 0: + return Response(data={'error':'scale error'}, status=ret[2]) + resp_data = json.JSONDecoder().decode(ret[1]) + jobId = resp_data["jobid"] + logger.info("resp_data=%s", resp_data) + except Exception as e: + logger.error("Error occurred when scaling VNF") + logger.error(traceback.format_exc()) + return Response(data={'error':'scale expection'}, status='500') + return Response(data=resp_data, status=ret[2]) + + +#@staticmethod +def get_vdus(nf_model, aspect_id): + associated_group = '' + members = [] + vnf_flavours = nf_model['vnf_flavours'] + for vnf_flaour in vnf_flavours: + scaling_aspects = vnf_flaour['scaling_aspects'] + for aspect in scaling_aspects: + if aspect_id == aspect['id']: + associated_group = aspect['associated_group'] + break + if not associated_group: + logger.error('Cannot find the corresponding element group') + raise Exception('Cannot find the corresponding element group') + for element_group in nf_model['element_groups']: + if element_group['group_id'] == associated_group: + members = element_group['members'] + if not members: + logger.error('Cannot find the corresponding members') + raise Exception('Cannot find the corresponding members') + return members + + +@api_view(http_method_names=['GET']) +def samples(request, *args, **kwargs): + return Response(data={"status": "ok"}) diff --git a/zte/vmanager/driver/pub/__init__.py b/zte/vmanager/driver/pub/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/pub/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/pub/config/__init__.py b/zte/vmanager/driver/pub/config/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/pub/config/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/pub/config/config.py b/zte/vmanager/driver/pub/config/config.py new file mode 100644 index 00000000..206e851a --- /dev/null +++ b/zte/vmanager/driver/pub/config/config.py @@ -0,0 +1,33 @@ +# Copyright 2016 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": "ztevmanagerdriver", + "version": "v1", + "url": "/openoapi/ztevmanagerdriver/v1", + "protocol": "REST", + "visualRange": "1", + "nodes": [{ + "ip": "127.0.0.1", + "port": "8410", + "ttl": 0 + }] +} diff --git a/zte/vmanager/driver/pub/database/__init__.py b/zte/vmanager/driver/pub/database/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/pub/database/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/pub/database/models.py b/zte/vmanager/driver/pub/database/models.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/pub/database/models.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/pub/utils/__init__.py b/zte/vmanager/driver/pub/utils/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/zte/vmanager/driver/pub/utils/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/zte/vmanager/driver/pub/utils/restcall.py b/zte/vmanager/driver/pub/utils/restcall.py new file mode 100644 index 00000000..ffde14da --- /dev/null +++ b/zte/vmanager/driver/pub/utils/restcall.py @@ -0,0 +1,95 @@ +# Copyright 2016 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/zte/vmanager/driver/settings.py b/zte/vmanager/driver/settings.py new file mode 100644 index 00000000..8fec618f --- /dev/null +++ b/zte/vmanager/driver/settings.py @@ -0,0 +1,124 @@ +# Copyright 2016 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: + 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/zte/vmanager/driver/swagger/__init__.py b/zte/vmanager/driver/swagger/__init__.py new file mode 100644 index 00000000..c7b6818e --- /dev/null +++ b/zte/vmanager/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/zte/vmanager/driver/swagger/swagger.json b/zte/vmanager/driver/swagger/swagger.json new file mode 100644 index 00000000..c15aa313 --- /dev/null +++ b/zte/vmanager/driver/swagger/swagger.json @@ -0,0 +1,545 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "ZTE VNFM Driver Service rest API" + }, + "basePath": "/openoapi/{vnfmtype}/v1", + "tags": [ + { + "name": "ztevmanagerdriver" + } + ], + "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 operation", + "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 operation", + "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 operation", + "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" + } + } + } + }, + "/{vnfmid}/vnfs/{nfInstanceId}/scale": { + "post": { + "tags": [ + "scale vnf" + ], + "summary": "scale vnf", + "description": "", + "operationId": "scale_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfmid", + "in": "path", + "description": "vnfm instance id", + "required": true, + "type": "string" + }, + { + "name": "nfInstanceId", + "in": "path", + "description": "nf instance id", + "required": true, + "type": "string" + }, + { + "name": "body", + "in": "body", + "description": "request data for grant the vnf", + "required": true, + "schema": { + "$ref": "#/definitions/RequestScaleParams" + } + } + ], + "responses": { + "201": { + "description": "successful scale", + "schema": { + "$ref": "#/definitions/ResponseScaleResult" + } + }, + "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": { + "vnfStatus": { + "type": "string" + } + } + } + } + }, + "OperationStatusInfo": { + "type": "object", + "properties": { + "responsedescriptor": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "responsehistorylist": { + "type": "string" + }, + "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": "string" + }, + "tenant": { + "type": "string" + }, + "vnfistanceid": { + "type": "string" + }, + "operationright": { + "type": "string" + }, + "vmlist": { + "type": "string" + } + } + }, + "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": "string" + } + } + }, + "ResponseNotifyResult": { + "type": "object" + }, + "RequestScaleParams": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "aspectId": { + "type": "string" + }, + "numberOfSteps": { + "type": "string" + }, + "additionalParam": { + "type": "object" + } + } + }, + "ResponseScaleResult": { + "type": "object", + "properties": { + "jobid": { + "type": "string" + }, + "nfInstanceId": { + "type": "string" + } + } + } + } +}
\ No newline at end of file diff --git a/zte/vmanager/driver/swagger/tests.py b/zte/vmanager/driver/swagger/tests.py new file mode 100644 index 00000000..2bc8f8c0 --- /dev/null +++ b/zte/vmanager/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/ztevmanagerdriver/v1/swagger.json") + self.assertEqual(status.HTTP_200_OK, response.status_code, response.content) + diff --git a/zte/vmanager/driver/swagger/urls.py b/zte/vmanager/driver/swagger/urls.py new file mode 100644 index 00000000..48378707 --- /dev/null +++ b/zte/vmanager/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/ztevmanagerdriver/v1/swagger.json$', views.SwaggerView.as_view()), +] diff --git a/zte/vmanager/driver/swagger/views.py b/zte/vmanager/driver/swagger/views.py new file mode 100644 index 00000000..e9c96043 --- /dev/null +++ b/zte/vmanager/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/zte/vmanager/driver/urls.py b/zte/vmanager/driver/urls.py new file mode 100644 index 00000000..94164d1b --- /dev/null +++ b/zte/vmanager/driver/urls.py @@ -0,0 +1,26 @@ +# Copyright 2016 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/zte/vmanager/driver/wsgi.py b/zte/vmanager/driver/wsgi.py new file mode 100644 index 00000000..68fc46c6 --- /dev/null +++ b/zte/vmanager/driver/wsgi.py @@ -0,0 +1,22 @@ +# Copyright 2016 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/zte/vmanager/initialize.sh b/zte/vmanager/initialize.sh new file mode 100644 index 00000000..8f6a0003 --- /dev/null +++ b/zte/vmanager/initialize.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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/zte/vmanager/logs/empty.txt b/zte/vmanager/logs/empty.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/zte/vmanager/logs/empty.txt diff --git a/zte/vmanager/manage.py b/zte/vmanager/manage.py new file mode 100644 index 00000000..401f8008 --- /dev/null +++ b/zte/vmanager/manage.py @@ -0,0 +1,23 @@ +# Copyright 2016 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/zte/vmanager/pom.xml b/zte/vmanager/pom.xml new file mode 100644 index 00000000..b61939c3 --- /dev/null +++ b/zte/vmanager/pom.xml @@ -0,0 +1,53 @@ +<?xml version="1.0"?> +<!-- + Copyright 2016 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-svnfm-zte-vmanager</artifactId> + <version>1.1.0-SNAPSHOT</version> + <packaging>pom</packaging> + <name>nfvo/drivers/vnfm/svnfm/zte/vmanager</name> + <description>nfvo drivers-vnfm-svnfm-zte-vmanager</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/zte/vmanager/requirements.txt b/zte/vmanager/requirements.txt new file mode 100644 index 00000000..49f0e42e --- /dev/null +++ b/zte/vmanager/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/zte/vmanager/run.sh b/zte/vmanager/run.sh new file mode 100644 index 00000000..8c0b4fa6 --- /dev/null +++ b/zte/vmanager/run.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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. +sip=127.0.0.1 +nohup python manage.py runserver $sip:8410 > /dev/null & diff --git a/zte/vmanager/stop.sh b/zte/vmanager/stop.sh new file mode 100644 index 00000000..528da95c --- /dev/null +++ b/zte/vmanager/stop.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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. +sip=127.0.0.1 +ps auxww | grep "manage.py runserver $sip:8410" | awk '{print $2}' | xargs kill -9 diff --git a/zte/vmanager/tox.ini b/zte/vmanager/tox.ini new file mode 100644 index 00000000..4e62baf4 --- /dev/null +++ b/zte/vmanager/tox.ini @@ -0,0 +1,7 @@ +[tox] +envlist = py27 +skipsdist = true + +[testenv] +deps = -r{toxinidir}/requirements.txt +commands = coverage run --branch manage.py test |