update link to upper-constraints.txt
[integration.git] / test / vcpe / vcpecommon.py
index 4b69fe4..0e02987 100755 (executable)
@@ -10,93 +10,28 @@ import sys
 import ipaddress
 import mysql.connector
 import requests
-import commands
+import subprocess
 import time
+import yaml
 from novaclient import client as openstackclient
+from openstack.config import loader
 from kubernetes import client, config
 from netaddr import IPAddress, IPNetwork
 
-######################################################################
-# Parts which must be updated / cross-checked during each deployment #
-# are marked as CHANGEME                                             #
-######################################################################
-
 class VcpeCommon:
-    #############################################################################################
-    # Set network prefix of k8s host external address; it's used for pod public IP autodetection
-    # but can be overriden from user in case of autodetection failure
-    external_net_addr = '10.12.0.0'
-    external_net_prefix_len = 16
-
-    #############################################################################################
-    # set the openstack cloud access credentials here
-    oom_mode = True
-
-    ###########################
-    # set Openstack credentials
-    # CHANGEME part
-    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': 'bc43d50ffcb84750bac0c1707a9a765b' 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'
-    }
-
-    ############################################################################
-    # set oam and public network which must exist in openstack before deployment
-    # CHANGEME part
-    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'
-    }
-
-    #############################################################################
-    # Set name of Onap's k8s namespace and sdnc controller pod
-    # CHANGEME part
-    onap_namespace = 'onap'
-    onap_environment = 'dev'
-    sdnc_controller_pod = '-'.join([onap_environment, '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 = 'SDN-ETHERNET-INTERNET'
-    project_name = 'Project-Demonstration'
-    owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
-    owning_entity_name = 'OE-Demonstration1'
-
-    def __init__(self, extra_host_names=None):
+
+    def __init__(self, extra_host_names=None, cfg_file=None):
         self.logger = logging.getLogger(__name__)
         self.logger.setLevel(logging.DEBUG)
         self.logger.info('Initializing configuration')
+        self.default_config = 'vcpeconfig.yaml'
 
-        ##################################################################################################################################
-        # following param must be updated e.g. from csar file (grep for VfModuleModelInvariantUuid string) before vcpe.py customer call !!
-        # vgw_VfModuleModelInvariantUuid is in rescust service csar,
-        # look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automatically
-        # CHANGEME part
-        self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
+        # Read configuration from config file
+        self._load_config(cfg_file)
+        # Load OpenStack settings
+        self._load_os_config()
 
+        self.sdnc_controller_pod = '-'.join([self.onap_environment, 'sdnc-sdnc-0'])
         # 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(self.sdnc_controller_pod)
         # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
@@ -111,7 +46,7 @@ class VcpeCommon:
         self.aai_query_port = '30233' if self.oom_mode else '8443'
         self.sniro_port = '30288' if self.oom_mode else '8080'
 
-        self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
+        self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name, 'mariadb-galera']
         if extra_host_names:
             self.host_names.extend(extra_host_names)
         # get IP addresses
@@ -132,12 +67,6 @@ class VcpeCommon:
             'vfmodule': 'vcpe_vfmodule'
         }
         self.aai_userpass = 'AAI', 'AAI'
-
-        ############################################################################################################
-        # following key is overriding public key from vCPE heat templates, it's important to use correct one in here
-        # CHANGEME part
-        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
@@ -172,10 +101,12 @@ class VcpeCommon:
         self.sdnc_db_name = 'sdnctl'
         self.sdnc_db_user = 'sdnctl'
         self.sdnc_db_pass = 'gamma'
-        self.sdnc_db_port = '32774'
+        self.sdnc_db_port = self.get_k8s_service_endpoint_info('mariadb-galera','port') if self.oom_mode else '3306'
         self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
         self.sdnc_preload_network_url = 'https://' + self.hosts['sdnc'] + \
                                         ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
+        self.sdnc_preload_network_gra_url = 'https://' + self.hosts['sdnc'] + \
+                                        ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-network-topology-operation'
         self.sdnc_preload_vnf_url = 'https://' + self.hosts['sdnc'] + \
                                     ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
         self.sdnc_preload_gra_url = 'https://' + self.hosts['sdnc'] + \
@@ -183,6 +114,11 @@ class VcpeCommon:
         self.sdnc_ar_cleanup_url = 'https://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
                                    '/restconf/config/GENERIC-RESOURCE-API:'
 
+        #############################################################################################
+        # MARIADB-GALERA settings
+        self.mariadb_galera_endpoint_ip = self.get_k8s_service_endpoint_info('mariadb-galera','ip')
+        self.mariadb_galera_endpoint_port = self.get_k8s_service_endpoint_info('mariadb-galera','port')
+
         #############################################################################################
         # 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',
@@ -193,7 +129,8 @@ class VcpeCommon:
         self.so_db_name = 'catalogdb'
         self.so_db_user = 'root'
         self.so_db_pass = 'secretpassword'
-        self.so_db_port = '30252' if self.oom_mode else '32769'
+        self.so_db_host = self.mariadb_galera_endpoint_ip if self.oom_mode else self.hosts['so']
+        self.so_db_port = self.mariadb_galera_endpoint_port if self.oom_mode else '3306'
 
         self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
         self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
@@ -211,6 +148,73 @@ class VcpeCommon:
         self.policy_api_service_name = 'policy-api'
         self.policy_pap_service_name = 'policy-pap'
 
+        #############################################################################################
+        # AAI urls
+        self.aai_region_query_url = 'https://' + self.oom_so_sdnc_aai_ip + ':' +\
+                                    self.aai_query_port +\
+                                    '/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/CloudOwner/' +\
+                                    self.cloud['--os-region-name']
+        self.aai_headers = {'Accept': 'application/json',
+                            'Content-Type': 'application/json',
+                            'X-FromAppId': 'postman', 'X-TransactionId': '9999'}
+
+    def _load_config(self, cfg_file):
+        """
+        Reads vcpe config file and injects settings as object's attributes
+        :param cfg_file: Configuration file path
+        """
+
+        if cfg_file is None:
+            cfg_file = self.default_config
+
+        try:
+            with open(cfg_file, 'r') as cfg:
+                cfg_yml = yaml.full_load(cfg)
+        except Exception as e:
+            self.logger.error('Error loading configuration: ' + str(e))
+            sys.exit(1)
+
+        self.logger.debug('\n' + yaml.dump(cfg_yml))
+
+        # Use setattr to load config file keys as VcpeCommon class' object
+        # attributes
+        try:
+            # Check config isn't empty
+            if cfg_yml is not None:
+                for cfg_key in cfg_yml:
+                    setattr(self, cfg_key, cfg_yml[cfg_key])
+        except TypeError as e:
+            self.logger.error('Unable to parse config file: ' + str(e))
+            sys.exit(1)
+
+    def _load_os_config(self):
+        """
+        Reads cloud settings and sets them as object's 'cloud' attribute
+        """
+        # Create OpenStackConfig config instance
+        os_config = loader.OpenStackConfig()
+        # Try reading cloud settings for self.cloud_name
+        try:
+            os_cloud = os_config.cloud_config['clouds'][self.cloud_name]
+        except KeyError:
+            self.logger.error('Error fetching cloud settings for cloud "{0}"'
+                              .format(self.cloud_name))
+            sys.exit(1)
+        self.logger.debug('Cloud config:\n {0}'.format(json.dumps(
+                          os_cloud,indent=4)))
+
+        # Extract all OS settings keys and alter their names
+        # to conform to openstack cli client
+        self.cloud = {}
+        for k in os_cloud:
+            if isinstance(os_cloud[k],dict):
+                for sub_k in os_cloud[k]:
+                    os_setting_name = '--os-' + sub_k.replace('_','-')
+                    self.cloud[os_setting_name] = os_cloud[k][sub_k]
+            else:
+                os_setting_name = '--os-' + k.replace('_','-')
+                self.cloud[os_setting_name] = os_cloud[k]
+
     def heatbridge(self, openstack_stack_name, svc_instance_uuid):
         """
         Add vserver information to AAI
@@ -218,7 +222,7 @@ class VcpeCommon:
         self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
         if not self.oom_mode:
             cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
-            ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
+            ret = subprocess.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
             self.logger.debug('%s', ret)
         else:
             print('To add vGMUX vserver info to AAI, do the following:')
@@ -232,8 +236,16 @@ class VcpeCommon:
         Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
         Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
         """
-        cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
-                                      host=self.hosts['sdnc'], port=self.sdnc_db_port)
+        if self.oom_mode:
+            db_host=self.mariadb_galera_endpoint_ip
+        else:
+            db_host=self.hosts['mariadb-galera']
+
+        cnx = mysql.connector.connect(user=self.sdnc_db_user,
+                                      password=self.sdnc_db_pass,
+                                      database=self.sdnc_db_name,
+                                      host=db_host,
+                                      port=self.sdnc_db_port)
         cursor = cnx.cursor()
         query = "SELECT * from DHCP_MAP"
         cursor.execute(query)
@@ -242,7 +254,7 @@ class VcpeCommon:
         mac_recent = None
         host = -1
         for mac, ip in cursor:
-            self.logger.debug(mac + ':' + ip)
+            self.logger.debug(mac + ' - ' + ip)
             this_host = int(ip.split('.')[-1])
             if host < this_host:
                 host = this_host
@@ -250,16 +262,26 @@ class VcpeCommon:
 
         cnx.close()
 
-        assert mac_recent
+        try:
+            assert mac_recent
+        except AssertionError:
+            self.logger.error('Failed to obtain BRG MAC address from database')
+            sys.exit(1)
+
         return mac_recent
 
+    def execute_cmds_mariadb(self, cmds):
+        self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass,
+                             self.sdnc_db_name, self.mariadb_galera_endpoint_ip,
+                             self.mariadb_galera_endpoint_port)
+
     def execute_cmds_sdnc_db(self, cmds):
         self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
                              self.hosts['sdnc'], self.sdnc_db_port)
 
     def execute_cmds_so_db(self, cmds):
         self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
-                             self.hosts['so'], self.so_db_port)
+                             self.so_db_host, self.so_db_port)
 
     def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
         cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
@@ -291,14 +313,14 @@ class VcpeCommon:
                 if filenamepath:
                     self.logger.error('Multiple files found for *{0}*.{1} in '
                                       'directory {2}'.format(file_name_keyword, file_ext, search_dir))
-                    sys.exit()
+                    sys.exit(1)
                 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()
+            sys.exit(1)
 
     @staticmethod
     def network_name_to_subnet_name(network_name):
@@ -349,10 +371,9 @@ class VcpeCommon:
                 policy_json = json.load(f)
             except ValueError:
                 self.logger.error(policy_template_file + " doesn't seem to contain valid JSON data")
-                sys.exit()
+                sys.exit(1)
 
         # Check policy already applied
-        requests.packages.urllib3.disable_warnings()
         policy_exists_req = requests.get(self.policy_pap_get_url.format(
                             p_pap_cluster_ip), auth=self.policy_userpass,
                             verify=False, headers=self.policy_headers)
@@ -360,13 +381,13 @@ class VcpeCommon:
             self.logger.error('Failure in checking CL policy existence. '
                                'Policy-pap responded with HTTP code {0}'.format(
                                policy_exists_req.status_code))
-            sys.exit()
+            sys.exit(1)
 
         try:
             policy_exists_json = policy_exists_req.json()
         except ValueError as e:
             self.logger.error('Policy-pap request failed: ' + e.message)
-            sys.exit()
+            sys.exit(1)
 
         try:
             assert policy_exists_json['groups'][0]['pdpSubgroups'] \
@@ -386,7 +407,7 @@ class VcpeCommon:
         if policy_create_req.status_code != 200:
             self.logger.error('Failed creating policy. Policy-api responded'
                               ' with HTTP code {0}'.format(policy_create_req.status_code))
-            sys.exit()
+            sys.exit(1)
 
         try:
             policy_version = json.loads(policy_create_req.text)['policy-version']
@@ -403,7 +424,7 @@ class VcpeCommon:
         if policy_insert_req.status_code != 200:
             self.logger.error('Policy PAP request failed with HTTP code'
                               '{0}'.format(policy_insert_req.status_code))
-            sys.exit()
+            sys.exit(1)
         self.logger.info('Successully pushed closed loop Policy')
 
     def is_node_in_aai(self, node_type, node_uuid):
@@ -417,13 +438,12 @@ class VcpeCommon:
             key = 'vnf-id'
         else:
             logging.error('Invalid node_type: ' + node_type)
-            sys.exit()
+            sys.exit(1)
 
         url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
             self.hosts['aai-inst1'], 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)
@@ -438,17 +458,17 @@ class VcpeCommon:
         :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)
+        network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False) # pylint: disable=E0602
         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)
+            this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False) # pylint: disable=E0602
             if this_net == network:
                 return str(ip)
         return None
 
     def get_pod_node_oam_ip(self, pod):
         """
-        :Assuming kubectl is available and configured by default config (~/.kube/config) 
+        :Assuming kubectl is available and configured by default config (~/.kube/config)
         :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
         :return pod's cluster node oam ip (10.0.0.0/16)
         """
@@ -465,12 +485,12 @@ class VcpeCommon:
                 break
 
         if ret is None:
-            ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node OAM IP address(10.0.0.0/16): ")
+            ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node OAM IP address(10.0.0.0/16): ") # pylint: disable=E0602
         return ret
 
     def get_pod_node_public_ip(self, pod):
         """
-        :Assuming kubectl is available and configured by default config (~/.kube/config) 
+        :Assuming kubectl is available and configured by default config (~/.kube/config)
         :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
         :return pod's cluster node public ip (i.e. 10.12.0.0/16)
         """
@@ -487,7 +507,7 @@ class VcpeCommon:
                 break
 
         if ret is None:
-            ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node public IP address(i.e. " + self.external_net_addr + "): ")
+            ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node public IP address(i.e. " + self.external_net_addr + "): ") # pylint: disable=E0602
         return ret
 
     def get_vm_public_ip_by_nova(self, vm):
@@ -497,10 +517,10 @@ class VcpeCommon:
         :return vm public ip
         """
         subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
-        nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url']) 
+        nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
         for i in nova.servers.list():
             if i.name == vm:
-                for k, v in i.networks.items():
+                for k, v in i.networks.items(): # pylint: disable=W0612
                     for ip in v:
                         if IPAddress(ip) in subnet:
                             return ip
@@ -535,14 +555,13 @@ class VcpeCommon:
             self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
             self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
             self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
-#            sys.exit()
+#            sys.exit(1)
         return ip_dict
 
     def get_oom_onap_vm_ip(self, keywords):
         vm_ip = {}
-        onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
         for vm in keywords:
-            if vm in onap_vm_list:
+            if vm in self.host_names:
                 vm_ip[vm] = self.oom_so_sdnc_aai_ip
         return vm_ip
 
@@ -560,10 +579,36 @@ class VcpeCommon:
             resp = api.read_namespaced_service(service, self.onap_namespace)
         except client.rest.ApiException as e:
             self.logger.error('Error while making k8s API request: ' + e.body)
-            sys.exit()
+            sys.exit(1)
 
         return resp.spec.cluster_ip
 
+    def get_k8s_service_endpoint_info(self, service, subset):
+        """
+        Returns endpoint data for a given service and subset. If there
+        is more than one endpoint returns data for the first one from
+        the list that API returned.
+        :param service: name of the service
+        :param subset: subset name, one of "ip","port"
+        :return: endpoint ip
+        """
+        config.load_kube_config()
+        api = client.CoreV1Api()
+        kslogger = logging.getLogger('kubernetes')
+        kslogger.setLevel(logging.INFO)
+        try:
+            resp = api.read_namespaced_endpoints(service, self.onap_namespace)
+        except client.rest.ApiException as e:
+            self.logger.error('Error while making k8s API request: ' + e.body)
+            sys.exit(1)
+
+        if subset == "ip":
+            return resp.subsets[0].addresses[0].ip
+        elif subset == "port":
+            return resp.subsets[0].ports[0].port
+        else:
+            self.logger.error("Unsupported subset type")
+
     def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
         vm_ip_dict = {}
         for line in novalist_results.split('\n'):
@@ -672,7 +717,7 @@ class VcpeCommon:
                 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
                 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
 
-            if len(self.get_vxlan_interfaces(ip)) > 0:
+            if self.get_vxlan_interfaces(ip):
                 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
                 return False
 
@@ -721,4 +766,3 @@ class VcpeCommon:
 
     def load_vgmux_vnf_name(self):
         return self.load_object(self.vgmux_vnf_name_file)
-