update link to upper-constraints.txt
[integration.git] / test / vcpe / vcpecommon.py
index 980092c..0e02987 100755 (executable)
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 import json
 import logging
 import os
@@ -8,94 +10,43 @@ 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
 
 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': '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'
-    }
-
-    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 = '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):
-        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)
 
+    def __init__(self, extra_host_names=None, cfg_file=None):
         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')
-        # 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')
-        # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
+        self.default_config = 'vcpeconfig.yaml'
+
+        # 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
+        self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip(self.sdnc_controller_pod)
+        # 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
+        # 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_port = '30227'
         self.so_nbi_port = '30277' if self.oom_mode else '8080'
-        self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
+        self.sdnc_preloading_port = '30267' if self.oom_mode else '8282'
         self.aai_query_port = '30233' if self.oom_mode else '8443'
         self.sniro_port = '30288' if self.oom_mode else '8080'
 
-        self.host_names = ['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
@@ -116,7 +67,6 @@ class VcpeCommon:
             'vfmodule': 'vcpe_vfmodule'
         }
         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
@@ -130,23 +80,45 @@ class VcpeCommon:
             "customerName": "some_company"
         }
 
+        #############################################################################################
+        # SDC urls
+        self.sdc_be_port = '30204'
+        self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
+        self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
+        self.sdc_be_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_be_port
+        self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
+
+        self.sdc_fe_port = '30207'
+        self.sdc_fe_request_userpass = 'beep', 'boop'
+        self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
+        self.sdc_fe_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
+        self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
+        self.sdc_create_allotted_resource_subcategory_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/category/resources/resourceNewCategory.allotted%20resource/subCategory'
+
         #############################################################################################
         # 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_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 = 'http://' + self.hosts['sdnc'] + \
+        self.sdnc_preload_network_url = 'https://' + 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_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 = 'http://' + self.hosts['sdnc'] + \
+        self.sdnc_preload_gra_url = 'https://' + 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 + \
+        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',
@@ -156,22 +128,101 @@ class VcpeCommon:
         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.so_db_pass = 'secretpassword'
+        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'}
         self.vpp_api_userpass = ('admin', 'admin')
         self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
 
-    def headbridge(self, openstack_stack_name, svc_instance_uuid):
+        #############################################################################################
+        # POLICY urls
+        self.policy_userpass = ('healthcheck', 'zb!XztG34')
+        self.policy_headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
+        self.policy_api_url = 'https://{0}:6969/policy/api/v1/policytypes/onap.policies.controlloop.Operational/versions/1.0.0/policies'
+        self.policy_pap_get_url = 'https://{0}:6969/policy/pap/v1/pdps'
+        self.policy_pap_json = {'policies': [{'policy-id': 'operational.vcpe'}]}
+        self.policy_pap_post_url = self.policy_pap_get_url + '/policies'
+        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
         """
         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:')
@@ -185,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)
@@ -195,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
@@ -203,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)
@@ -244,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):
@@ -291,6 +360,73 @@ class VcpeCommon:
             self.logger.error("Can't get subnet info from network name: " + network_name)
             return False
 
+    def set_closed_loop_policy(self, policy_template_file):
+        # Gather policy services cluster ips
+        p_api_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_api_service_name)
+        p_pap_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_pap_service_name)
+
+        # Read policy json from file
+        with open(policy_template_file) as f:
+            try:
+                policy_json = json.load(f)
+            except ValueError:
+                self.logger.error(policy_template_file + " doesn't seem to contain valid JSON data")
+                sys.exit(1)
+
+        # Check policy already applied
+        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)
+        if policy_exists_req.status_code != 200:
+            self.logger.error('Failure in checking CL policy existence. '
+                               'Policy-pap responded with HTTP code {0}'.format(
+                               policy_exists_req.status_code))
+            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(1)
+
+        try:
+            assert policy_exists_json['groups'][0]['pdpSubgroups'] \
+                               [1]['policies'][0]['name'] != 'operational.vcpe'
+        except AssertionError:
+            self.logger.info('vCPE closed loop policy already exists, not applying')
+            return
+        except IndexError:
+            pass # policy doesn't exist
+
+        # Create policy
+        policy_create_req = requests.post(self.policy_api_url.format(
+                            p_api_cluster_ip), auth=self.policy_userpass,
+                            json=policy_json, verify=False,
+                            headers=self.policy_headers)
+        # Get the policy id from policy-api response
+        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(1)
+
+        try:
+            policy_version = json.loads(policy_create_req.text)['policy-version']
+        except (KeyError, ValueError):
+            self.logger.error('Policy API response not understood:')
+            self.logger.debug('\n' + str(policy_create_req.text))
+
+        # Inject the policy into Policy PAP
+        self.policy_pap_json['policies'].append({'policy-version': policy_version})
+        policy_insert_req = requests.post(self.policy_pap_post_url.format(
+                            p_pap_cluster_ip), auth=self.policy_userpass,
+                            json=self.policy_pap_json, verify=False,
+                            headers=self.policy_headers)
+        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(1)
+        self.logger.info('Successully pushed closed loop Policy')
+
     def is_node_in_aai(self, node_type, node_uuid):
         key = None
         search_node_type = None
@@ -302,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)
@@ -323,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)
         """
@@ -350,12 +485,12 @@ class VcpeCommon:
                 break
 
         if ret is None:
-            ret = raw_input("Enter sdnc-sdnc-0 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)
         """
@@ -372,7 +507,7 @@ class VcpeCommon:
                 break
 
         if ret is None:
-            ret = raw_input("Enter sdnc-sdnc-0 pod cluster node public IP address(i.e. 10.12.0.0/16): ")
+            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):
@@ -382,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
@@ -420,17 +555,60 @@ 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(['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
 
+    def get_k8s_service_cluster_ip(self, service):
+        """
+        Returns cluster IP for a given service
+        :param service: name of the service
+        :return: cluster ip
+        """
+        config.load_kube_config()
+        api = client.CoreV1Api()
+        kslogger = logging.getLogger('kubernetes')
+        kslogger.setLevel(logging.INFO)
+        try:
+            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(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'):
@@ -539,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
 
@@ -588,4 +766,3 @@ class VcpeCommon:
 
     def load_vgmux_vnf_name(self):
         return self.load_object(self.vgmux_vnf_name_file)
-