blob: 77e36227196be1ed13283160cf04e05a2322f4f7 [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):
Yang Xu65b84a42018-12-31 17:48:02 +000069 rootlogger = logging.getLogger()
70 handler = logging.StreamHandler()
71 formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s.%(funcName)s(): %(message)s')
72 handler.setFormatter(formatter)
73 rootlogger.addHandler(handler)
74 rootlogger.setLevel(logging.INFO)
75
Kang Xi11d278c2018-04-06 16:56:04 -040076 self.logger = logging.getLogger(__name__)
Yang Xu65b84a42018-12-31 17:48:02 +000077 self.logger.propagate = False
78 self.logger.addHandler(handler)
79 self.logger.setLevel(logging.DEBUG)
Kang Xi11d278c2018-04-06 16:56:04 -040080 self.logger.info('Initializing configuration')
81
Yang Xu27e16242018-12-28 01:00:50 -050082 # CHANGEME: vgw_VfModuleModelInvariantUuid is in rescust service csar, look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automcatically
83 self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
84 # 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 +000085 self.sdnc_oam_ip = self.get_pod_node_oam_ip('sdnc-sdnc-0')
Yang Xu27e16242018-12-28 01:00:50 -050086 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu64339a82018-12-30 05:32:21 +000087 self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip('sdnc-sdnc-0')
Yang Xu27e16242018-12-28 01:00:50 -050088 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +000089 self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
Yang Xu27e16242018-12-28 01:00:50 -050090 # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
Yang Xu65b84a42018-12-31 17:48:02 +000091 self.mr_ip_addr = self.oom_so_sdnc_aai_ip
Brian Freeman8076a872018-11-13 11:34:48 -050092 self.mr_ip_port = '30227'
Brian Freemana605bc72018-11-12 10:50:30 -050093 self.so_nbi_port = '30277' if self.oom_mode else '8080'
Kang Xi0e0a1d62018-07-23 16:53:54 -040094 self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
95 self.aai_query_port = '30233' if self.oom_mode else '8443'
96 self.sniro_port = '30288' if self.oom_mode else '8080'
97
Yang Xuc52ed6e2019-04-29 00:20:52 -040098 self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
Kang Xi11d278c2018-04-06 16:56:04 -040099 if extra_host_names:
100 self.host_names.extend(extra_host_names)
101 # get IP addresses
102 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
103 # this is the keyword used to name vgw stack, must not be used in other stacks
104 self.vgw_name_keyword = 'base_vcpe_vgw'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500105 # this is the file that will keep the index of last assigned SO name
106 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
Kang Xi11d278c2018-04-06 16:56:04 -0400107 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
108 self.preload_dict_file = '__var/preload_dict'
109 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
110 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
111 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
112 self.instance_name_prefix = {
113 'service': 'vcpe_svc',
114 'network': 'vcpe_net',
115 'vnf': 'vcpe_vnf',
116 'vfmodule': 'vcpe_vfmodule'
117 }
118 self.aai_userpass = 'AAI', 'AAI'
119 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
120 self.os_tenant_id = self.cloud['--os-tenant-id']
121 self.os_region_name = self.cloud['--os-region-name']
122 self.common_preload_config['pub_key'] = self.pub_key
Kang Xi0e0a1d62018-07-23 16:53:54 -0400123 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
Kang Xi11d278c2018-04-06 16:56:04 -0400124 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Kang Xi268b74d2018-05-23 15:53:42 -0400125 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
126# self.homing_solution = 'oof'
127 self.customer_location_used_by_oof = {
128 "customerLatitude": "32.897480",
129 "customerLongitude": "-97.040443",
130 "customerName": "some_company"
131 }
Kang Xi11d278c2018-04-06 16:56:04 -0400132
133 #############################################################################################
Yang Xuc52ed6e2019-04-29 00:20:52 -0400134 # SDC urls
135 self.sdc_get_request_userpass = 'vid','Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
136 self.sdc_get_request_headers = {'X-ECOMP-InstanceID': 'VID'}
137 self.sdc_port = '30205'
138 self.sdc_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_port
139 self.sdc_service_list_url = self.sdc_url_prefix + '/sdc/v1/catalog/services'
140
141 #############################################################################################
Kang Xi11d278c2018-04-06 16:56:04 -0400142 # SDNC urls
143 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
144 self.sdnc_db_name = 'sdnctl'
145 self.sdnc_db_user = 'sdnctl'
146 self.sdnc_db_pass = 'gamma'
Kang Xi268b74d2018-05-23 15:53:42 -0400147 self.sdnc_db_port = '32774'
Kang Xi11d278c2018-04-06 16:56:04 -0400148 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
149 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400150 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
Kang Xi11d278c2018-04-06 16:56:04 -0400151 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400152 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500153 self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
154 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400155 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
156 '/restconf/config/GENERIC-RESOURCE-API:'
Kang Xi11d278c2018-04-06 16:56:04 -0400157
158 #############################################################################################
159 # SO urls, note: do NOT add a '/' at the end of the url
Brian Freemana605bc72018-11-12 10:50:30 -0500160 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 -0500161 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
Brian Freemana605bc72018-11-12 10:50:30 -0500162 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 -0400163 self.so_userpass = 'InfraPortalClient', 'password1$'
164 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Brian Freemana605bc72018-11-12 10:50:30 -0500165 self.so_db_name = 'catalogdb'
Kang Xi11d278c2018-04-06 16:56:04 -0400166 self.so_db_user = 'root'
167 self.so_db_pass = 'password'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400168 self.so_db_port = '30252' if self.oom_mode else '32769'
Kang Xi11d278c2018-04-06 16:56:04 -0400169
170 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
171 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
172 self.vpp_api_userpass = ('admin', 'admin')
173 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
174
175 def headbridge(self, openstack_stack_name, svc_instance_uuid):
176 """
177 Add vserver information to AAI
178 """
179 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
Kang Xi0e0a1d62018-07-23 16:53:54 -0400180 if not self.oom_mode:
181 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
182 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
183 self.logger.debug('%s', ret)
184 else:
185 print('To add vGMUX vserver info to AAI, do the following:')
186 print('- ssh to rancher')
187 print('- sudo su -')
188 print('- cd /root/oom/kubernetes/robot')
189 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
Kang Xi11d278c2018-04-06 16:56:04 -0400190
191 def get_brg_mac_from_sdnc(self):
192 """
Kang Xi6c762392018-05-30 09:27:34 -0400193 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
194 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 -0400195 """
196 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
197 host=self.hosts['sdnc'], port=self.sdnc_db_port)
198 cursor = cnx.cursor()
199 query = "SELECT * from DHCP_MAP"
200 cursor.execute(query)
201
202 self.logger.debug('DHCP_MAP table in SDNC')
Kang Xi6c762392018-05-30 09:27:34 -0400203 mac_recent = None
204 host = -1
Kang Xi11d278c2018-04-06 16:56:04 -0400205 for mac, ip in cursor:
Kang Xi11d278c2018-04-06 16:56:04 -0400206 self.logger.debug(mac + ':' + ip)
Kang Xi6c762392018-05-30 09:27:34 -0400207 this_host = int(ip.split('.')[-1])
208 if host < this_host:
209 host = this_host
210 mac_recent = mac
Kang Xi11d278c2018-04-06 16:56:04 -0400211
212 cnx.close()
213
Kang Xi6c762392018-05-30 09:27:34 -0400214 assert mac_recent
215 return mac_recent
Kang Xi11d278c2018-04-06 16:56:04 -0400216
Kang Xi6c762392018-05-30 09:27:34 -0400217 def execute_cmds_sdnc_db(self, cmds):
218 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
219 self.hosts['sdnc'], self.sdnc_db_port)
Kang Xi11d278c2018-04-06 16:56:04 -0400220
Kang Xi6c762392018-05-30 09:27:34 -0400221 def execute_cmds_so_db(self, cmds):
222 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
223 self.hosts['so'], self.so_db_port)
224
225 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
226 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
Kang Xi11d278c2018-04-06 16:56:04 -0400227 cursor = cnx.cursor()
228 for cmd in cmds:
229 self.logger.debug(cmd)
230 cursor.execute(cmd)
231 self.logger.debug('%s', cursor)
232 cnx.commit()
233 cursor.close()
234 cnx.close()
235
236 def find_file(self, file_name_keyword, file_ext, search_dir):
237 """
238 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
239 :param file_ext: e.g., csar, json
240 :param search_dir path to search
241 :return: path name of the file
242 """
243 file_name_keyword = file_name_keyword.lower()
244 file_ext = file_ext.lower()
245 if not file_ext.startswith('.'):
246 file_ext = '.' + file_ext
247
248 filenamepath = None
249 for file_name in os.listdir(search_dir):
250 file_name_lower = file_name.lower()
251 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
252 if filenamepath:
253 self.logger.error('Multiple files found for *{0}*.{1} in '
254 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
255 sys.exit()
256 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
257
258 if filenamepath:
259 return filenamepath
260 else:
261 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
262 sys.exit()
263
264 @staticmethod
265 def network_name_to_subnet_name(network_name):
266 """
267 :param network_name: example: vcpe_net_cpe_signal_201711281221
268 :return: vcpe_net_cpe_signal_subnet_201711281221
269 """
270 fields = network_name.split('_')
271 fields.insert(-1, 'subnet')
272 return '_'.join(fields)
273
274 def set_network_name(self, network_name):
275 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
276 openstackcmd = 'openstack ' + param
277 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
278 os.popen(cmd)
279
280 def set_subnet_name(self, network_name):
281 """
282 Example: network_name = vcpe_net_cpe_signal_201711281221
283 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
284 :return:
285 """
286 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
287 openstackcmd = 'openstack ' + param
288
289 # expected results: | subnets | subnet_id |
290 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
291 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
292 subnet_id = subnet_info[2].strip()
293 subnet_name = self.network_name_to_subnet_name(network_name)
294 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
295 os.popen(cmd)
296 self.logger.info("Subnet name set to: " + subnet_name)
297 return True
298 else:
299 self.logger.error("Can't get subnet info from network name: " + network_name)
300 return False
301
302 def is_node_in_aai(self, node_type, node_uuid):
303 key = None
304 search_node_type = None
305 if node_type == 'service':
306 search_node_type = 'service-instance'
307 key = 'service-instance-id'
308 elif node_type == 'vnf':
309 search_node_type = 'generic-vnf'
310 key = 'vnf-id'
311 else:
312 logging.error('Invalid node_type: ' + node_type)
313 sys.exit()
314
Kang Xi0e0a1d62018-07-23 16:53:54 -0400315 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
316 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
Kang Xi11d278c2018-04-06 16:56:04 -0400317
Kang Xi268b74d2018-05-23 15:53:42 -0400318 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
Kang Xi11d278c2018-04-06 16:56:04 -0400319 requests.packages.urllib3.disable_warnings()
320 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
321 response = r.json()
322 self.logger.debug('aai query: ' + url)
323 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
324 return 'result-data' in response
325
326 @staticmethod
327 def extract_ip_from_str(net_addr, net_addr_len, sz):
328 """
329 :param net_addr: e.g. 10.5.12.0
330 :param net_addr_len: e.g. 24
331 :param sz: a string
332 :return: the first IP address matching the network, e.g. 10.5.12.3
333 """
334 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
335 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
336 for ip in ip_list:
337 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
338 if this_net == network:
339 return str(ip)
340 return None
341
Yang Xu75b0f5e2018-11-14 14:04:20 -0500342 def get_pod_node_oam_ip(self, pod):
343 """
Yang Xu64339a82018-12-30 05:32:21 +0000344 :Assuming kubectl is available and configured by default config (~/.kube/config)
345 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
346 :return pod's cluster node oam ip (10.0.0.0/16)
Yang Xu75b0f5e2018-11-14 14:04:20 -0500347 """
Yang Xu64339a82018-12-30 05:32:21 +0000348 ret = None
349 config.load_kube_config()
350 api = client.CoreV1Api()
351 kslogger = logging.getLogger('kubernetes')
352 kslogger.setLevel(logging.INFO)
353 res = api.list_pod_for_all_namespaces()
354 for i in res.items:
355 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000356 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 +0000357 ret = i.status.host_ip
358 break
359
360 if ret is None:
361 ret = raw_input("Enter sdnc-sdnc-0 pod cluster node OAM IP address(10.0.0.0/16): ")
362 return ret
363
364 def get_pod_node_public_ip(self, pod):
365 """
366 :Assuming kubectl is available and configured by default config (~/.kube/config)
367 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
368 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
369 """
370 ret = None
371 config.load_kube_config()
372 api = client.CoreV1Api()
373 kslogger = logging.getLogger('kubernetes')
374 kslogger.setLevel(logging.INFO)
375 res = api.list_pod_for_all_namespaces()
376 for i in res.items:
377 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000378 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
379 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
Yang Xu64339a82018-12-30 05:32:21 +0000380 break
381
382 if ret is None:
383 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 -0500384 return ret
385
Yang Xu65b84a42018-12-31 17:48:02 +0000386 def get_vm_public_ip_by_nova(self, vm):
387 """
388 This method uses openstack nova api to retrieve vm public ip
389 :param vm: vm name
390 :return vm public ip
391 """
392 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
393 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
394 for i in nova.servers.list():
395 if i.name == vm:
396 for k, v in i.networks.items():
397 for ip in v:
398 if IPAddress(ip) in subnet:
399 return ip
400 return None
401
Kang Xi11d278c2018-04-06 16:56:04 -0400402 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
403 """
404 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
405 :param net_addr: e.g. 10.12.5.0
406 :param net_addr_len: e.g. 24
407 :return: dictionary {keyword: ip}
408 """
409 if not net_addr:
410 net_addr = self.external_net_addr
411
412 if not net_addr_len:
413 net_addr_len = self.external_net_prefix_len
414
415 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
416 openstackcmd = 'nova ' + param + ' list'
417 self.logger.debug(openstackcmd)
418
Kang Xi11d278c2018-04-06 16:56:04 -0400419 results = os.popen(openstackcmd).read()
Kang Xi6c762392018-05-30 09:27:34 -0400420 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
421 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
422 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
423 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
Kang Xi0e0a1d62018-07-23 16:53:54 -0400424 if self.oom_mode:
425 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
Kang Xi6c762392018-05-30 09:27:34 -0400426
Kang Xi11d278c2018-04-06 16:56:04 -0400427 if len(ip_dict) != len(keywords):
428 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
429 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
Yang Xu64339a82018-12-30 05:32:21 +0000430 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
Kang Xi268b74d2018-05-23 15:53:42 -0400431# sys.exit()
Kang Xi11d278c2018-04-06 16:56:04 -0400432 return ip_dict
433
Kang Xi0e0a1d62018-07-23 16:53:54 -0400434 def get_oom_onap_vm_ip(self, keywords):
435 vm_ip = {}
Yang Xuc52ed6e2019-04-29 00:20:52 -0400436 onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
Kang Xi0e0a1d62018-07-23 16:53:54 -0400437 for vm in keywords:
438 if vm in onap_vm_list:
439 vm_ip[vm] = self.oom_so_sdnc_aai_ip
440 return vm_ip
441
Kang Xi6c762392018-05-30 09:27:34 -0400442 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
443 vm_ip_dict = {}
444 for line in novalist_results.split('\n'):
445 fields = line.split('|')
446 if len(fields) == 8:
447 vm_name = fields[2]
448 ip_info = fields[-2]
449 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
450 vm_ip_dict[vm_name] = ip
451
452 return vm_ip_dict
453
454 def remove_old_vms(self, vm_list, prefix):
455 """
456 For vms with format name_timestamp, only keep the one with the latest timestamp.
457 E.g.,
458 zdcpe1cpe01brgemu01_201805222148 (drop this)
459 zdcpe1cpe01brgemu01_201805222229 (keep this)
460 zdcpe1cpe01gw01_201805162201
461 """
462 new_vm_list = []
463 same_type_vm_dict = {}
464 for vm in vm_list:
465 fields = vm.split('_')
466 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
467 if vm > same_type_vm_dict.get(fields[0], '0'):
468 same_type_vm_dict[fields[0]] = vm
469 else:
470 new_vm_list.append(vm)
471
472 new_vm_list.extend(same_type_vm_dict.values())
473 return new_vm_list
474
475 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
476 vm_ip_dict = {}
477 for keyword in vm_name_keyword_list:
478 for vm, ip in all_vm_ip_dict.items():
479 if keyword in vm:
480 vm_ip_dict[keyword] = ip
481 break
482 return vm_ip_dict
483
Kang Xi11d278c2018-04-06 16:56:04 -0400484 def del_vgmux_ves_mode(self):
485 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
486 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
487 self.logger.debug('%s', r)
488
489 def del_vgmux_ves_collector(self):
490 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
491 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
492 self.logger.debug('%s', r)
493
494 def set_vgmux_ves_collector(self ):
495 url = self.vpp_ves_url.format(self.hosts['mux'])
496 data = {'config':
Kang Xi268b74d2018-05-23 15:53:42 -0400497 {'server-addr': self.hosts[self.dcae_ves_collector_name],
Kang Xi0e0a1d62018-07-23 16:53:54 -0400498 'server-port': '30235' if self.oom_mode else '8081',
Kang Xi11d278c2018-04-06 16:56:04 -0400499 'read-interval': '10',
500 'is-add':'1'
501 }
502 }
503 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
504 self.logger.debug('%s', r)
505
506 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
507 url = self.vpp_ves_url.format(self.hosts['mux'])
508 data = {"mode":
509 {"working-mode": "demo",
510 "base-packet-loss": str(lossrate),
511 "source-name": vg_vnf_instance_name
512 }
513 }
514 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
515 self.logger.debug('%s', r)
516
517 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
518 def get_vxlan_interfaces(self, ip, print_info=False):
519 url = self.vpp_inf_url.format(ip)
520 self.logger.debug('url is this: %s', url)
521 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
522 data = r.json()['interfaces']['interface']
523 if print_info:
524 for inf in data:
525 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
526 print(json.dumps(inf, indent=4, sort_keys=True))
527
528 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
529
530 # delete all VxLAN interfaces of each hosts
531 def delete_vxlan_interfaces(self, host_dic):
532 for host, ip in host_dic.items():
533 deleted = False
534 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
535 inf_list = self.get_vxlan_interfaces(ip)
536 for inf in inf_list:
537 deleted = True
538 time.sleep(2)
539 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
540 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
541 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
542
543 for inf in inf_list:
544 deleted = True
545 time.sleep(2)
546 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
547 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
548 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
549
550 if len(self.get_vxlan_interfaces(ip)) > 0:
551 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
552 return False
553
554 if not deleted:
555 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
556 return True
557
558 @staticmethod
559 def save_object(obj, filepathname):
560 with open(filepathname, 'wb') as fout:
561 pickle.dump(obj, fout)
562
563 @staticmethod
564 def load_object(filepathname):
565 with open(filepathname, 'rb') as fin:
566 return pickle.load(fin)
567
Kang Xi6c762392018-05-30 09:27:34 -0400568 @staticmethod
569 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
570 with open(vnf_template_file) as json_input:
571 json_data = json.load(json_input)
572 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
573 for param in param_list:
574 if param['vnf-parameter-name'] in vnf_parameter_name_list:
575 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
576 number = int(ipaddr_or_vni[-1])
577 if 254 == number:
578 number = 10
579 else:
580 number = number + 1
581 ipaddr_or_vni[-1] = str(number)
582 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
583
584 assert json_data is not None
585 with open(vnf_template_file, 'w') as json_output:
586 json.dump(json_data, json_output, indent=4, sort_keys=True)
587
Kang Xi11d278c2018-04-06 16:56:04 -0400588 def save_preload_data(self, preload_data):
589 self.save_object(preload_data, self.preload_dict_file)
590
591 def load_preload_data(self):
592 return self.load_object(self.preload_dict_file)
593
594 def save_vgmux_vnf_name(self, vgmux_vnf_name):
595 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
596
597 def load_vgmux_vnf_name(self):
598 return self.load_object(self.vgmux_vnf_name_file)
599