blob: 4e00aae6ccb3fe17daff432073c9ffdcca800dbe [file] [log] [blame]
Kang Xi11d278c2018-04-06 16:56:04 -04001import json
2import logging
3import os
4import pickle
5import re
6import sys
7
8import ipaddress
9import mysql.connector
10import requests
11import commands
12import time
Yang Xu65b84a42018-12-31 17:48:02 +000013from novaclient import client as openstackclient
Yang Xu64339a82018-12-30 05:32:21 +000014from kubernetes import client, config
Yang Xu65b84a42018-12-31 17:48:02 +000015from netaddr import IPAddress, IPNetwork
Kang Xi11d278c2018-04-06 16:56:04 -040016
Michal Ptacek0b06f782019-09-12 12:27:47 +000017######################################################################
18# Parts which must be updated / cross-checked during each deployment #
19# are marked as CHANGEME #
20######################################################################
21
Kang Xi11d278c2018-04-06 16:56:04 -040022class VcpeCommon:
23 #############################################################################################
Michal Ptacek0b06f782019-09-12 12:27:47 +000024 # Set network prefix of k8s host external address; it's used for pod public IP autodetection
25 # but can be overriden from user in case of autodetection failure
Kang Xi11d278c2018-04-06 16:56:04 -040026 external_net_addr = '10.12.0.0'
27 external_net_prefix_len = 16
Bartek Grzybowski73b02ed2019-09-12 14:04:59 +020028
Kang Xi11d278c2018-04-06 16:56:04 -040029 #############################################################################################
30 # set the openstack cloud access credentials here
Yang Xu75b0f5e2018-11-14 14:04:20 -050031 oom_mode = True
Kang Xi0e0a1d62018-07-23 16:53:54 -040032
Michal Ptacek0b06f782019-09-12 12:27:47 +000033 ###########################
34 # set Openstack credentials
35 # CHANGEME part
Kang Xi11d278c2018-04-06 16:56:04 -040036 cloud = {
37 '--os-auth-url': 'http://10.12.25.2:5000',
Kang Xi268b74d2018-05-23 15:53:42 -040038 '--os-username': 'kxi',
Kang Xi11d278c2018-04-06 16:56:04 -040039 '--os-user-domain-id': 'default',
40 '--os-project-domain-id': 'default',
Yang Xu27e16242018-12-28 01:00:50 -050041 '--os-tenant-id': 'bc43d50ffcb84750bac0c1707a9a765b' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
Kang Xi11d278c2018-04-06 16:56:04 -040042 '--os-region-name': 'RegionOne',
Kang Xi268b74d2018-05-23 15:53:42 -040043 '--os-password': 'n3JhGMGuDzD8',
Yang Xu27e16242018-12-28 01:00:50 -050044 '--os-project-domain-name': 'Integration-SB-03' if oom_mode else 'Integration-SB-07',
Kang Xi11d278c2018-04-06 16:56:04 -040045 '--os-identity-api-version': '3'
46 }
47
Michal Ptacek0b06f782019-09-12 12:27:47 +000048 ############################################################################
49 # set oam and public network which must exist in openstack before deployment
50 # CHANGEME part
Kang Xi11d278c2018-04-06 16:56:04 -040051 common_preload_config = {
Yang Xu27e16242018-12-28 01:00:50 -050052 'oam_onap_net': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
53 'oam_onap_subnet': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
Kang Xi11d278c2018-04-06 16:56:04 -040054 'public_net': 'external',
55 'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
56 }
Yang Xu75b0f5e2018-11-14 14:04:20 -050057
Michal Ptacek0b06f782019-09-12 12:27:47 +000058 #############################################################################
59 # set name of sdnc controller pod, prefix is taken from helm environment name
60 # CHANGEME part
61 sdnc_controller_pod = 'dev-sdnc-sdnc-0'
Kang Xi11d278c2018-04-06 16:56:04 -040062
63 template_variable_symbol = '${'
Kang Xi6c762392018-05-30 09:27:34 -040064 cpe_vm_prefix = 'zdcpe'
Michal Ptacek0b06f782019-09-12 12:27:47 +000065
Kang Xi11d278c2018-04-06 16:56:04 -040066 #############################################################################################
67 # preloading network config
68 # key=network role
69 # value = [subnet_start_ip, subnet_gateway_ip]
70 preload_network_config = {
71 'cpe_public': ['10.2.0.2', '10.2.0.1'],
72 'cpe_signal': ['10.4.0.2', '10.4.0.1'],
73 'brg_bng': ['10.3.0.2', '10.3.0.1'],
74 'bng_mux': ['10.1.0.10', '10.1.0.1'],
75 'mux_gw': ['10.5.0.10', '10.5.0.1']
76 }
77
Kang Xi268b74d2018-05-23 15:53:42 -040078 dcae_ves_collector_name = 'dcae-bootstrap'
Kang Xi11d278c2018-04-06 16:56:04 -040079 global_subscriber_id = 'SDN-ETHERNET-INTERNET'
Kang Xi268b74d2018-05-23 15:53:42 -040080 project_name = 'Project-Demonstration'
81 owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
Yang Xu86c0e4a2018-12-02 13:10:41 -050082 owning_entity_name = 'OE-Demonstration1'
Kang Xi11d278c2018-04-06 16:56:04 -040083
84 def __init__(self, extra_host_names=None):
85 self.logger = logging.getLogger(__name__)
Yang Xu65b84a42018-12-31 17:48:02 +000086 self.logger.setLevel(logging.DEBUG)
Kang Xi11d278c2018-04-06 16:56:04 -040087 self.logger.info('Initializing configuration')
88
Michal Ptacek0b06f782019-09-12 12:27:47 +000089 ##################################################################################################################################
90 # following param must be updated e.g. from csar file (grep for VfModuleModelInvariantUuid string) before vcpe.py customer call !!
91 # vgw_VfModuleModelInvariantUuid is in rescust service csar,
92 # look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automatically
93 # CHANGEME part
Yang Xu27e16242018-12-28 01:00:50 -050094 self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
Michal Ptacek0b06f782019-09-12 12:27:47 +000095
96 # 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
Bartek Grzybowski863af9a2019-09-13 08:54:14 +020097 self.sdnc_oam_ip = self.get_pod_node_oam_ip(self.sdnc_controller_pod)
98 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
99 self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip(self.sdnc_controller_pod)
Michal Ptacek0b06f782019-09-12 12:27:47 +0000100 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +0000101 self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
Michal Ptacek0b06f782019-09-12 12:27:47 +0000102 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +0000103 self.mr_ip_addr = self.oom_so_sdnc_aai_ip
Brian Freeman8076a872018-11-13 11:34:48 -0500104 self.mr_ip_port = '30227'
Brian Freemana605bc72018-11-12 10:50:30 -0500105 self.so_nbi_port = '30277' if self.oom_mode else '8080'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400106 self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
107 self.aai_query_port = '30233' if self.oom_mode else '8443'
108 self.sniro_port = '30288' if self.oom_mode else '8080'
109
Yang Xuc52ed6e2019-04-29 00:20:52 -0400110 self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
Kang Xi11d278c2018-04-06 16:56:04 -0400111 if extra_host_names:
112 self.host_names.extend(extra_host_names)
113 # get IP addresses
114 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
115 # this is the keyword used to name vgw stack, must not be used in other stacks
116 self.vgw_name_keyword = 'base_vcpe_vgw'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500117 # this is the file that will keep the index of last assigned SO name
118 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
Kang Xi11d278c2018-04-06 16:56:04 -0400119 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
120 self.preload_dict_file = '__var/preload_dict'
121 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
122 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
123 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
124 self.instance_name_prefix = {
125 'service': 'vcpe_svc',
126 'network': 'vcpe_net',
127 'vnf': 'vcpe_vnf',
128 'vfmodule': 'vcpe_vfmodule'
129 }
130 self.aai_userpass = 'AAI', 'AAI'
Michal Ptacek0b06f782019-09-12 12:27:47 +0000131
132 ############################################################################################################
133 # following key is overriding public key from vCPE heat templates, it's important to use correct one in here
134 # CHANGEME part
Kang Xi11d278c2018-04-06 16:56:04 -0400135 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
Michal Ptacek0b06f782019-09-12 12:27:47 +0000136
Kang Xi11d278c2018-04-06 16:56:04 -0400137 self.os_tenant_id = self.cloud['--os-tenant-id']
138 self.os_region_name = self.cloud['--os-region-name']
139 self.common_preload_config['pub_key'] = self.pub_key
Kang Xi0e0a1d62018-07-23 16:53:54 -0400140 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
Kang Xi11d278c2018-04-06 16:56:04 -0400141 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Kang Xi268b74d2018-05-23 15:53:42 -0400142 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
143# self.homing_solution = 'oof'
144 self.customer_location_used_by_oof = {
145 "customerLatitude": "32.897480",
146 "customerLongitude": "-97.040443",
147 "customerName": "some_company"
148 }
Kang Xi11d278c2018-04-06 16:56:04 -0400149
150 #############################################################################################
Yang Xuc52ed6e2019-04-29 00:20:52 -0400151 # SDC urls
Yang Xu0e319ef2019-04-30 14:28:07 -0400152 self.sdc_be_port = '30205'
153 self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
154 self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
155 self.sdc_be_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_be_port
156 self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
157
158 self.sdc_fe_port = '30206'
159 self.sdc_fe_request_userpass = 'beep', 'boop'
160 self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
161 self.sdc_fe_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
162 self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
163 self.sdc_create_allotted_resource_subcategory_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/category/resources/resourceNewCategory.allotted%20resource/subCategory'
Yang Xuc52ed6e2019-04-29 00:20:52 -0400164
165 #############################################################################################
Kang Xi11d278c2018-04-06 16:56:04 -0400166 # SDNC urls
167 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
168 self.sdnc_db_name = 'sdnctl'
169 self.sdnc_db_user = 'sdnctl'
170 self.sdnc_db_pass = 'gamma'
Kang Xi268b74d2018-05-23 15:53:42 -0400171 self.sdnc_db_port = '32774'
Kang Xi11d278c2018-04-06 16:56:04 -0400172 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
173 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400174 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
Kang Xi11d278c2018-04-06 16:56:04 -0400175 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400176 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500177 self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
178 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400179 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
180 '/restconf/config/GENERIC-RESOURCE-API:'
Kang Xi11d278c2018-04-06 16:56:04 -0400181
182 #############################################################################################
183 # SO urls, note: do NOT add a '/' at the end of the url
Brian Freemana605bc72018-11-12 10:50:30 -0500184 self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
Yang Xu63a0afd2018-11-20 16:01:01 -0500185 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
Brian Freemana605bc72018-11-12 10:50:30 -0500186 self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
Kang Xi11d278c2018-04-06 16:56:04 -0400187 self.so_userpass = 'InfraPortalClient', 'password1$'
188 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Brian Freemana605bc72018-11-12 10:50:30 -0500189 self.so_db_name = 'catalogdb'
Kang Xi11d278c2018-04-06 16:56:04 -0400190 self.so_db_user = 'root'
Yang Xu21b09c92019-06-13 13:19:20 -0400191 self.so_db_pass = 'secretpassword'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400192 self.so_db_port = '30252' if self.oom_mode else '32769'
Kang Xi11d278c2018-04-06 16:56:04 -0400193
194 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
195 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
196 self.vpp_api_userpass = ('admin', 'admin')
197 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
198
Bartek Grzybowskibfeeb242019-09-13 08:35:48 +0200199 def heatbridge(self, openstack_stack_name, svc_instance_uuid):
Kang Xi11d278c2018-04-06 16:56:04 -0400200 """
201 Add vserver information to AAI
202 """
203 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
Kang Xi0e0a1d62018-07-23 16:53:54 -0400204 if not self.oom_mode:
205 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
206 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
207 self.logger.debug('%s', ret)
208 else:
209 print('To add vGMUX vserver info to AAI, do the following:')
210 print('- ssh to rancher')
211 print('- sudo su -')
212 print('- cd /root/oom/kubernetes/robot')
213 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
Kang Xi11d278c2018-04-06 16:56:04 -0400214
215 def get_brg_mac_from_sdnc(self):
216 """
Kang Xi6c762392018-05-30 09:27:34 -0400217 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
218 Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
Kang Xi11d278c2018-04-06 16:56:04 -0400219 """
220 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
221 host=self.hosts['sdnc'], port=self.sdnc_db_port)
222 cursor = cnx.cursor()
223 query = "SELECT * from DHCP_MAP"
224 cursor.execute(query)
225
226 self.logger.debug('DHCP_MAP table in SDNC')
Kang Xi6c762392018-05-30 09:27:34 -0400227 mac_recent = None
228 host = -1
Kang Xi11d278c2018-04-06 16:56:04 -0400229 for mac, ip in cursor:
Kang Xi11d278c2018-04-06 16:56:04 -0400230 self.logger.debug(mac + ':' + ip)
Kang Xi6c762392018-05-30 09:27:34 -0400231 this_host = int(ip.split('.')[-1])
232 if host < this_host:
233 host = this_host
234 mac_recent = mac
Kang Xi11d278c2018-04-06 16:56:04 -0400235
236 cnx.close()
237
Kang Xi6c762392018-05-30 09:27:34 -0400238 assert mac_recent
239 return mac_recent
Kang Xi11d278c2018-04-06 16:56:04 -0400240
Kang Xi6c762392018-05-30 09:27:34 -0400241 def execute_cmds_sdnc_db(self, cmds):
242 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
243 self.hosts['sdnc'], self.sdnc_db_port)
Kang Xi11d278c2018-04-06 16:56:04 -0400244
Kang Xi6c762392018-05-30 09:27:34 -0400245 def execute_cmds_so_db(self, cmds):
246 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
247 self.hosts['so'], self.so_db_port)
248
249 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
250 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
Kang Xi11d278c2018-04-06 16:56:04 -0400251 cursor = cnx.cursor()
252 for cmd in cmds:
253 self.logger.debug(cmd)
254 cursor.execute(cmd)
255 self.logger.debug('%s', cursor)
256 cnx.commit()
257 cursor.close()
258 cnx.close()
259
260 def find_file(self, file_name_keyword, file_ext, search_dir):
261 """
262 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
263 :param file_ext: e.g., csar, json
264 :param search_dir path to search
265 :return: path name of the file
266 """
267 file_name_keyword = file_name_keyword.lower()
268 file_ext = file_ext.lower()
269 if not file_ext.startswith('.'):
270 file_ext = '.' + file_ext
271
272 filenamepath = None
273 for file_name in os.listdir(search_dir):
274 file_name_lower = file_name.lower()
275 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
276 if filenamepath:
277 self.logger.error('Multiple files found for *{0}*.{1} in '
278 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
279 sys.exit()
280 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
281
282 if filenamepath:
283 return filenamepath
284 else:
285 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
286 sys.exit()
287
288 @staticmethod
289 def network_name_to_subnet_name(network_name):
290 """
291 :param network_name: example: vcpe_net_cpe_signal_201711281221
292 :return: vcpe_net_cpe_signal_subnet_201711281221
293 """
294 fields = network_name.split('_')
295 fields.insert(-1, 'subnet')
296 return '_'.join(fields)
297
298 def set_network_name(self, network_name):
299 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
300 openstackcmd = 'openstack ' + param
301 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
302 os.popen(cmd)
303
304 def set_subnet_name(self, network_name):
305 """
306 Example: network_name = vcpe_net_cpe_signal_201711281221
307 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
308 :return:
309 """
310 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
311 openstackcmd = 'openstack ' + param
312
313 # expected results: | subnets | subnet_id |
314 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
315 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
316 subnet_id = subnet_info[2].strip()
317 subnet_name = self.network_name_to_subnet_name(network_name)
318 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
319 os.popen(cmd)
320 self.logger.info("Subnet name set to: " + subnet_name)
321 return True
322 else:
323 self.logger.error("Can't get subnet info from network name: " + network_name)
324 return False
325
326 def is_node_in_aai(self, node_type, node_uuid):
327 key = None
328 search_node_type = None
329 if node_type == 'service':
330 search_node_type = 'service-instance'
331 key = 'service-instance-id'
332 elif node_type == 'vnf':
333 search_node_type = 'generic-vnf'
334 key = 'vnf-id'
335 else:
336 logging.error('Invalid node_type: ' + node_type)
337 sys.exit()
338
Kang Xi0e0a1d62018-07-23 16:53:54 -0400339 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
340 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
Kang Xi11d278c2018-04-06 16:56:04 -0400341
Kang Xi268b74d2018-05-23 15:53:42 -0400342 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
Kang Xi11d278c2018-04-06 16:56:04 -0400343 requests.packages.urllib3.disable_warnings()
344 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
345 response = r.json()
346 self.logger.debug('aai query: ' + url)
347 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
348 return 'result-data' in response
349
350 @staticmethod
351 def extract_ip_from_str(net_addr, net_addr_len, sz):
352 """
353 :param net_addr: e.g. 10.5.12.0
354 :param net_addr_len: e.g. 24
355 :param sz: a string
356 :return: the first IP address matching the network, e.g. 10.5.12.3
357 """
358 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
359 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
360 for ip in ip_list:
361 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
362 if this_net == network:
363 return str(ip)
364 return None
365
Yang Xu75b0f5e2018-11-14 14:04:20 -0500366 def get_pod_node_oam_ip(self, pod):
367 """
Yang Xu64339a82018-12-30 05:32:21 +0000368 :Assuming kubectl is available and configured by default config (~/.kube/config)
369 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
370 :return pod's cluster node oam ip (10.0.0.0/16)
Yang Xu75b0f5e2018-11-14 14:04:20 -0500371 """
Yang Xu64339a82018-12-30 05:32:21 +0000372 ret = None
373 config.load_kube_config()
374 api = client.CoreV1Api()
375 kslogger = logging.getLogger('kubernetes')
376 kslogger.setLevel(logging.INFO)
377 res = api.list_pod_for_all_namespaces()
378 for i in res.items:
379 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000380 self.logger.debug("found {0}\t{1}\t{2}".format(i.metadata.name, i.status.host_ip, i.spec.node_name))
Yang Xu64339a82018-12-30 05:32:21 +0000381 ret = i.status.host_ip
382 break
383
384 if ret is None:
Bartek Grzybowski863af9a2019-09-13 08:54:14 +0200385 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node OAM IP address(10.0.0.0/16): ")
Yang Xu64339a82018-12-30 05:32:21 +0000386 return ret
387
388 def get_pod_node_public_ip(self, pod):
389 """
390 :Assuming kubectl is available and configured by default config (~/.kube/config)
391 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
392 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
393 """
394 ret = None
395 config.load_kube_config()
396 api = client.CoreV1Api()
397 kslogger = logging.getLogger('kubernetes')
398 kslogger.setLevel(logging.INFO)
399 res = api.list_pod_for_all_namespaces()
400 for i in res.items:
401 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000402 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
403 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
Yang Xu64339a82018-12-30 05:32:21 +0000404 break
405
406 if ret is None:
Bartek Grzybowski863af9a2019-09-13 08:54:14 +0200407 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node public IP address(i.e. " + self.external_net_addr + "): ")
Yang Xu75b0f5e2018-11-14 14:04:20 -0500408 return ret
409
Yang Xu65b84a42018-12-31 17:48:02 +0000410 def get_vm_public_ip_by_nova(self, vm):
411 """
412 This method uses openstack nova api to retrieve vm public ip
413 :param vm: vm name
414 :return vm public ip
415 """
416 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
417 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
418 for i in nova.servers.list():
419 if i.name == vm:
420 for k, v in i.networks.items():
421 for ip in v:
422 if IPAddress(ip) in subnet:
423 return ip
424 return None
425
Kang Xi11d278c2018-04-06 16:56:04 -0400426 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
427 """
428 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
429 :param net_addr: e.g. 10.12.5.0
430 :param net_addr_len: e.g. 24
431 :return: dictionary {keyword: ip}
432 """
433 if not net_addr:
434 net_addr = self.external_net_addr
435
436 if not net_addr_len:
437 net_addr_len = self.external_net_prefix_len
438
439 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
440 openstackcmd = 'nova ' + param + ' list'
441 self.logger.debug(openstackcmd)
442
Kang Xi11d278c2018-04-06 16:56:04 -0400443 results = os.popen(openstackcmd).read()
Kang Xi6c762392018-05-30 09:27:34 -0400444 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
445 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
446 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
447 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
Kang Xi0e0a1d62018-07-23 16:53:54 -0400448 if self.oom_mode:
449 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
Kang Xi6c762392018-05-30 09:27:34 -0400450
Kang Xi11d278c2018-04-06 16:56:04 -0400451 if len(ip_dict) != len(keywords):
452 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
453 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
Yang Xu64339a82018-12-30 05:32:21 +0000454 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
Kang Xi268b74d2018-05-23 15:53:42 -0400455# sys.exit()
Kang Xi11d278c2018-04-06 16:56:04 -0400456 return ip_dict
457
Kang Xi0e0a1d62018-07-23 16:53:54 -0400458 def get_oom_onap_vm_ip(self, keywords):
459 vm_ip = {}
Yang Xuc52ed6e2019-04-29 00:20:52 -0400460 onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
Kang Xi0e0a1d62018-07-23 16:53:54 -0400461 for vm in keywords:
462 if vm in onap_vm_list:
463 vm_ip[vm] = self.oom_so_sdnc_aai_ip
464 return vm_ip
465
Kang Xi6c762392018-05-30 09:27:34 -0400466 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
467 vm_ip_dict = {}
468 for line in novalist_results.split('\n'):
469 fields = line.split('|')
470 if len(fields) == 8:
471 vm_name = fields[2]
472 ip_info = fields[-2]
473 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
474 vm_ip_dict[vm_name] = ip
475
476 return vm_ip_dict
477
478 def remove_old_vms(self, vm_list, prefix):
479 """
480 For vms with format name_timestamp, only keep the one with the latest timestamp.
481 E.g.,
482 zdcpe1cpe01brgemu01_201805222148 (drop this)
483 zdcpe1cpe01brgemu01_201805222229 (keep this)
484 zdcpe1cpe01gw01_201805162201
485 """
486 new_vm_list = []
487 same_type_vm_dict = {}
488 for vm in vm_list:
489 fields = vm.split('_')
490 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
491 if vm > same_type_vm_dict.get(fields[0], '0'):
492 same_type_vm_dict[fields[0]] = vm
493 else:
494 new_vm_list.append(vm)
495
496 new_vm_list.extend(same_type_vm_dict.values())
497 return new_vm_list
498
499 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
500 vm_ip_dict = {}
501 for keyword in vm_name_keyword_list:
502 for vm, ip in all_vm_ip_dict.items():
503 if keyword in vm:
504 vm_ip_dict[keyword] = ip
505 break
506 return vm_ip_dict
507
Kang Xi11d278c2018-04-06 16:56:04 -0400508 def del_vgmux_ves_mode(self):
509 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
510 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
511 self.logger.debug('%s', r)
512
513 def del_vgmux_ves_collector(self):
514 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
515 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
516 self.logger.debug('%s', r)
517
518 def set_vgmux_ves_collector(self ):
519 url = self.vpp_ves_url.format(self.hosts['mux'])
520 data = {'config':
Kang Xi268b74d2018-05-23 15:53:42 -0400521 {'server-addr': self.hosts[self.dcae_ves_collector_name],
Kang Xi0e0a1d62018-07-23 16:53:54 -0400522 'server-port': '30235' if self.oom_mode else '8081',
Kang Xi11d278c2018-04-06 16:56:04 -0400523 'read-interval': '10',
524 'is-add':'1'
525 }
526 }
527 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
528 self.logger.debug('%s', r)
529
530 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
531 url = self.vpp_ves_url.format(self.hosts['mux'])
532 data = {"mode":
533 {"working-mode": "demo",
534 "base-packet-loss": str(lossrate),
535 "source-name": vg_vnf_instance_name
536 }
537 }
538 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
539 self.logger.debug('%s', r)
540
541 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
542 def get_vxlan_interfaces(self, ip, print_info=False):
543 url = self.vpp_inf_url.format(ip)
544 self.logger.debug('url is this: %s', url)
545 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
546 data = r.json()['interfaces']['interface']
547 if print_info:
548 for inf in data:
549 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
550 print(json.dumps(inf, indent=4, sort_keys=True))
551
552 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
553
554 # delete all VxLAN interfaces of each hosts
555 def delete_vxlan_interfaces(self, host_dic):
556 for host, ip in host_dic.items():
557 deleted = False
558 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
559 inf_list = self.get_vxlan_interfaces(ip)
560 for inf in inf_list:
561 deleted = True
562 time.sleep(2)
563 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
564 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
565 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
566
567 for inf in inf_list:
568 deleted = True
569 time.sleep(2)
570 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
571 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
572 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
573
574 if len(self.get_vxlan_interfaces(ip)) > 0:
575 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
576 return False
577
578 if not deleted:
579 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
580 return True
581
582 @staticmethod
583 def save_object(obj, filepathname):
584 with open(filepathname, 'wb') as fout:
585 pickle.dump(obj, fout)
586
587 @staticmethod
588 def load_object(filepathname):
589 with open(filepathname, 'rb') as fin:
590 return pickle.load(fin)
591
Kang Xi6c762392018-05-30 09:27:34 -0400592 @staticmethod
593 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
594 with open(vnf_template_file) as json_input:
595 json_data = json.load(json_input)
596 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
597 for param in param_list:
598 if param['vnf-parameter-name'] in vnf_parameter_name_list:
599 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
600 number = int(ipaddr_or_vni[-1])
601 if 254 == number:
602 number = 10
603 else:
604 number = number + 1
605 ipaddr_or_vni[-1] = str(number)
606 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
607
608 assert json_data is not None
609 with open(vnf_template_file, 'w') as json_output:
610 json.dump(json_data, json_output, indent=4, sort_keys=True)
611
Kang Xi11d278c2018-04-06 16:56:04 -0400612 def save_preload_data(self, preload_data):
613 self.save_object(preload_data, self.preload_dict_file)
614
615 def load_preload_data(self):
616 return self.load_object(self.preload_dict_file)
617
618 def save_vgmux_vnf_name(self, vgmux_vnf_name):
619 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
620
621 def load_vgmux_vnf_name(self):
622 return self.load_object(self.vgmux_vnf_name_file)
623