blob: b0a17bc2e609f1f2334881979d649bcd26221a14 [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
17class VcpeCommon:
18 #############################################################################################
19 # Start: configurations that you must change for a new ONAP installation
20 external_net_addr = '10.12.0.0'
21 external_net_prefix_len = 16
22 #############################################################################################
23 # set the openstack cloud access credentials here
Yang Xu75b0f5e2018-11-14 14:04:20 -050024 oom_mode = True
Kang Xi0e0a1d62018-07-23 16:53:54 -040025
Kang Xi11d278c2018-04-06 16:56:04 -040026 cloud = {
27 '--os-auth-url': 'http://10.12.25.2:5000',
Kang Xi268b74d2018-05-23 15:53:42 -040028 '--os-username': 'kxi',
Kang Xi11d278c2018-04-06 16:56:04 -040029 '--os-user-domain-id': 'default',
30 '--os-project-domain-id': 'default',
Yang Xu27e16242018-12-28 01:00:50 -050031 '--os-tenant-id': 'bc43d50ffcb84750bac0c1707a9a765b' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
Kang Xi11d278c2018-04-06 16:56:04 -040032 '--os-region-name': 'RegionOne',
Kang Xi268b74d2018-05-23 15:53:42 -040033 '--os-password': 'n3JhGMGuDzD8',
Yang Xu27e16242018-12-28 01:00:50 -050034 '--os-project-domain-name': 'Integration-SB-03' if oom_mode else 'Integration-SB-07',
Kang Xi11d278c2018-04-06 16:56:04 -040035 '--os-identity-api-version': '3'
36 }
37
38 common_preload_config = {
Yang Xu27e16242018-12-28 01:00:50 -050039 'oam_onap_net': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
40 'oam_onap_subnet': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
Kang Xi11d278c2018-04-06 16:56:04 -040041 'public_net': 'external',
42 'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
43 }
Yang Xu75b0f5e2018-11-14 14:04:20 -050044 sdnc_controller_pod = 'dev-sdnc-sdnc-0'
45
Kang Xi11d278c2018-04-06 16:56:04 -040046 #############################################################################################
47
48 template_variable_symbol = '${'
Kang Xi6c762392018-05-30 09:27:34 -040049 cpe_vm_prefix = 'zdcpe'
Kang Xi11d278c2018-04-06 16:56:04 -040050 #############################################################################################
51 # preloading network config
52 # key=network role
53 # value = [subnet_start_ip, subnet_gateway_ip]
54 preload_network_config = {
55 'cpe_public': ['10.2.0.2', '10.2.0.1'],
56 'cpe_signal': ['10.4.0.2', '10.4.0.1'],
57 'brg_bng': ['10.3.0.2', '10.3.0.1'],
58 'bng_mux': ['10.1.0.10', '10.1.0.1'],
59 'mux_gw': ['10.5.0.10', '10.5.0.1']
60 }
61
Kang Xi268b74d2018-05-23 15:53:42 -040062 dcae_ves_collector_name = 'dcae-bootstrap'
Kang Xi11d278c2018-04-06 16:56:04 -040063 global_subscriber_id = 'SDN-ETHERNET-INTERNET'
Kang Xi268b74d2018-05-23 15:53:42 -040064 project_name = 'Project-Demonstration'
65 owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
Yang Xu86c0e4a2018-12-02 13:10:41 -050066 owning_entity_name = 'OE-Demonstration1'
Kang Xi11d278c2018-04-06 16:56:04 -040067
68 def __init__(self, extra_host_names=None):
69 self.logger = logging.getLogger(__name__)
Yang Xu65b84a42018-12-31 17:48:02 +000070 self.logger.setLevel(logging.DEBUG)
Kang Xi11d278c2018-04-06 16:56:04 -040071 self.logger.info('Initializing configuration')
72
Yang Xu27e16242018-12-28 01:00:50 -050073 # CHANGEME: vgw_VfModuleModelInvariantUuid is in rescust service csar, look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automcatically
74 self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
75 # 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
Yang Xu64339a82018-12-30 05:32:21 +000076 self.sdnc_oam_ip = self.get_pod_node_oam_ip('sdnc-sdnc-0')
Yang Xu27e16242018-12-28 01:00:50 -050077 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu64339a82018-12-30 05:32:21 +000078 self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip('sdnc-sdnc-0')
Yang Xu27e16242018-12-28 01:00:50 -050079 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +000080 self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
Yang Xu27e16242018-12-28 01:00:50 -050081 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +000082 self.mr_ip_addr = self.oom_so_sdnc_aai_ip
Brian Freeman8076a872018-11-13 11:34:48 -050083 self.mr_ip_port = '30227'
Brian Freemana605bc72018-11-12 10:50:30 -050084 self.so_nbi_port = '30277' if self.oom_mode else '8080'
Kang Xi0e0a1d62018-07-23 16:53:54 -040085 self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
86 self.aai_query_port = '30233' if self.oom_mode else '8443'
87 self.sniro_port = '30288' if self.oom_mode else '8080'
88
Yang Xuc52ed6e2019-04-29 00:20:52 -040089 self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
Kang Xi11d278c2018-04-06 16:56:04 -040090 if extra_host_names:
91 self.host_names.extend(extra_host_names)
92 # get IP addresses
93 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
94 # this is the keyword used to name vgw stack, must not be used in other stacks
95 self.vgw_name_keyword = 'base_vcpe_vgw'
Brian Freeman81f6e9e2018-11-11 22:36:20 -050096 # this is the file that will keep the index of last assigned SO name
97 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
Kang Xi11d278c2018-04-06 16:56:04 -040098 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
99 self.preload_dict_file = '__var/preload_dict'
100 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
101 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
102 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
103 self.instance_name_prefix = {
104 'service': 'vcpe_svc',
105 'network': 'vcpe_net',
106 'vnf': 'vcpe_vnf',
107 'vfmodule': 'vcpe_vfmodule'
108 }
109 self.aai_userpass = 'AAI', 'AAI'
110 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
111 self.os_tenant_id = self.cloud['--os-tenant-id']
112 self.os_region_name = self.cloud['--os-region-name']
113 self.common_preload_config['pub_key'] = self.pub_key
Kang Xi0e0a1d62018-07-23 16:53:54 -0400114 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
Kang Xi11d278c2018-04-06 16:56:04 -0400115 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Kang Xi268b74d2018-05-23 15:53:42 -0400116 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
117# self.homing_solution = 'oof'
118 self.customer_location_used_by_oof = {
119 "customerLatitude": "32.897480",
120 "customerLongitude": "-97.040443",
121 "customerName": "some_company"
122 }
Kang Xi11d278c2018-04-06 16:56:04 -0400123
124 #############################################################################################
Yang Xuc52ed6e2019-04-29 00:20:52 -0400125 # SDC urls
Yang Xu0e319ef2019-04-30 14:28:07 -0400126 self.sdc_be_port = '30205'
127 self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
128 self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
129 self.sdc_be_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_be_port
130 self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
131
132 self.sdc_fe_port = '30206'
133 self.sdc_fe_request_userpass = 'beep', 'boop'
134 self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
135 self.sdc_fe_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
136 self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
137 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 -0400138
139 #############################################################################################
Kang Xi11d278c2018-04-06 16:56:04 -0400140 # SDNC urls
141 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
142 self.sdnc_db_name = 'sdnctl'
143 self.sdnc_db_user = 'sdnctl'
144 self.sdnc_db_pass = 'gamma'
Kang Xi268b74d2018-05-23 15:53:42 -0400145 self.sdnc_db_port = '32774'
Kang Xi11d278c2018-04-06 16:56:04 -0400146 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
147 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400148 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
Kang Xi11d278c2018-04-06 16:56:04 -0400149 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400150 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500151 self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
152 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400153 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
154 '/restconf/config/GENERIC-RESOURCE-API:'
Kang Xi11d278c2018-04-06 16:56:04 -0400155
156 #############################################################################################
157 # SO urls, note: do NOT add a '/' at the end of the url
Brian Freemana605bc72018-11-12 10:50:30 -0500158 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 -0500159 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
Brian Freemana605bc72018-11-12 10:50:30 -0500160 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 -0400161 self.so_userpass = 'InfraPortalClient', 'password1$'
162 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Brian Freemana605bc72018-11-12 10:50:30 -0500163 self.so_db_name = 'catalogdb'
Kang Xi11d278c2018-04-06 16:56:04 -0400164 self.so_db_user = 'root'
Yang Xu21b09c92019-06-13 13:19:20 -0400165 self.so_db_pass = 'secretpassword'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400166 self.so_db_port = '30252' if self.oom_mode else '32769'
Kang Xi11d278c2018-04-06 16:56:04 -0400167
168 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
169 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
170 self.vpp_api_userpass = ('admin', 'admin')
171 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
172
173 def headbridge(self, openstack_stack_name, svc_instance_uuid):
174 """
175 Add vserver information to AAI
176 """
177 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
Kang Xi0e0a1d62018-07-23 16:53:54 -0400178 if not self.oom_mode:
179 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
180 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
181 self.logger.debug('%s', ret)
182 else:
183 print('To add vGMUX vserver info to AAI, do the following:')
184 print('- ssh to rancher')
185 print('- sudo su -')
186 print('- cd /root/oom/kubernetes/robot')
187 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
Kang Xi11d278c2018-04-06 16:56:04 -0400188
189 def get_brg_mac_from_sdnc(self):
190 """
Kang Xi6c762392018-05-30 09:27:34 -0400191 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
192 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 -0400193 """
194 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
195 host=self.hosts['sdnc'], port=self.sdnc_db_port)
196 cursor = cnx.cursor()
197 query = "SELECT * from DHCP_MAP"
198 cursor.execute(query)
199
200 self.logger.debug('DHCP_MAP table in SDNC')
Kang Xi6c762392018-05-30 09:27:34 -0400201 mac_recent = None
202 host = -1
Kang Xi11d278c2018-04-06 16:56:04 -0400203 for mac, ip in cursor:
Kang Xi11d278c2018-04-06 16:56:04 -0400204 self.logger.debug(mac + ':' + ip)
Kang Xi6c762392018-05-30 09:27:34 -0400205 this_host = int(ip.split('.')[-1])
206 if host < this_host:
207 host = this_host
208 mac_recent = mac
Kang Xi11d278c2018-04-06 16:56:04 -0400209
210 cnx.close()
211
Kang Xi6c762392018-05-30 09:27:34 -0400212 assert mac_recent
213 return mac_recent
Kang Xi11d278c2018-04-06 16:56:04 -0400214
Kang Xi6c762392018-05-30 09:27:34 -0400215 def execute_cmds_sdnc_db(self, cmds):
216 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
217 self.hosts['sdnc'], self.sdnc_db_port)
Kang Xi11d278c2018-04-06 16:56:04 -0400218
Kang Xi6c762392018-05-30 09:27:34 -0400219 def execute_cmds_so_db(self, cmds):
220 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
221 self.hosts['so'], self.so_db_port)
222
223 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
224 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
Kang Xi11d278c2018-04-06 16:56:04 -0400225 cursor = cnx.cursor()
226 for cmd in cmds:
227 self.logger.debug(cmd)
228 cursor.execute(cmd)
229 self.logger.debug('%s', cursor)
230 cnx.commit()
231 cursor.close()
232 cnx.close()
233
234 def find_file(self, file_name_keyword, file_ext, search_dir):
235 """
236 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
237 :param file_ext: e.g., csar, json
238 :param search_dir path to search
239 :return: path name of the file
240 """
241 file_name_keyword = file_name_keyword.lower()
242 file_ext = file_ext.lower()
243 if not file_ext.startswith('.'):
244 file_ext = '.' + file_ext
245
246 filenamepath = None
247 for file_name in os.listdir(search_dir):
248 file_name_lower = file_name.lower()
249 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
250 if filenamepath:
251 self.logger.error('Multiple files found for *{0}*.{1} in '
252 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
253 sys.exit()
254 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
255
256 if filenamepath:
257 return filenamepath
258 else:
259 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
260 sys.exit()
261
262 @staticmethod
263 def network_name_to_subnet_name(network_name):
264 """
265 :param network_name: example: vcpe_net_cpe_signal_201711281221
266 :return: vcpe_net_cpe_signal_subnet_201711281221
267 """
268 fields = network_name.split('_')
269 fields.insert(-1, 'subnet')
270 return '_'.join(fields)
271
272 def set_network_name(self, network_name):
273 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
274 openstackcmd = 'openstack ' + param
275 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
276 os.popen(cmd)
277
278 def set_subnet_name(self, network_name):
279 """
280 Example: network_name = vcpe_net_cpe_signal_201711281221
281 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
282 :return:
283 """
284 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
285 openstackcmd = 'openstack ' + param
286
287 # expected results: | subnets | subnet_id |
288 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
289 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
290 subnet_id = subnet_info[2].strip()
291 subnet_name = self.network_name_to_subnet_name(network_name)
292 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
293 os.popen(cmd)
294 self.logger.info("Subnet name set to: " + subnet_name)
295 return True
296 else:
297 self.logger.error("Can't get subnet info from network name: " + network_name)
298 return False
299
300 def is_node_in_aai(self, node_type, node_uuid):
301 key = None
302 search_node_type = None
303 if node_type == 'service':
304 search_node_type = 'service-instance'
305 key = 'service-instance-id'
306 elif node_type == 'vnf':
307 search_node_type = 'generic-vnf'
308 key = 'vnf-id'
309 else:
310 logging.error('Invalid node_type: ' + node_type)
311 sys.exit()
312
Kang Xi0e0a1d62018-07-23 16:53:54 -0400313 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
314 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
Kang Xi11d278c2018-04-06 16:56:04 -0400315
Kang Xi268b74d2018-05-23 15:53:42 -0400316 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
Kang Xi11d278c2018-04-06 16:56:04 -0400317 requests.packages.urllib3.disable_warnings()
318 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
319 response = r.json()
320 self.logger.debug('aai query: ' + url)
321 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
322 return 'result-data' in response
323
324 @staticmethod
325 def extract_ip_from_str(net_addr, net_addr_len, sz):
326 """
327 :param net_addr: e.g. 10.5.12.0
328 :param net_addr_len: e.g. 24
329 :param sz: a string
330 :return: the first IP address matching the network, e.g. 10.5.12.3
331 """
332 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
333 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
334 for ip in ip_list:
335 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
336 if this_net == network:
337 return str(ip)
338 return None
339
Yang Xu75b0f5e2018-11-14 14:04:20 -0500340 def get_pod_node_oam_ip(self, pod):
341 """
Yang Xu64339a82018-12-30 05:32:21 +0000342 :Assuming kubectl is available and configured by default config (~/.kube/config)
343 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
344 :return pod's cluster node oam ip (10.0.0.0/16)
Yang Xu75b0f5e2018-11-14 14:04:20 -0500345 """
Yang Xu64339a82018-12-30 05:32:21 +0000346 ret = None
347 config.load_kube_config()
348 api = client.CoreV1Api()
349 kslogger = logging.getLogger('kubernetes')
350 kslogger.setLevel(logging.INFO)
351 res = api.list_pod_for_all_namespaces()
352 for i in res.items:
353 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000354 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 +0000355 ret = i.status.host_ip
356 break
357
358 if ret is None:
359 ret = raw_input("Enter sdnc-sdnc-0 pod cluster node OAM IP address(10.0.0.0/16): ")
360 return ret
361
362 def get_pod_node_public_ip(self, pod):
363 """
364 :Assuming kubectl is available and configured by default config (~/.kube/config)
365 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
366 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
367 """
368 ret = None
369 config.load_kube_config()
370 api = client.CoreV1Api()
371 kslogger = logging.getLogger('kubernetes')
372 kslogger.setLevel(logging.INFO)
373 res = api.list_pod_for_all_namespaces()
374 for i in res.items:
375 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000376 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
377 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
Yang Xu64339a82018-12-30 05:32:21 +0000378 break
379
380 if ret is None:
381 ret = raw_input("Enter sdnc-sdnc-0 pod cluster node public IP address(i.e. 10.12.0.0/16): ")
Yang Xu75b0f5e2018-11-14 14:04:20 -0500382 return ret
383
Yang Xu65b84a42018-12-31 17:48:02 +0000384 def get_vm_public_ip_by_nova(self, vm):
385 """
386 This method uses openstack nova api to retrieve vm public ip
387 :param vm: vm name
388 :return vm public ip
389 """
390 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
391 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
392 for i in nova.servers.list():
393 if i.name == vm:
394 for k, v in i.networks.items():
395 for ip in v:
396 if IPAddress(ip) in subnet:
397 return ip
398 return None
399
Kang Xi11d278c2018-04-06 16:56:04 -0400400 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
401 """
402 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
403 :param net_addr: e.g. 10.12.5.0
404 :param net_addr_len: e.g. 24
405 :return: dictionary {keyword: ip}
406 """
407 if not net_addr:
408 net_addr = self.external_net_addr
409
410 if not net_addr_len:
411 net_addr_len = self.external_net_prefix_len
412
413 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
414 openstackcmd = 'nova ' + param + ' list'
415 self.logger.debug(openstackcmd)
416
Kang Xi11d278c2018-04-06 16:56:04 -0400417 results = os.popen(openstackcmd).read()
Kang Xi6c762392018-05-30 09:27:34 -0400418 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
419 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
420 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
421 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
Kang Xi0e0a1d62018-07-23 16:53:54 -0400422 if self.oom_mode:
423 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
Kang Xi6c762392018-05-30 09:27:34 -0400424
Kang Xi11d278c2018-04-06 16:56:04 -0400425 if len(ip_dict) != len(keywords):
426 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
427 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
Yang Xu64339a82018-12-30 05:32:21 +0000428 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
Kang Xi268b74d2018-05-23 15:53:42 -0400429# sys.exit()
Kang Xi11d278c2018-04-06 16:56:04 -0400430 return ip_dict
431
Kang Xi0e0a1d62018-07-23 16:53:54 -0400432 def get_oom_onap_vm_ip(self, keywords):
433 vm_ip = {}
Yang Xuc52ed6e2019-04-29 00:20:52 -0400434 onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
Kang Xi0e0a1d62018-07-23 16:53:54 -0400435 for vm in keywords:
436 if vm in onap_vm_list:
437 vm_ip[vm] = self.oom_so_sdnc_aai_ip
438 return vm_ip
439
Kang Xi6c762392018-05-30 09:27:34 -0400440 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
441 vm_ip_dict = {}
442 for line in novalist_results.split('\n'):
443 fields = line.split('|')
444 if len(fields) == 8:
445 vm_name = fields[2]
446 ip_info = fields[-2]
447 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
448 vm_ip_dict[vm_name] = ip
449
450 return vm_ip_dict
451
452 def remove_old_vms(self, vm_list, prefix):
453 """
454 For vms with format name_timestamp, only keep the one with the latest timestamp.
455 E.g.,
456 zdcpe1cpe01brgemu01_201805222148 (drop this)
457 zdcpe1cpe01brgemu01_201805222229 (keep this)
458 zdcpe1cpe01gw01_201805162201
459 """
460 new_vm_list = []
461 same_type_vm_dict = {}
462 for vm in vm_list:
463 fields = vm.split('_')
464 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
465 if vm > same_type_vm_dict.get(fields[0], '0'):
466 same_type_vm_dict[fields[0]] = vm
467 else:
468 new_vm_list.append(vm)
469
470 new_vm_list.extend(same_type_vm_dict.values())
471 return new_vm_list
472
473 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
474 vm_ip_dict = {}
475 for keyword in vm_name_keyword_list:
476 for vm, ip in all_vm_ip_dict.items():
477 if keyword in vm:
478 vm_ip_dict[keyword] = ip
479 break
480 return vm_ip_dict
481
Kang Xi11d278c2018-04-06 16:56:04 -0400482 def del_vgmux_ves_mode(self):
483 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
484 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
485 self.logger.debug('%s', r)
486
487 def del_vgmux_ves_collector(self):
488 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
489 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
490 self.logger.debug('%s', r)
491
492 def set_vgmux_ves_collector(self ):
493 url = self.vpp_ves_url.format(self.hosts['mux'])
494 data = {'config':
Kang Xi268b74d2018-05-23 15:53:42 -0400495 {'server-addr': self.hosts[self.dcae_ves_collector_name],
Kang Xi0e0a1d62018-07-23 16:53:54 -0400496 'server-port': '30235' if self.oom_mode else '8081',
Kang Xi11d278c2018-04-06 16:56:04 -0400497 'read-interval': '10',
498 'is-add':'1'
499 }
500 }
501 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
502 self.logger.debug('%s', r)
503
504 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
505 url = self.vpp_ves_url.format(self.hosts['mux'])
506 data = {"mode":
507 {"working-mode": "demo",
508 "base-packet-loss": str(lossrate),
509 "source-name": vg_vnf_instance_name
510 }
511 }
512 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
513 self.logger.debug('%s', r)
514
515 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
516 def get_vxlan_interfaces(self, ip, print_info=False):
517 url = self.vpp_inf_url.format(ip)
518 self.logger.debug('url is this: %s', url)
519 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
520 data = r.json()['interfaces']['interface']
521 if print_info:
522 for inf in data:
523 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
524 print(json.dumps(inf, indent=4, sort_keys=True))
525
526 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
527
528 # delete all VxLAN interfaces of each hosts
529 def delete_vxlan_interfaces(self, host_dic):
530 for host, ip in host_dic.items():
531 deleted = False
532 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
533 inf_list = self.get_vxlan_interfaces(ip)
534 for inf in inf_list:
535 deleted = True
536 time.sleep(2)
537 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
538 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
539 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
540
541 for inf in inf_list:
542 deleted = True
543 time.sleep(2)
544 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
545 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
546 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
547
548 if len(self.get_vxlan_interfaces(ip)) > 0:
549 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
550 return False
551
552 if not deleted:
553 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
554 return True
555
556 @staticmethod
557 def save_object(obj, filepathname):
558 with open(filepathname, 'wb') as fout:
559 pickle.dump(obj, fout)
560
561 @staticmethod
562 def load_object(filepathname):
563 with open(filepathname, 'rb') as fin:
564 return pickle.load(fin)
565
Kang Xi6c762392018-05-30 09:27:34 -0400566 @staticmethod
567 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
568 with open(vnf_template_file) as json_input:
569 json_data = json.load(json_input)
570 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
571 for param in param_list:
572 if param['vnf-parameter-name'] in vnf_parameter_name_list:
573 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
574 number = int(ipaddr_or_vni[-1])
575 if 254 == number:
576 number = 10
577 else:
578 number = number + 1
579 ipaddr_or_vni[-1] = str(number)
580 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
581
582 assert json_data is not None
583 with open(vnf_template_file, 'w') as json_output:
584 json.dump(json_data, json_output, indent=4, sort_keys=True)
585
Kang Xi11d278c2018-04-06 16:56:04 -0400586 def save_preload_data(self, preload_data):
587 self.save_object(preload_data, self.preload_dict_file)
588
589 def load_preload_data(self):
590 return self.load_object(self.preload_dict_file)
591
592 def save_vgmux_vnf_name(self, vgmux_vnf_name):
593 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
594
595 def load_vgmux_vnf_name(self):
596 return self.load_object(self.vgmux_vnf_name_file)
597