Add identity-url to region data in AAI
[integration.git] / test / vcpe / vcpecommon.py
index 0ee22da..ddbce4b 100755 (executable)
@@ -1,3 +1,5 @@
+#!/usr/bin/env python
+
 import json
 import logging
 import os
@@ -30,6 +32,11 @@ class VcpeCommon:
     # set the openstack cloud access credentials here
     oom_mode = True
 
+    #############################################################################################
+    # set the gra_api flag
+    # Mustn't be set to True until Frankfurt DGs are updated for GRA-API infrastructure
+    gra_api_flag= False
+
     ###########################
     # set Openstack credentials
     # CHANGEME part
@@ -38,10 +45,10 @@ class VcpeCommon:
         '--os-username': 'kxi',
         '--os-user-domain-id': 'default',
         '--os-project-domain-id': 'default',
-        '--os-tenant-id': 'bc43d50ffcb84750bac0c1707a9a765b' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
+        '--os-tenant-id': '712b6016580e410b9abfec9ca34953ce' 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-project-domain-name': 'Integration-Release-Daily' if oom_mode else 'Integration-SB-07',
         '--os-identity-api-version': '3'
     }
 
@@ -49,16 +56,18 @@ class VcpeCommon:
     # 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',
+        'oam_onap_net': 'oam_network_exxC' if oom_mode else 'oam_onap_lAky',
+        'oam_onap_subnet': 'oam_network_exxC' if oom_mode else 'oam_onap_lAky',
         'public_net': 'external',
         'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
     }
 
     #############################################################################
-    # set name of sdnc controller pod, prefix is taken from helm environment name
+    # Set name of Onap's k8s namespace and sdnc controller pod
     # CHANGEME part
-    sdnc_controller_pod = 'dev-sdnc-sdnc-0'
+    onap_namespace = 'onap'
+    onap_environment = 'dev'
+    sdnc_controller_pod = '-'.join([onap_environment, 'sdnc-sdnc-0'])
 
     template_variable_symbol = '${'
     cpe_vm_prefix = 'zdcpe'
@@ -94,20 +103,20 @@ class VcpeCommon:
         self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
 
         # 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')
-        # 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.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
         # 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 = ['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
@@ -149,16 +158,16 @@ class VcpeCommon:
 
         #############################################################################################
         # SDC urls
-        self.sdc_be_port = '30205'
+        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 = 'http://' + self.hosts['sdc'] + ':' + self.sdc_be_port
+        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 = '30206'
+        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 = 'http://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
+        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'
 
@@ -168,15 +177,17 @@ 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 = '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:'
 
         #############################################################################################
@@ -196,7 +207,33 @@ class VcpeCommon:
         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'
+
+        #############################################################################################
+        # 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')
+
+        #############################################################################################
+        # 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 heatbridge(self, openstack_stack_name, svc_instance_uuid):
         """
         Add vserver information to AAI
         """
@@ -217,8 +254,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)
@@ -227,7 +272,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
@@ -235,9 +280,19 @@ 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)
@@ -276,14 +331,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):
@@ -323,6 +378,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
@@ -334,13 +456,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)
@@ -382,7 +503,7 @@ 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): ")
         return ret
 
     def get_pod_node_public_ip(self, pod):
@@ -404,7 +525,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 + "): ")
         return ret
 
     def get_vm_public_ip_by_nova(self, vm):
@@ -452,17 +573,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(['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
 
+    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'):