Merge "Add docker image and build for NFVO/LCM"
authorFu Jinhua <fu.jinhua@zte.com.cn>
Fri, 1 Sep 2017 01:18:08 +0000 (01:18 +0000)
committerGerrit Code Review <gerrit@onap.org>
Fri, 1 Sep 2017 01:18:08 +0000 (01:18 +0000)
14 files changed:
lcm/ns/tests/test_ns_heal.py
lcm/ns/tests/test_ns_instant.py
lcm/ns/tests/tests_ns_terminate.py
lcm/pub/msapi/aai.py
lcm/pub/msapi/sdc.py
lcm/pub/utils/restcall.py
lcm/pub/utils/toscaparser/__init__.py
lcm/pub/utils/toscaparser/basemodel.py
lcm/pub/utils/toscaparser/convert.py [deleted file]
lcm/pub/utils/toscaparser/dataentityext.py
lcm/pub/utils/toscaparser/nsdmodel.py
lcm/pub/utils/toscaparser/parser.py [deleted file]
lcm/pub/utils/toscaparser/vnfdmodel.py
requirements.txt

index 0d77dbc..e47f5f4 100644 (file)
@@ -97,10 +97,6 @@ class TestHealNsViews(TestCase):
         NSHealService(self.ns_inst_id, data, self.job_id).run()
         self.assertEqual(NSInstModel.objects.get(id=self.ns_inst_id).status, NS_INST_STATUS.HEALING)
 
-    def test_swagger_ok(self):
-        resp = self.client.get("/api/nslcm/v1/swagger.json", format='json')
-        self.assertEqual(resp.status_code, status.HTTP_200_OK)
-
     @mock.patch.object(NSHealService, "start")
     def test_ns_heal_non_existing_ns(self, mock_start):
         mock_start.side_effect = NSLCMException("NS Not Found")
index 955284c..77fb7f3 100644 (file)
@@ -60,6 +60,6 @@ class TestNsInstant(TestCase):
         self.assertEqual(resp.status_code, status.HTTP_200_OK)
     """
     
-    def test_swagger_ok(self):
-        resp = self.client.get("/api/nslcm/v1/swagger.json", format='json')
-        self.assertEqual(resp.status_code, status.HTTP_200_OK)
+    def test_swagger_ok(self):
+        resp = self.client.get("/api/nslcm/v1/swagger.json", format='json')
+        self.assertEqual(resp.status_code, status.HTTP_200_OK)
index b12a456..5251c1d 100644 (file)
@@ -49,8 +49,9 @@ class TestTerminateNsViews(TestCase):
         NSInstModel.objects.all().delete()
         NfInstModel.objects.all().delete()
 
-    @mock.patch.object(TerminateNsService, 'do_biz')
+    @mock.patch.object(TerminateNsService, 'run')
     def test_terminate_vnf_url(self, mock_run):
+        mock_run.re.return_value = None
         req_data = {
             "terminationType": "forceful",
             "gracefulTerminationTimeout": "600"}
@@ -60,7 +61,7 @@ class TestTerminateNsViews(TestCase):
         response = self.client.delete("/api/nslcm/v1/ns/%s" % self.ns_inst_id)
         self.failUnlessEqual(status.HTTP_204_NO_CONTENT, response.status_code)
 
-    @mock.patch.object(restcall, "call_req")
+    @mock.patch.object(restcall, 'call_req')
     def test_terminate_vnf(self, mock_call_req):
         job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, self.nf_inst_id)
 
@@ -90,7 +91,7 @@ class TestTerminateNsViews(TestCase):
 
         mock_call_req.side_effect = side_effect
 
-        TerminateNsService(self.nf_inst_id, "forceful", "600", job_id).start()
+        TerminateNsService(self.nf_inst_id, "forceful", "600", job_id).run()
         nsinst = NSInstModel.objects.get(id=self.ns_inst_id)
         if nsinst:
             self.assertTrue(1, 0)
index 93a1421..4cfc8e6 100644 (file)
@@ -14,6 +14,7 @@
 
 import json
 import logging
+import uuid
 
 from lcm.pub.exceptions import NSLCMException
 from lcm.pub.utils import restcall
@@ -23,11 +24,69 @@ logger = logging.getLogger(__name__)
 
 
 def call_aai(resource, method, content=''):
+    additional_headers = {
+        'X-FromAppId': 'VFC-NFVO-LCM',
+        'X-TransactionId': str(uuid.uuid1())
+    }
     return restcall.call_req(base_url=AAI_BASE_URL, 
         user=AAI_USER, 
         passwd=AAI_PASSWD, 
         auth_type=restcall.rest_no_auth, 
         resource=resource, 
         method=method, 
-        content=content)
+        content=content,
+        additional_headers=additional_headers)
 
+def create_ns_aai(global_customer_id, service_type, service_instance_id, data):
+    resource = "/business/customers/customer/%s/service-subscriptions/service-subscription/" \
+               "%s/service-instances/service-instance/%s" % \
+               (global_customer_id, service_type, service_instance_id)
+    ret = call_aai(resource, "PUT", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Ns instance creation exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
+
+def delete_ns_aai(global_customer_id, service_type, service_instance_id, data):
+    resource = "/business/customers/customer/%s/service-subscriptions/service-subscription/" \
+               "%s/service-instances/service-instance/%s" % \
+               (global_customer_id, service_type, service_instance_id)
+    ret = call_aai(resource, "DELETE", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Ns instance delete exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
+
+def query_ns_aai(global_customer_id, service_type, service_instance_id, data):
+    resource = "/business/customers/customer/%s/service-subscriptions/service-subscription/" \
+               "%s/service-instances/service-instance/%s" % \
+               (global_customer_id, service_type, service_instance_id)
+    ret = call_aai(resource, "GET", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Ns instance query exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
+
+def create_vnf_aai(vnf_id, data):
+    resource = "/network/generic-vnfs/generic-vnf/%s" % vnf_id
+    ret = call_aai(resource, "PUT", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Vnf instance creation exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
+
+def delete_vnf_aai(vnf_id, data=''):
+    resource = "/network/generic-vnfs/generic-vnf/%s" % vnf_id
+    ret = call_aai(resource, "DELETE", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Vnf instance delete exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
+
+def query_vnf_aai(vnf_id, data):
+    resource = "/network/generic-vnfs/generic-vnf/%s" % vnf_id
+    ret = call_aai(resource, "GET", data)
+    if ret[0] != 0:
+        logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+        raise NSLCMException("Vnf instance query exception in AAI")
+    return json.JSONDecoder().decode(ret[1])
index 7a3e939..b4ec177 100644 (file)
@@ -25,13 +25,17 @@ ASSETTYPE_RESOURCES = "resources"
 ASSETTYPE_SERVICES = "services"
 
 def call_sdc(resource, method, content=''):
+    additional_headers = {
+        'X-ECOMP-InstanceID': 'VFC-NFVO-LCM'
+    }
     return restcall.call_req(base_url=SDC_BASE_URL, 
         user=SDC_USER, 
         passwd=SDC_PASSWD, 
         auth_type=restcall.rest_no_auth, 
         resource=resource, 
         method=method, 
-        content=content)
+        content=content,
+        additional_headers=additional_headers)
 
 def get_artifacts(asset_type):
     resource = "/sdc/v1/catalog/{assetType}"
index 0ef2056..d05d30b 100644 (file)
@@ -29,7 +29,8 @@ HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUES
 logger = logging.getLogger(__name__)
 
 
-def call_req(base_url, user, passwd, auth_type, resource, method, content=''):
+def call_req(base_url, user, passwd, auth_type, resource, method, 
+    content='', additional_headers={}):
     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))
@@ -41,6 +42,8 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''):
         if user:
             headers['Authorization'] = 'Basic ' + ('%s:%s' % (user, passwd)).encode("base64")
         ca_certs = None
+        if additional_headers:
+            headers.update(additional_headers)
         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
index 919e95e..075ae27 100644 (file)
@@ -11,6 +11,7 @@
 # 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
 
 from lcm.pub.utils.toscaparser.nsdmodel import EtsiNsdInfoModel
index 452e6e3..351f093 100644 (file)
@@ -1,10 +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.
+
 import copy
+import ftplib
 import json
 import os
 import re
 import shutil
 import urllib
 
+import paramiko
 from toscaparser.functions import GetInput
 from toscaparser.tosca_template import ToscaTemplate
 
@@ -101,6 +117,32 @@ class BaseInfoModel(object):
             self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName)
         return localFileName
 
+    def sftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
+        # return
+        t = None
+        try:
+            t = paramiko.Transport(hostIp, int(hostPort))
+            t.connect(username=userName, password=userPwd)
+            sftp = paramiko.SFTPClient.from_transport(t)
+            sftp.get(remoteFileName, localFileName)
+        finally:
+            if t != None:
+                t.close()
+
+
+    def ftp_get(self, userName, userPwd, hostIp, hostPort, remoteFileName, localFileName):
+        f = None
+        try:
+            ftp = ftplib.FTP()
+            ftp.connect(hostIp, hostPort)
+            ftp.login(userName, userPwd)
+            f = open(localFileName, 'wb')
+            ftp.retrbinary('RETR ' + remoteFileName, f.write, 1024)
+            f.close()
+        finally:
+            if f != None:
+                f.close()
+
     def buidMetadata(self, tosca):
         if 'metadata' in tosca.tpl:
             self.metadata = copy.deepcopy(tosca.tpl['metadata'])
@@ -267,4 +309,20 @@ class BaseInfoModel(object):
         for node in node_templates:
             if node['name'] == name:
                 return node
-        return None
\ No newline at end of file
+        return None
+
+    def get_all_nested_ns(self, nodes):
+        nss = []
+        for node in nodes:
+            if self.is_nested_ns(node):
+                ns = {}
+                ns['ns_id'] = node['name']
+                ns['description'] = node['description']
+                ns['properties'] = node['properties']
+                ns['networks'] = self.get_networks(node)
+
+                nss.append(ns)
+        return nss
+
+    def is_nested_ns(self, node):
+        return node['nodeType'].upper().find('.NS.') >= 0 or node['nodeType'].upper().endswith('.NS')
diff --git a/lcm/pub/utils/toscaparser/convert.py b/lcm/pub/utils/toscaparser/convert.py
deleted file mode 100644 (file)
index d7d7a50..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2017 ZTE Corporation.\r
-#\r
-# Licensed under the Apache License, Version 2.0 (the "License");\r
-# you may not use this file except in compliance with the License.\r
-# You may obtain a copy of the License at\r
-#\r
-#         http://www.apache.org/licenses/LICENSE-2.0\r
-#\r
-# Unless required by applicable law or agreed to in writing, software\r
-# distributed under the License is distributed on an "AS IS" BASIS,\r
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-# See the License for the specific language governing permissions and\r
-# limitations under the License.\r
-\r
-\r
-def convert_nsd(nsd_object):\r
-    pass\r
-\r
-def convert_vnfd(vnfd_object):\r
-    pass
\ No newline at end of file
index 02c11ac..6ca2066 100644 (file)
@@ -1,23 +1,24 @@
-#    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
+# 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.
-
+# 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 toscaparser.dataentity import DataEntity
 from toscaparser.elements.constraints import Schema
 from toscaparser.common.exception import ExceptionCollector
 
 class DataEntityExt(object):
+
     '''A complex data value entity ext.'''
-    
     @staticmethod
     def validate_datatype(type, value, entry_schema=None, custom_def=None):
         if value:
@@ -31,4 +32,3 @@ class DataEntityExt(object):
     
             return DataEntity.validate_datatype(type, value, entry_schema, custom_def)
         return value
-
index 9d2b5a4..e13f026 100644 (file)
@@ -1,3 +1,17 @@
+# 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 functools
 
 from lcm.pub.utils.toscaparser.basemodel import BaseInfoModel
@@ -28,6 +42,7 @@ class EtsiNsdInfoModel(BaseInfoModel):
         self.ns_exposed = self.get_all_endpoint_exposed(tosca.topology_template)
         self.policies = self._get_policies_scaling(tosca.topology_template.policies)
         self.ns_flavours = self.get_all_flavour(tosca.topology_template.groups)
+        self.nested_ns = self.get_all_nested_ns(nodeTemplates)
 
 
     def buildInputs(self, top_inputs):
@@ -343,4 +358,4 @@ class EtsiNsdInfoModel(BaseInfoModel):
         return rets
 
     def _isFlavour(self, group):
-        return group.type.upper().find('FLAVOUR') >= 0
\ No newline at end of file
+        return group.type.upper().find('FLAVOUR') >= 0
diff --git a/lcm/pub/utils/toscaparser/parser.py b/lcm/pub/utils/toscaparser/parser.py
deleted file mode 100644 (file)
index f6605f6..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright 2017 ZTE Corporation.\r
-#\r
-# Licensed under the Apache License, Version 2.0 (the "License");\r
-# you may not use this file except in compliance with the License.\r
-# You may obtain a copy of the License at\r
-#\r
-#         http://www.apache.org/licenses/LICENSE-2.0\r
-#\r
-# Unless required by applicable law or agreed to in writing, software\r
-# distributed under the License is distributed on an "AS IS" BASIS,\r
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-# See the License for the specific language governing permissions and\r
-# limitations under the License.\r
-from os import R_OK, access\r
-\r
-from lcm.pub.exceptions import NSLCMException\r
-from toscaparser.tosca_template import ToscaTemplate\r
-\r
-def parse_nsd_model(path, input_parameters):\r
-    isexist = check_file_exist(path)\r
-    if isexist:\r
-        nsd_tpl = parse_nsd_csar(path, input_parameters)\r
-    else:\r
-        raise NSLCMException('%s is not exist.' % path)\r
-    return nsd_tpl\r
-\r
-\r
-def parse_vnfd_model(path, input_parameters):\r
-    isexist = check_file_exist(path)\r
-    if isexist:\r
-        vnfd_tpl = parse_vnfd_csar(path, input_parameters)\r
-    else:\r
-        raise NSLCMException('%s is not exist.' % path)\r
-    return vnfd_tpl\r
-\r
-def check_file_exist(path):\r
-    if path.exists(path) and path.isfile(path) and access(path, R_OK):\r
-        return True\r
-    else:\r
-        return False\r
-\r
-def parse_nsd_csar(path, input_parameters=[], a_file=True):\r
-    nsd_object = None\r
-    nsd_object = ToscaTemplate(path, input_parameters)\r
-    return nsd_object\r
-\r
-\r
-def parse_vnfd_csar(path, input_parameters=[], a_file=True):\r
-    vnfd_object = None\r
-    vnfd_object = ToscaTemplate(path, input_parameters)\r
-    return vnfd_object
\ No newline at end of file
index a9b61b8..cd1ee56 100644 (file)
@@ -1,3 +1,17 @@
+# 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 functools
 
 from lcm.pub.utils.toscaparser import EtsiNsdInfoModel
@@ -20,6 +34,8 @@ class EtsiVnfdInfoModel(EtsiNsdInfoModel):
         self.vcloud = self._get_all_vcloud(nodeTemplates)
         self.vcenter = self._get_all_vcenter(nodeTemplates)
         self.image_files = self._get_all_image_file(nodeTemplates)
+        self.local_storages = self._get_all_local_storage(nodeTemplates)
+        self.volume_storages = self._get_all_volume_storage(nodeTemplates)
 
 
     def _get_all_services(self, nodeTemplates):
@@ -96,4 +112,40 @@ class EtsiVnfdInfoModel(EtsiNsdInfoModel):
         return rets
 
     def _isImageFile(self, node):
-        return node['nodeType'].upper().find('.IMAGEFILE.') >= 0 or node['nodeType'].upper().endswith('.IMAGEFILE')
\ No newline at end of file
+        return node['nodeType'].upper().find('.IMAGEFILE.') >= 0 or node['nodeType'].upper().endswith('.IMAGEFILE')
+
+    def _get_all_local_storage(self, nodeTemplates):
+        rets = []
+        for node in nodeTemplates:
+            if self._isLocalStorage(node):
+                ret = {}
+                ret['local_storage_id'] = node['name']
+                if 'description' in node:
+                    ret['description'] = node['description']
+                ret['properties'] = node['properties']
+
+                rets.append(ret)
+        return rets
+
+    def _isLocalStorage(self, node):
+        return node['nodeType'].upper().find('.LOCALSTORAGE.') >= 0 or node['nodeType'].upper().endswith(
+            '.LOCALSTORAGE')
+
+    def _get_all_volume_storage(self, nodeTemplates):
+        rets = []
+        for node in nodeTemplates:
+            if self._isVolumeStorage(node):
+                ret = {}
+                ret['volume_storage_id'] = node['name']
+                if 'description' in node:
+                    ret['description'] = node['description']
+                ret['properties'] = node['properties']
+                ret['image_file'] = map(lambda x: self.get_requirement_node_name(x),
+                                        self.getRequirementByName(node, 'image_file'))
+
+                rets.append(ret)
+        return rets
+
+    def _isVolumeStorage(self, node):
+        return node['nodeType'].upper().find('.VOLUMESTORAGE.') >= 0 or node['nodeType'].upper().endswith(
+            '.VOLUMESTORAGE')
index e9f7ce6..9fb0e89 100644 (file)
@@ -26,4 +26,5 @@ mock==2.0.0
 unittest_xml_reporting==1.12.0
 
 # for parser
+paramiko==2.0.2
 nfv-toscaparser==0.5.0.dev95