Add vcpeutils for vFW 99/85499/4
authorBrian Freeman <bf1936@att.com>
Tue, 16 Apr 2019 16:27:00 +0000 (11:27 -0500)
committerBrian Freeman <bf1936@att.com>
Tue, 16 Apr 2019 19:18:30 +0000 (14:18 -0500)
Additional changes in runTags.sh and setup.sh in testsuite needed to use vcpeutils

Issue-ID: INT-1028
Change-Id: I45fb4ded70998685c94d9c11a154ce4a452836ce
Signed-off-by: Brian Freeman <bf1936@att.com>
setup.py
vcpeutils/SoUtils.py [new file with mode: 0755]
vcpeutils/csar_parser.py [new file with mode: 0755]
vcpeutils/preload.py [new file with mode: 0755]
vcpeutils/vcpecommon.py [new file with mode: 0755]

index 7c8d9ff..20aec53 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -34,8 +34,8 @@ setup(
         'deepdiff>=2.5,<3.3',
         'Jinja2'
     ],  # what we need
-    packages=['eteutils', 'loadtest'],       # The name of your scripts package
-    package_dir={'eteutils': 'eteutils', 'loadtest': 'loadtest'}, # The location of your scipts package
+    packages=['eteutils', 'loadtest', 'vcpeutils'],       # The name of your scripts package
+    package_dir={'eteutils': 'eteutils', 'loadtest': 'loadtest', 'vcpeutils':'vcpeutils'}, # The location of your scipts package
     classifiers=[
         'Development Status :: 4 - Beta',
         'Intended Audience :: Developers',
@@ -45,4 +45,4 @@ setup(
         'Framework :: Robot Framework :: Library',
         'License :: OSI Approved :: Apache Software License'
     ]
-)
\ No newline at end of file
+)
diff --git a/vcpeutils/SoUtils.py b/vcpeutils/SoUtils.py
new file mode 100755 (executable)
index 0000000..a027c5d
--- /dev/null
@@ -0,0 +1,370 @@
+#! /usr/bin/python
+
+import sys
+import logging
+import requests
+import json
+from datetime import datetime
+#import progressbar
+import time
+import csar_parser
+import preload
+from vcpecommon import *
+from robot.api import logger
+
+
+class SoUtils:
+
+    def __init__(self):
+        """
+        :param api_version: must be 'v4' or 'v5'
+        """
+        self.tmp_solution_for_so_bug = False
+        #self.logger = logging.getLogger(__name__)
+        self.logger = logger 
+        self.vcpecommon = VcpeCommon()
+        self.api_version = 'v4'
+        self.service_req_api_url = self.vcpecommon.so_req_api_url[self.api_version]
+
+
+    def submit_create_req(self, req_json, req_type, service_instance_id=None, vnf_instance_id=None):
+        """
+        POST   {serverRoot}/serviceInstances/v4
+        POST   {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs
+        POST   {serverRoot}/serviceInstances/v4/{serviceInstanceId}/networks
+        POST   {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules
+        :param req_json:
+        :param service_instance_id:  this is required only for networks, vnfs, and vf modules
+        :param req_type:
+        :param vnf_instance_id:
+        :return: req_id, instance_id
+        """
+        if req_type == 'service':
+            url = self.service_req_api_url
+        elif req_type == 'vnf':
+            url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs'])
+        elif req_type == 'network':
+            url = '/'.join([self.service_req_api_url, service_instance_id, 'networks'])
+        elif req_type == 'vfmodule':
+            url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs', vnf_instance_id, 'vfModules'])
+        else:
+            self.logger.error('Invalid request type: {0}. Can only be service/vnf/network/vfmodule'.format(req_type))
+            return None, None
+
+        self.logger.info(url)
+        r = requests.post(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass, json=req_json)
+        self.logger.debug(r)
+        response = r.json()
+
+        self.logger.debug('---------------------------------------------------------------')
+        self.logger.debug('------- Creation request submitted to SO, got response --------')
+        self.logger.debug(json.dumps(response, indent=4, sort_keys=True))
+        self.logger.debug('---------------------------------------------------------------')
+        req_id = response.get('requestReferences', {}).get('requestId', '')
+        instance_id = response.get('requestReferences', {}).get('instanceId', '')
+
+        return req_id, instance_id
+
+    def check_progress(self, req_id, eta=0, interval=5):
+        if not req_id:
+            self.logger.error('Error when checking SO request progress, invalid request ID: ' + req_id)
+            return False
+        duration = 0.0
+        #bar = progressbar.ProgressBar(redirect_stdout=True)
+        url = self.vcpecommon.so_check_progress_api_url + '/' + req_id
+
+        while True:
+            time.sleep(interval)
+            r = requests.get(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass)
+            response = r.json()
+
+            duration += interval
+            if eta > 0:
+                percentage = min(95, 100 * duration / eta)
+            else:
+                percentage = int(response['request']['requestStatus']['percentProgress'])
+
+            if response['request']['requestStatus']['requestState'] == 'IN_PROGRESS':
+                self.logger.debug('------------------Request Status-------------------------------')
+                self.logger.debug(json.dumps(response, indent=4, sort_keys=True))
+                #bar.update(percentage)
+            else:
+                self.logger.debug('---------------------------------------------------------------')
+                self.logger.debug('----------------- Creation Request Results --------------------')
+                self.logger.debug(json.dumps(response, indent=4, sort_keys=True))
+                self.logger.debug('---------------------------------------------------------------')
+                flag = response['request']['requestStatus']['requestState'] == 'COMPLETE'
+                if not flag:
+                    self.logger.error('Request failed.')
+                    self.logger.error(json.dumps(response, indent=4, sort_keys=True))
+                #bar.update(100)
+                #bar.finish()
+                return flag
+
+    def add_req_info(self, req_details, instance_name, product_family_id=None):
+        req_details['requestInfo'] = {
+                    'instanceName': instance_name,
+                    'source': 'VID',
+                    'suppressRollback': 'true',
+                    'requestorId': 'vCPE-Robot'
+        }
+        if product_family_id:
+            req_details['requestInfo']['productFamilyId'] = product_family_id
+
+    def add_related_instance(self, req_details, instance_id, instance_model):
+        instance = {"instanceId": instance_id, "modelInfo": instance_model}
+        if 'relatedInstanceList' not in req_details:
+            req_details['relatedInstanceList'] = [{"relatedInstance": instance}]
+        else:
+            req_details['relatedInstanceList'].append({"relatedInstance": instance})
+
+    def generate_vnf_or_network_request(self, req_type, instance_name, vnf_or_network_model, service_instance_id,
+                                        service_model):
+        req_details = {
+            'modelInfo':  vnf_or_network_model,
+            #'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name,
+            #                       "tenantId": self.vcpecommon.os_tenant_id},
+            'cloudConfiguration': {"lcpCloudRegionId": self.region_name,
+                                   "tenantId": self.tenant_id},
+            'requestParameters':  {"userParams": []},
+            'platform': {"platformName": "Platform-Demonstration"}
+        }
+        self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id)
+        self.add_related_instance(req_details, service_instance_id, service_model)
+        return {'requestDetails': req_details}
+
+    def generate_vfmodule_request(self, instance_name, vfmodule_model, service_instance_id,
+                                        service_model, vnf_instance_id, vnf_model):
+        req_details = {
+            'modelInfo':  vfmodule_model,
+            #'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name,
+            #                       "tenantId": self.vcpecommon.os_tenant_id},
+            'cloudConfiguration': {"lcpCloudRegionId": self.region_name,
+                                   "tenantId": self.tenant_id},
+            'requestParameters': {"usePreload": 'true'}
+        }
+        self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id)
+        self.add_related_instance(req_details, service_instance_id, service_model)
+        self.add_related_instance(req_details, vnf_instance_id, vnf_model)
+        return {'requestDetails': req_details}
+
+    def generate_service_request(self, instance_name, model):
+        req_details = {
+            'modelInfo':  model,
+            'subscriberInfo':  {'globalSubscriberId': self.vcpecommon.global_subscriber_id},
+            'requestParameters': {
+                "userParams": [],
+                "subscriptionServiceType": "vCPE",
+                "aLaCarte": 'true'
+            }
+        }
+        self.add_req_info(req_details, instance_name)
+        self.add_project_info(req_details)
+        self.add_owning_entity(req_details)
+        return {'requestDetails': req_details}
+
+    def add_project_info(self, req_details):
+        req_details['project'] = {'projectName': self.vcpecommon.project_name}
+
+    def add_owning_entity(self, req_details):
+        req_details['owningEntity'] = {'owningEntityId': self.vcpecommon.owning_entity_id,
+                                       'owningEntityName': self.vcpecommon.owning_entity_name}
+
+    def generate_custom_service_request(self, instance_name, model, brg_mac):
+        brg_mac_enc = brg_mac.replace(':', '-')
+        req_details = {
+            'modelInfo':  model,
+            'subscriberInfo':  {'subscriberName': 'Kaneohe',
+                                'globalSubscriberId': self.vcpecommon.global_subscriber_id},
+            'cloudConfiguration': {"lcpCloudRegionId": self.region_name,
+                                   "tenantId": self.tenant_id},
+            'requestParameters': {
+                "userParams": [
+                    {
+                        'name': 'BRG_WAN_MAC_Address',
+                        'value': brg_mac
+                    },
+                    {
+                       'name': 'VfModuleNames',
+                       'value': [
+                            {
+                                'VfModuleModelInvariantUuid': self.vcpecommon.vgw_VfModuleModelInvariantUuid,
+                                'VfModuleName': 'VGW2BRG-{0}'.format(brg_mac_enc)
+                            }
+                       ]
+                    },
+                    {
+                         "name": "Customer_Location",
+                         "value": self.vcpecommon.customer_location_used_by_oof
+                    },
+                    {
+                         "name": "Homing_Solution",
+                         "value": self.vcpecommon.homing_solution
+                    }
+                ],
+                "subscriptionServiceType": "vCPE",
+                'aLaCarte': 'false'
+            }
+        }
+        self.add_req_info(req_details, instance_name, self.vcpecommon.custom_product_family_id)
+        self.add_project_info(req_details)
+        self.add_owning_entity(req_details)
+        return {'requestDetails': req_details}
+
+    def create_custom_service(self, csar_file, brg_mac, name_suffix=None):
+        parser = csar_parser.CsarParser()
+        if not parser.parse_csar(csar_file):
+            return False
+
+        # yyyymmdd_hhmm
+        if not name_suffix:
+            name_suffix = '_' + datetime.now().strftime('%Y%m%d%H%M')
+
+        # create service
+        instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'],
+                                  parser.svc_model['modelName'][0:10], name_suffix])
+        instance_name = instance_name.lower()
+        req = self.generate_custom_service_request(instance_name, parser.svc_model, brg_mac)
+        self.logger.info(json.dumps(req, indent=2, sort_keys=True))
+        self.logger.info('Creating custom service {0}.'.format(instance_name))
+        req_id, svc_instance_id = self.submit_create_req(req, 'service')
+        if not self.check_progress(req_id, 140):
+            return False
+        return True
+
+    def wait_for_aai(self, node_type, uuid):
+        self.logger.info('Waiting for AAI traversal to complete...')
+        #bar = progressbar.ProgressBar()
+        for i in range(30):
+            time.sleep(1)
+            #bar.update(i*100.0/30)
+            if self.vcpecommon.is_node_in_aai(node_type, uuid):
+                #bar.update(100)
+                #bar.finish()
+                return
+
+        self.logger.error("AAI traversal didn't finish in 30 seconds. Something is wrong. Type {0}, UUID {1}".format(
+            node_type, uuid))
+        sys.exit()
+
+    def create_entire_service(self, csar_file, vnf_template_file, preload_dict, name_suffix, region_name, tenant_id, heatbridge=False):
+        """
+        :param csar_file:
+        :param vnf_template_file:
+        :param preload_dict:
+        :param name_suffix:
+        :return:  service instance UUID
+        """
+        self.region_name=region_name
+        self.tenant_id=tenant_id
+        self.logger.info('\n----------------------------------------------------------------------------------')
+        self.logger.info('Start to create entire service defined in csar: {0}'.format(csar_file))
+        parser = csar_parser.CsarParser()
+        self.logger.info('Parsing csar ...')
+        if not parser.parse_csar(csar_file):
+            self.logger.error('Cannot parse csar: {0}'.format(csar_file))
+            return None
+
+        # Set Global timestamp for instancenames
+        global_timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
+        # create service
+        instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'],
+                                  parser.svc_model['modelName'], global_timestamp, name_suffix])
+        instance_name = instance_name.lower()
+        instance_name = instance_name.replace(' ','')
+        instance_name = instance_name.replace(':','')
+        self.logger.info('Creating service instance: {0}.'.format(instance_name))
+        req = self.generate_service_request(instance_name, parser.svc_model)
+        self.logger.debug(json.dumps(req, indent=2, sort_keys=True))
+        req_id, svc_instance_id = self.submit_create_req(req, 'service')
+        if not self.check_progress(req_id, eta=2, interval=5):
+            return None
+
+        # wait for AAI to complete traversal
+        self.wait_for_aai('service', svc_instance_id)
+
+        # create networks
+        for model in parser.net_models:
+            base_name = model['modelCustomizationName'].lower().replace('mux_vg', 'mux_gw')
+            network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], base_name, name_suffix])
+            network_name = network_name.lower()
+            self.logger.info('Creating network: ' + network_name)
+            req = self.generate_vnf_or_network_request('network', network_name, model, svc_instance_id,
+                                                       parser.svc_model)
+            self.logger.debug(json.dumps(req, indent=2, sort_keys=True))
+            req_id, net_instance_id = self.submit_create_req(req, 'network', svc_instance_id)
+            if not self.check_progress(req_id, eta=20):
+                return None
+
+            self.logger.info('Changing subnet name to ' + self.vcpecommon.network_name_to_subnet_name(network_name))
+            self.vcpecommon.set_network_name(network_name)
+            subnet_name_changed = False
+            for i in range(20):
+                time.sleep(3)
+                if self.vcpecommon.set_subnet_name(network_name):
+                    subnet_name_changed = True
+                    break
+
+            if not subnet_name_changed:
+                self.logger.error('Failed to change subnet name for ' + network_name)
+                return None
+
+
+        vnf_model = None
+        vnf_instance_id = None
+        # create VNF
+        if len(parser.vnf_models) == 1:
+            vnf_model = parser.vnf_models[0]
+            vnf_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vnf'],
+                                          vnf_model['modelCustomizationName'].split(' ')[0],  name_suffix])
+            vnf_instance_name = vnf_instance_name.lower()
+            vnf_instance_name = vnf_instance_name.replace(' ','')
+            vnf_instance_name = vnf_instance_name.replace(':','')
+            self.logger.info('Creating VNF: ' + vnf_instance_name)
+            req = self.generate_vnf_or_network_request('vnf', vnf_instance_name, vnf_model, svc_instance_id,
+                                                       parser.svc_model)
+            self.logger.debug(json.dumps(req, indent=2, sort_keys=True))
+            req_id, vnf_instance_id = self.submit_create_req(req, 'vnf', svc_instance_id)
+            if not self.check_progress(req_id, eta=2, interval=5):
+                self.logger.error('Failed to create VNF {0}.'.format(vnf_instance_name))
+                return False
+
+            # wait for AAI to complete traversal
+            if not vnf_instance_id:
+                self.logger.error('No VNF instance ID returned!')
+                sys.exit()
+            self.wait_for_aai('vnf', vnf_instance_id)
+
+        # SDNC Preload 
+
+        preloader = preload.Preload(self.vcpecommon)
+        preloader.preload_vfmodule(vnf_template_file, svc_instance_id, parser.vnf_models[0], parser.vfmodule_models[0],
+                                   preload_dict, name_suffix)
+
+        # create VF Module
+        if len(parser.vfmodule_models) == 1:
+            if not vnf_instance_id or not vnf_model:
+                self.logger.error('Invalid VNF instance ID or VNF model!')
+                sys.exit()
+
+            model = parser.vfmodule_models[0]
+            vfmodule_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'],
+                                               model['modelCustomizationName'].split('..')[0], name_suffix])
+            vfmodule_instance_name = vfmodule_instance_name.lower()
+            vfmodule_instance_name = vfmodule_instance_name.replace(' ','')
+            vfmoduel_instance_name = vfmodule_instance_name.replace(':','')
+            self.logger.info('Creating VF Module: ' + vfmodule_instance_name)
+            req = self.generate_vfmodule_request(vfmodule_instance_name, model, svc_instance_id, parser.svc_model,
+                                                 vnf_instance_id, vnf_model)
+            self.logger.debug(json.dumps(req, indent=2, sort_keys=True))
+            req_id, vfmodule_instance_id = self.submit_create_req(req, 'vfmodule', svc_instance_id, vnf_instance_id)
+            if not self.check_progress(req_id, eta=70, interval=50):
+                self.logger.error('Failed to create VF Module {0}.'.format(vfmodule_instance_name))
+                return None
+
+        # run heatbridge
+        if heatbridge:
+            self.vcpecommon.heatbridge(vfmodule_instance_name, svc_instance_id)
+            self.vcpecommon.save_vgmux_vnf_name(vnf_instance_name)
+
+        return svc_instance_id
diff --git a/vcpeutils/csar_parser.py b/vcpeutils/csar_parser.py
new file mode 100755 (executable)
index 0000000..f101364
--- /dev/null
@@ -0,0 +1,231 @@
+#! /usr/bin/python
+import os
+import zipfile
+import shutil
+import yaml
+import json
+import logging
+
+
+class CsarParser:
+    def __init__(self):
+        self.logger = logging.getLogger(__name__)
+        self.svc_model = {}
+        self.net_models = []  # there could be multiple networks
+        self.vnf_models = [] # this version only support a single VNF in the service template
+        self.vfmodule_models = [] # this version only support a single VF module in the service template
+
+    def get_service_yaml_from_csar(self, csar_file):
+        """
+        :param csar_file: csar file path name, e.g. 'csar/vgmux.csar'
+        :return:
+        """
+        tmpdir = './__tmp'
+        if os.path.isdir(tmpdir):
+            shutil.rmtree(tmpdir)
+        os.mkdir(tmpdir)
+
+        with zipfile.ZipFile(csar_file, "r") as zip_ref:
+            zip_ref.extractall(tmpdir)
+
+        yamldir = tmpdir + '/Definitions'
+        if os.path.isdir(yamldir):
+            for filename in os.listdir(yamldir):
+                # look for service template like this: service-Vcpesvcbrgemu111601-template.yml
+                if filename.startswith('service-') and filename.endswith('-template.yml'):
+                    return os.path.join(yamldir, filename)
+
+        self.logger.error('Invalid file: ' + csar_file)
+        return ''
+
+    def get_service_model_info(self, svc_template):
+        """ extract service model info from yaml and convert to what to be used in SO request
+        Sample from yaml:
+            {
+                "UUID": "aed4fc5e-b871-4e26-8531-ceabd46df85e",
+                "category": "Network L1-3",
+                "description": "Infra service",
+                "ecompGeneratedNaming": true,
+                "invariantUUID": "c806682a-5b3a-44d8-9e88-0708be151296",
+                "name": "vcpesvcinfra111601",
+                "namingPolicy": "",
+                "serviceEcompNaming": true,
+                "serviceRole": "",
+                "serviceType": "",
+                "type": "Service"
+            },
+
+        Convert to
+            {
+                 "modelType": "service",
+                 "modelInvariantId": "ca4c7a70-06fd-45d8-8b9e-c9745d25bf2b",
+                 "modelVersionId": "5d8911b4-e50c-4096-a81e-727a8157193c",
+                 "modelName": "vcpesvcbrgemu111601",
+                 "modelVersion": "1.0"
+             },
+
+        """
+        if svc_template['metadata']['type'] != 'Service':
+            self.logger.error('csar error: metadata->type is not Service')
+            return
+
+        metadata = svc_template['metadata']
+        self.svc_model = {
+            'modelType': 'service',
+            'modelInvariantId': metadata['invariantUUID'],
+            'modelVersionId': metadata['UUID'],
+            'modelName':  metadata['name']
+        }
+        if 'version' in metadata:
+            self.svc_model['modelVersion'] = metadata['version']
+        else:
+            self.svc_model['modelVersion'] = '1.0'
+
+    def get_vnf_and_network_model_info(self, svc_template):
+        """ extract vnf and network model info from yaml and convert to what to be used in SO request
+        Sample from yaml:
+        "topology_template": {
+            "node_templates": {
+                "CPE_PUBLIC": {
+                    "metadata": {
+                        "UUID": "33b2c367-a165-4bb3-81c3-0150cd06ceff",
+                        "category": "Generic",
+                        "customizationUUID": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1",
+                        "description": "Generic NeutronNet",
+                        "invariantUUID": "3d4c0e47-4794-4e98-a794-baaced668930",
+                        "name": "Generic NeutronNet",
+                        "resourceVendor": "ATT (Tosca)",
+                        "resourceVendorModelNumber": "",
+                        "resourceVendorRelease": "1.0.0.wd03",
+                        "subcategory": "Network Elements",
+                        "type": "VL",
+                        "version": "1.0"
+                    },
+                    "type": "org.openecomp.resource.vl.GenericNeutronNet"
+                },
+        Convert to
+            {
+                "modelType": "network",
+                "modelInvariantId": "3d4c0e47-4794-4e98-a794-baaced668930",
+                "modelVersionId": "33b2c367-a165-4bb3-81c3-0150cd06ceff",
+                "modelName": "Generic NeutronNet",
+                "modelVersion": "1.0",
+                "modelCustomizationId": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1",
+                "modelCustomizationName": "CPE_PUBLIC"
+            },
+        """
+        node_dic = svc_template['topology_template']['node_templates']
+        for node_name, v in node_dic.items():
+            model = {
+                'modelInvariantId':  v['metadata']['invariantUUID'],
+                'modelVersionId': v['metadata']['UUID'],
+                'modelName': v['metadata']['name'],
+                'modelVersion': v['metadata']['version'],
+                'modelCustomizationId': v['metadata']['customizationUUID'],
+                'modelCustomizationName': node_name
+            }
+
+            if v['type'].startswith('org.openecomp.resource.vl.GenericNeutronNet'):
+                # a neutron network is found
+                self.logger.info('Parser found a network: ' + node_name)
+                model['modelType'] = 'network'
+                self.net_models.append(model)
+            elif v['type'].startswith('org.openecomp.resource.vf.'):
+                # a VNF is found
+                self.logger.info('Parser found a VNF: ' + node_name)
+                model['modelType'] = 'vnf'
+                self.vnf_models.append(model)
+            else:
+                self.logger.warning('Parser found a node that is neither a network nor a VNF: ' + node_name)
+
+    def get_vfmodule_model_info(self, svc_template):
+        """ extract network model info from yaml and convert to what to be used in SO request
+        Sample from yaml:
+            "topology_template": {
+                "groups": {
+                    "vspinfra1116010..Vspinfra111601..base_vcpe_infra..module-0": {
+                        "metadata": {
+                            "vfModuleModelCustomizationUUID": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288",
+                            "vfModuleModelInvariantUUID": "02f70416-581e-4f00-bde1-d65e69af95c5",
+                            "vfModuleModelName": "Vspinfra111601..base_vcpe_infra..module-0",
+                            "vfModuleModelUUID": "88c78078-f1fd-4f73-bdd9-10420b0f6353",
+                            "vfModuleModelVersion": "1"
+                        },
+                        "properties": {
+                            "availability_zone_count": null,
+                            "initial_count": 1,
+                            "max_vf_module_instances": 1,
+                            "min_vf_module_instances": 1,
+                            "vf_module_description": null,
+                            "vf_module_label": "base_vcpe_infra",
+                            "vf_module_type": "Base",
+                            "vfc_list": null,
+                            "volume_group": false
+                        },
+                        "type": "org.openecomp.groups.VfModule"
+                    }
+                },
+        Convert to
+            {
+                "modelType": "vfModule",
+                "modelInvariantId": "02f70416-581e-4f00-bde1-d65e69af95c5",
+                "modelVersionId": "88c78078-f1fd-4f73-bdd9-10420b0f6353",
+                "modelName": "Vspinfra111601..base_vcpe_infra..module-0",
+                "modelVersion": "1",
+                "modelCustomizationId": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288",
+                "modelCustomizationName": "Vspinfra111601..base_vcpe_infra..module-0"
+            },
+        """
+        node_dic = svc_template['topology_template']['groups']
+        for node_name, v in node_dic.items():
+            if v['type'].startswith('org.openecomp.groups.VfModule'):
+                model = {
+                    'modelType': 'vfModule',
+                    'modelInvariantId':  v['metadata']['vfModuleModelInvariantUUID'],
+                    'modelVersionId': v['metadata']['vfModuleModelUUID'],
+                    'modelName': v['metadata']['vfModuleModelName'],
+                    'modelVersion': v['metadata']['vfModuleModelVersion'],
+                    'modelCustomizationId': v['metadata']['vfModuleModelCustomizationUUID'],
+                    'modelCustomizationName': v['metadata']['vfModuleModelName']
+                }
+                self.vfmodule_models.append(model)
+                self.logger.info('Parser found a VF module: ' + model['modelCustomizationName'])
+
+    def parse_service_yaml(self, filename):
+        # clean up
+        self.svc_model = {}
+        self.net_models = []    # there could be multiple networks
+        self.vnf_models = []    # this version only support a single VNF in the service template
+        self.vfmodule_models = []   # this version only support a single VF module in the service template
+
+        svc_template = yaml.load(file(filename, 'r'))
+        self.get_service_model_info(svc_template)
+        self.get_vnf_and_network_model_info(svc_template)
+        self.get_vfmodule_model_info(svc_template)
+
+        return True
+
+    def parse_csar(self, csar_file):
+        yaml_file = self.get_service_yaml_from_csar(csar_file)
+        if yaml_file != '':
+            return self.parse_service_yaml(yaml_file)
+
+    def print_models(self):
+        print('---------Service Model----------')
+        print(json.dumps(self.svc_model, indent=2, sort_keys=True))
+
+        print('---------Network Model(s)----------')
+        for model in self.net_models:
+            print(json.dumps(model, indent=2, sort_keys=True))
+
+        print('---------VNF Model(s)----------')
+        for model in self.vnf_models:
+            print(json.dumps(model, indent=2, sort_keys=True))
+
+        print('---------VF Module Model(s)----------')
+        for model in self.vfmodule_models:
+            print(json.dumps(model, indent=2, sort_keys=True))
+
+    def test(self):
+        self.parse_csar('csar/service-Vcpesvcinfra111601-csar.csar')
+        self.print_models()
diff --git a/vcpeutils/preload.py b/vcpeutils/preload.py
new file mode 100755 (executable)
index 0000000..aab28f9
--- /dev/null
@@ -0,0 +1,231 @@
+#! /usr/bin/python
+
+import requests
+import json
+import sys
+from datetime import datetime
+from vcpecommon import *
+import csar_parser
+#import logging
+from robot.api import logger
+import base64
+
+
+class Preload:
+    def __init__(self, vcpecommon):
+        #self.logger = logging.getLogger(__name__)
+        self.logger = logger
+        self.vcpecommon = vcpecommon
+
+    def replace(self, sz, replace_dict):
+        for old_string, new_string in replace_dict.items():
+            sz = sz.replace(old_string, new_string)
+        if self.vcpecommon.template_variable_symbol in sz:
+            self.logger.error('Error! Cannot find a value to replace ' + sz)
+        return sz
+
+    def generate_json(self, template_file, replace_dict):
+        with open(template_file) as json_input:
+            json_data = json.load(json_input)
+            stk = [json_data]
+            while len(stk) > 0:
+                data = stk.pop()
+                for k, v in data.items():
+                    if type(v) is dict:
+                        stk.append(v)
+                    elif type(v) is list:
+                        stk.extend(v)
+                    elif type(v) is str or type(v) is unicode:
+                        if self.vcpecommon.template_variable_symbol in v:
+                            data[k] = self.replace(v, replace_dict)
+                    else:
+                        self.logger.warning('Unexpected line in template: %s. Look for value %s', template_file, v)
+        return json_data
+
+    def reset_sniro(self):
+        self.logger.debug('Clearing SNIRO data')
+        r = requests.post(self.vcpecommon.sniro_url + '/reset', headers=self.vcpecommon.sniro_headers)
+        if 2 != r.status_code / 100:
+            self.logger.debug(r.content)
+            self.logger.error('Clearing SNIRO date failed.')
+            sys.exit()
+
+    def preload_sniro(self, template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, vbrg_ar_name,
+                      vgmux_svc_instance_uuid, vbrg_svc_instance_uuid):
+        self.reset_sniro()
+        self.logger.info('Preloading SNIRO for homing service')
+        replace_dict = {'${tunnelxconn_ar_name}': tunnelxconn_ar_name,
+                        '${vgw_name}': vgw_name,
+                        '${brg_ar_name}': vbrg_ar_name,
+                        '${vgmux_svc_instance_uuid}': vgmux_svc_instance_uuid,
+                        '${vbrg_svc_instance_uuid}': vbrg_svc_instance_uuid
+                        }
+        sniro_data = self.generate_json(template_sniro_data, replace_dict)
+        self.logger.debug('SNIRO data:')
+        self.logger.debug(json.dumps(sniro_data, indent=4, sort_keys=True))
+
+        base64_sniro_data = base64.b64encode(json.dumps(sniro_data))
+        self.logger.debug('SNIRO data: 64')
+        self.logger.debug(base64_sniro_data)
+        replace_dict = {'${base64_sniro_data}': base64_sniro_data, '${sniro_ip}': self.vcpecommon.hosts['robot']}
+        sniro_request = self.generate_json(template_sniro_request, replace_dict)
+        self.logger.debug('SNIRO request:')
+        self.logger.debug(json.dumps(sniro_request, indent=4, sort_keys=True))
+
+        r = requests.post(self.vcpecommon.sniro_url, headers=self.vcpecommon.sniro_headers, json=sniro_request)
+        if 2 != r.status_code / 100:
+            response = r.json()
+            self.logger.debug(json.dumps(response, indent=4, sort_keys=True))
+            self.logger.error('SNIRO preloading failed.')
+            sys.exit()
+
+        return True
+
+    def preload_network(self, template_file, network_role, subnet_start_ip, subnet_gateway, common_dict, name_suffix):
+        """
+        :param template_file:
+        :param network_role: cpe_signal, cpe_public, brg_bng, bng_mux, mux_gw
+        :param subnet_start_ip:
+        :param subnet_gateway:
+        :param name_suffix: e.g. '201711201311'
+        :return:
+        """
+        network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], network_role.lower(), name_suffix])
+        subnet_name = self.vcpecommon.network_name_to_subnet_name(network_name)
+        common_dict['${' + network_role+'_net}'] = network_name
+        common_dict['${' + network_role+'_subnet}'] = subnet_name
+        replace_dict = {'${network_role}': network_role,
+                        '${service_type}': 'vCPE',
+                        '${network_type}': 'Generic NeutronNet',
+                        '${network_name}': network_name,
+                        '${subnet_start_ip}': subnet_start_ip,
+                        '${subnet_gateway}': subnet_gateway
+                        }
+        self.logger.info('Preloading network ' + network_role)
+        return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_network_url)
+
+    def preload(self, template_file, replace_dict, url):
+        self.logger.debug(json.dumps(replace_dict, indent=4, sort_keys=True))
+        json_data = self.generate_json(template_file, replace_dict)
+        self.logger.debug(json.dumps(json_data, indent=4, sort_keys=True))
+        r = requests.post(url, headers=self.vcpecommon.sdnc_headers, auth=self.vcpecommon.sdnc_userpass, json=json_data)
+        response = r.json()
+        if int(response.get('output', {}).get('response-code', 0)) != 200:
+            self.logger.debug(json.dumps(response, indent=4, sort_keys=True))
+            self.logger.error('Preloading failed.')
+            return False
+        return True
+
+    def preload_vgw(self, template_file, brg_mac, commont_dict, name_suffix):
+        replace_dict = {'${brg_mac}': brg_mac,
+                        '${suffix}': name_suffix
+                        }
+        replace_dict.update(commont_dict)
+        self.logger.info('Preloading vGW')
+        return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url)
+
+    def preload_vgw_gra(self, template_file, brg_mac, commont_dict, name_suffix, vgw_vfmod_name_index):
+        replace_dict = {'${brg_mac}': brg_mac,
+                        '${suffix}': name_suffix,
+                        '${vgw_vfmod_name_index}': vgw_vfmod_name_index
+                        }
+        replace_dict.update(commont_dict)
+        self.logger.info('Preloading vGW-GRA')
+        return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_gra_url)
+
+    def preload_vfmodule(self, template_file, service_instance_id, vnf_model, vfmodule_model, common_dict, name_suffix):
+        """
+        :param template_file:
+        :param service_instance_id:
+        :param vnf_model:  parsing results from csar_parser
+        :param vfmodule_model:  parsing results from csar_parser
+        :param common_dict:
+        :param name_suffix:
+        :return:
+        """
+
+        # examples:
+        # vfmodule_model['modelCustomizationName']: "Vspinfra111601..base_vcpe_infra..module-0",
+        # vnf_model['modelCustomizationName']: "vspinfra111601 0",
+
+        vfmodule_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'],
+                                  vfmodule_model['modelCustomizationName'].split('..')[0].lower(), name_suffix])
+
+        # vnf_type and generic_vnf_type are identical
+        replace_dict = {'${vnf_type}': vfmodule_model['modelCustomizationName'],
+                        '${generic_vnf_type}': vfmodule_model['modelCustomizationName'],
+                        '${service_type}': service_instance_id,
+                        '${generic_vnf_name}': vnf_model['modelCustomizationName'],
+                        '${vnf_name}': vfmodule_name,
+                        '${mr_ip_addr}': self.vcpecommon.mr_ip_addr,
+                        '${mr_ip_port}': self.vcpecommon.mr_ip_port,
+                        '${sdnc_oam_ip}': self.vcpecommon.sdnc_oam_ip,
+                        '${suffix}': name_suffix}
+        replace_dict.update(common_dict)
+        self.logger.info('Preloading VF Module ' + vfmodule_name)
+        return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url)
+
+    def preload_all_networks(self, template_file, name_suffix):
+        common_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()}
+        for network, v in self.vcpecommon.preload_network_config.items():
+            subnet_start_ip, subnet_gateway_ip = v
+            if not self.preload_network(template_file, network, subnet_start_ip, subnet_gateway_ip,
+                                        common_dict, name_suffix):
+                return None
+        return common_dict
+
+    def test(self):
+        # this is for testing purpose
+        name_suffix = datetime.now().strftime('%Y%m%d%H%M')
+        vcpecommon = VcpeCommon()
+        preloader = Preload(vcpecommon)
+
+        network_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()}
+        template_file = 'preload_templates/template.network.json'
+        for k, v in self.vcpecommon.preload_network_config.items():
+            if not preloader.preload_network(template_file, k, v[0], v[1], network_dict, name_suffix):
+                break
+
+        print('---------------------------------------------------------------')
+        print('Network related replacement dictionary:')
+        print(json.dumps(network_dict, indent=4, sort_keys=True))
+        print('---------------------------------------------------------------')
+
+        keys = ['infra', 'bng', 'gmux', 'brg']
+        for key in keys:
+            csar_file = self.vcpecommon.find_file(key, 'csar', 'csar')
+            template_file = self.vcpecommon.find_file(key, 'json', 'preload_templates')
+            if csar_file and template_file:
+                parser = csar_parser.CsarParser()
+                parser.parse_csar(csar_file)
+                service_instance_id = 'test112233'
+                preloader.preload_vfmodule(template_file, service_instance_id, parser.vnf_models[0],
+                                           parser.vfmodule_models[0], network_dict, name_suffix)
+
+    def test_sniro(self):
+        template_sniro_data = self.vcpecommon.find_file('sniro_data', 'json', 'preload_templates')
+        template_sniro_request = self.vcpecommon.find_file('sniro_request', 'json', 'preload_templates')
+
+        vcperescust_csar = self.vcpecommon.find_file('rescust', 'csar', 'csar')
+        parser = csar_parser.CsarParser()
+        parser.parse_csar(vcperescust_csar)
+        tunnelxconn_ar_name = None
+        brg_ar_name = None
+        vgw_name = None
+        for model in parser.vnf_models:
+            if 'tunnel' in model['modelCustomizationName']:
+                tunnelxconn_ar_name = model['modelCustomizationName']
+            elif 'brg' in model['modelCustomizationName']:
+                brg_ar_name = model['modelCustomizationName']
+            elif 'vgw' in model['modelCustomizationName']:
+                vgw_name = model['modelCustomizationName']
+
+        if not (tunnelxconn_ar_name and brg_ar_name and vgw_name):
+            self.logger.error('Cannot find all names from %s.', vcperescust_csar)
+            sys.exit()
+
+        vgmux_svc_instance_uuid = '88888888888888'
+        vbrg_svc_instance_uuid = '999999999999999'
+
+        self.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name,
+                           vgmux_svc_instance_uuid, vbrg_svc_instance_uuid)
diff --git a/vcpeutils/vcpecommon.py b/vcpeutils/vcpecommon.py
new file mode 100755 (executable)
index 0000000..95b5bbe
--- /dev/null
@@ -0,0 +1,325 @@
+import json
+import logging
+import os
+import pickle
+import re
+import sys
+
+import ipaddress
+import requests
+import commands
+import time
+from novaclient import client as openstackclient
+from netaddr import IPAddress, IPNetwork
+
+class VcpeCommon:
+    #############################################################################################
+    #     Start: configurations that you must change for a new ONAP installation
+    external_net_addr = '10.12.0.0'
+    external_net_prefix_len = 16
+    #############################################################################################
+    # set the openstack cloud access credentials here
+    oom_mode = True
+
+    cloud = {
+        '--os-auth-url': 'http://10.12.25.2:5000',
+        '--os-username': 'kxi',
+        '--os-user-domain-id': 'default',
+        '--os-project-domain-id': 'default',
+        '--os-tenant-id': '09d8566ea45e43aa974cf447ed591d77' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
+        '--os-region-name': 'RegionOne',
+        '--os-password': 'n3JhGMGuDzD8',
+        '--os-project-domain-name': 'Integration-SB-03' if oom_mode else 'Integration-SB-07',
+        '--os-identity-api-version': '3'
+    }
+
+    common_preload_config = {
+        'oam_onap_net': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
+        'oam_onap_subnet': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
+        'public_net': 'external',
+        'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
+    }
+    sdnc_controller_pod = 'dev-sdnc-sdnc-0'
+
+    #############################################################################################
+
+    template_variable_symbol = '${'
+    cpe_vm_prefix = 'zdcpe'
+    #############################################################################################
+    # preloading network config
+    #  key=network role
+    #  value = [subnet_start_ip, subnet_gateway_ip]
+    preload_network_config = {
+        'cpe_public': ['10.2.0.2', '10.2.0.1'],
+        'cpe_signal': ['10.4.0.2', '10.4.0.1'],
+        'brg_bng': ['10.3.0.2', '10.3.0.1'],
+        'bng_mux': ['10.1.0.10', '10.1.0.1'],
+        'mux_gw': ['10.5.0.10', '10.5.0.1']
+    }
+
+    dcae_ves_collector_name = 'dcae-bootstrap'
+    global_subscriber_id = 'Demonstration'
+    project_name = 'Project-Demonstration'
+    owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
+    owning_entity_name = 'OE-Demonstration1'
+
+    def __init__(self, extra_host_names=None):
+        rootlogger = logging.getLogger()
+        handler = logging.StreamHandler()
+        formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s.%(funcName)s(): %(message)s')
+        handler.setFormatter(formatter)
+        rootlogger.addHandler(handler)
+        rootlogger.setLevel(logging.INFO)
+
+        self.logger = logging.getLogger(__name__)
+        self.logger.propagate = False
+        self.logger.addHandler(handler)
+        self.logger.setLevel(logging.DEBUG)
+        self.logger.info('Initializing configuration')
+
+        # CHANGEME: vgw_VfModuleModelInvariantUuid is in rescust service csar, look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automcatically
+        self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
+        # CHANGEME: OOM: this is the address that the brg and bng will nat for sdnc access - 10.0.0.x address of k8 host for sdnc-0 container
+        #self.sdnc_oam_ip = self.get_pod_node_oam_ip('sdnc-sdnc-0')
+        self.sdnc_oam_ip = 'sdnc.onap'
+        # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP 
+        #self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip('sdnc-sdnc-0')
+        #self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip('sdnc-sdnc-0')
+        # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
+        #self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
+        # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
+        #self.mr_ip_addr = self.oom_so_sdnc_aai_ip
+        self.mr_ip_addr = 'mr.onap'
+        #self.mr_ip_port = '30227'
+        self.mr_ip_port = '3904'
+        #self.so_nbi_port = '30277' if self.oom_mode else '8080'
+        self.so_nbi_port = '8080' 
+        #self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
+        self.sdnc_preloading_port = '8282'
+        #self.aai_query_port = '30233' if self.oom_mode else '8443'
+        self.aai_query_port = '8443' 
+        #self.sniro_port = '30288' if self.oom_mode else '8080'
+        self.sniro_port = '8080' 
+
+        self.host_names = ['so', 'sdnc', 'robot', 'aai', self.dcae_ves_collector_name]
+        if extra_host_names:
+            self.host_names.extend(extra_host_names)
+        # get IP addresses
+        #self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
+        self.hosts = { 'so': 'so.onap', 'sdnc': 'sdnc.onap', 'robot': 'robot.onap', 'aai': 'aai.onap' }
+        # this is the keyword used to name vgw stack, must not be used in other stacks
+        self.vgw_name_keyword = 'base_vcpe_vgw'
+        # this is the file that will keep the index of last assigned SO name
+        self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
+        self.svc_instance_uuid_file = '__var/svc_instance_uuid'
+        self.preload_dict_file = '__var/preload_dict'
+        self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
+        self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
+        self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
+        self.instance_name_prefix = {
+            'service': 'svc',
+            'network': 'net',
+            'vnf': 'vnf',
+            'vfmodule': 'vf'
+        }
+        self.aai_userpass = 'AAI', 'AAI'
+        self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
+        self.os_tenant_id = self.cloud['--os-tenant-id']
+        self.os_region_name = self.cloud['--os-region-name']
+        self.common_preload_config['pub_key'] = self.pub_key
+        self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
+        self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
+        self.homing_solution = 'sniro'  # value is either 'sniro' or 'oof'
+#        self.homing_solution = 'oof'
+        self.customer_location_used_by_oof = {
+            "customerLatitude": "32.897480",
+            "customerLongitude": "-97.040443",
+            "customerName": "some_company"
+        }
+
+        #############################################################################################
+        # SDNC urls
+        self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
+        self.sdnc_db_name = 'sdnctl'
+        self.sdnc_db_user = 'sdnctl'
+        self.sdnc_db_pass = 'gamma'
+        self.sdnc_db_port = '32774'
+        self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
+        self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
+                                        ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
+        self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
+                                    ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
+        self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
+                                    ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
+        self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
+                                   '/restconf/config/GENERIC-RESOURCE-API:'
+
+        #############################################################################################
+        # SO urls, note: do NOT add a '/' at the end of the url
+        self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
+                           'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
+        self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
+        self.so_userpass = 'InfraPortalClient', 'password1$'
+        self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
+        self.so_db_name = 'catalogdb'
+        self.so_db_user = 'root'
+        self.so_db_pass = 'password'
+        self.so_db_port = '30252' if self.oom_mode else '32769'
+
+        self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
+        self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
+        self.vpp_api_userpass = ('admin', 'admin')
+        self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
+
+
+
+    def find_file(self, file_name_keyword, file_ext, search_dir):
+        """
+        :param file_name_keyword:  keyword used to look for the csar file, case insensitive matching, e.g, infra
+        :param file_ext: e.g., csar, json
+        :param search_dir path to search
+        :return: path name of the file
+        """
+        file_name_keyword = file_name_keyword.lower()
+        file_ext = file_ext.lower()
+        if not file_ext.startswith('.'):
+            file_ext = '.' + file_ext
+
+        filenamepath = None
+        for file_name in os.listdir(search_dir):
+            file_name_lower = file_name.lower()
+            if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
+                if filenamepath:
+                    self.logger.error('Multiple files found for *{0}*.{1} in '
+                                      'directory {2}'.format(file_name_keyword, file_ext, search_dir))
+                    sys.exit()
+                filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
+
+        if filenamepath:
+            return filenamepath
+        else:
+            self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
+            sys.exit()
+
+    @staticmethod
+    def network_name_to_subnet_name(network_name):
+        """
+        :param network_name: example: vcpe_net_cpe_signal_201711281221
+        :return: vcpe_net_cpe_signal_subnet_201711281221
+        """
+        fields = network_name.split('_')
+        fields.insert(-1, 'subnet')
+        return '_'.join(fields)
+
+    def set_network_name(self, network_name):
+        param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
+        openstackcmd = 'openstack ' + param
+        cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
+        os.popen(cmd)
+
+    def set_subnet_name(self, network_name):
+        """
+        Example: network_name =  vcpe_net_cpe_signal_201711281221
+        set subnet name to vcpe_net_cpe_signal_subnet_201711281221
+        :return:
+        """
+        param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
+        openstackcmd = 'openstack ' + param
+
+        # expected results: | subnets | subnet_id |
+        subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
+        if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
+            subnet_id = subnet_info[2].strip()
+            subnet_name = self.network_name_to_subnet_name(network_name)
+            cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
+            os.popen(cmd)
+            self.logger.info("Subnet name set to: " + subnet_name)
+            return True
+        else:
+            self.logger.error("Can't get subnet info from network name: " + network_name)
+            return False
+
+    def is_node_in_aai(self, node_type, node_uuid):
+        key = None
+        search_node_type = None
+        if node_type == 'service':
+            search_node_type = 'service-instance'
+            key = 'service-instance-id'
+        elif node_type == 'vnf':
+            search_node_type = 'generic-vnf'
+            key = 'vnf-id'
+        else:
+            logging.error('Invalid node_type: ' + node_type)
+            sys.exit()
+
+        url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
+            self.hosts['aai'], self.aai_query_port, search_node_type, key, node_uuid)
+
+        headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
+        requests.packages.urllib3.disable_warnings()
+        r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
+        response = r.json()
+        self.logger.debug('aai query: ' + url)
+        self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
+        return 'result-data' in response
+
+    @staticmethod
+    def extract_ip_from_str(net_addr, net_addr_len, sz):
+        """
+        :param net_addr:  e.g. 10.5.12.0
+        :param net_addr_len: e.g. 24
+        :param sz: a string
+        :return: the first IP address matching the network, e.g. 10.5.12.3
+        """
+        network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
+        ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
+        for ip in ip_list:
+            this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
+            if this_net == network:
+                return str(ip)
+        return None
+
+
+
+    @staticmethod
+    def save_object(obj, filepathname):
+        with open(filepathname, 'wb') as fout:
+            pickle.dump(obj, fout)
+
+    @staticmethod
+    def load_object(filepathname):
+        with open(filepathname, 'rb') as fin:
+            return pickle.load(fin)
+
+    @staticmethod
+    def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
+        with open(vnf_template_file) as json_input:
+            json_data = json.load(json_input)
+            param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
+            for param in param_list:
+                if param['vnf-parameter-name'] in vnf_parameter_name_list:
+                    ipaddr_or_vni = param['vnf-parameter-value'].split('.')
+                    number = int(ipaddr_or_vni[-1])
+                    if 254 == number:
+                        number = 10
+                    else:
+                        number = number + 1
+                    ipaddr_or_vni[-1] = str(number)
+                    param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
+
+        assert json_data is not None
+        with open(vnf_template_file, 'w') as json_output:
+            json.dump(json_data, json_output, indent=4, sort_keys=True)
+
+    def save_preload_data(self, preload_data):
+        self.save_object(preload_data, self.preload_dict_file)
+
+    def load_preload_data(self):
+        return self.load_object(self.preload_dict_file)
+
+    def save_vgmux_vnf_name(self, vgmux_vnf_name):
+        self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
+
+    def load_vgmux_vnf_name(self):
+        return self.load_object(self.vgmux_vnf_name_file)
+