blob: 980092c82eb7c21faab3ef82dd0debad01de7ed7 [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
Kang Xi268b74d2018-05-23 15:53:42 -040098 self.host_names = ['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 #############################################################################################
134 # SDNC urls
135 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
136 self.sdnc_db_name = 'sdnctl'
137 self.sdnc_db_user = 'sdnctl'
138 self.sdnc_db_pass = 'gamma'
Kang Xi268b74d2018-05-23 15:53:42 -0400139 self.sdnc_db_port = '32774'
Kang Xi11d278c2018-04-06 16:56:04 -0400140 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
141 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400142 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
Kang Xi11d278c2018-04-06 16:56:04 -0400143 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
Kang Xi0e0a1d62018-07-23 16:53:54 -0400144 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
Brian Freeman81f6e9e2018-11-11 22:36:20 -0500145 self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
146 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400147 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
148 '/restconf/config/GENERIC-RESOURCE-API:'
Kang Xi11d278c2018-04-06 16:56:04 -0400149
150 #############################################################################################
151 # SO urls, note: do NOT add a '/' at the end of the url
Brian Freemana605bc72018-11-12 10:50:30 -0500152 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 -0500153 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
Brian Freemana605bc72018-11-12 10:50:30 -0500154 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 -0400155 self.so_userpass = 'InfraPortalClient', 'password1$'
156 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
Brian Freemana605bc72018-11-12 10:50:30 -0500157 self.so_db_name = 'catalogdb'
Kang Xi11d278c2018-04-06 16:56:04 -0400158 self.so_db_user = 'root'
159 self.so_db_pass = 'password'
Kang Xi0e0a1d62018-07-23 16:53:54 -0400160 self.so_db_port = '30252' if self.oom_mode else '32769'
Kang Xi11d278c2018-04-06 16:56:04 -0400161
162 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
163 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
164 self.vpp_api_userpass = ('admin', 'admin')
165 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
166
167 def headbridge(self, openstack_stack_name, svc_instance_uuid):
168 """
169 Add vserver information to AAI
170 """
171 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
Kang Xi0e0a1d62018-07-23 16:53:54 -0400172 if not self.oom_mode:
173 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
174 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
175 self.logger.debug('%s', ret)
176 else:
177 print('To add vGMUX vserver info to AAI, do the following:')
178 print('- ssh to rancher')
179 print('- sudo su -')
180 print('- cd /root/oom/kubernetes/robot')
181 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
Kang Xi11d278c2018-04-06 16:56:04 -0400182
183 def get_brg_mac_from_sdnc(self):
184 """
Kang Xi6c762392018-05-30 09:27:34 -0400185 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
186 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 -0400187 """
188 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
189 host=self.hosts['sdnc'], port=self.sdnc_db_port)
190 cursor = cnx.cursor()
191 query = "SELECT * from DHCP_MAP"
192 cursor.execute(query)
193
194 self.logger.debug('DHCP_MAP table in SDNC')
Kang Xi6c762392018-05-30 09:27:34 -0400195 mac_recent = None
196 host = -1
Kang Xi11d278c2018-04-06 16:56:04 -0400197 for mac, ip in cursor:
Kang Xi11d278c2018-04-06 16:56:04 -0400198 self.logger.debug(mac + ':' + ip)
Kang Xi6c762392018-05-30 09:27:34 -0400199 this_host = int(ip.split('.')[-1])
200 if host < this_host:
201 host = this_host
202 mac_recent = mac
Kang Xi11d278c2018-04-06 16:56:04 -0400203
204 cnx.close()
205
Kang Xi6c762392018-05-30 09:27:34 -0400206 assert mac_recent
207 return mac_recent
Kang Xi11d278c2018-04-06 16:56:04 -0400208
Kang Xi6c762392018-05-30 09:27:34 -0400209 def execute_cmds_sdnc_db(self, cmds):
210 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
211 self.hosts['sdnc'], self.sdnc_db_port)
Kang Xi11d278c2018-04-06 16:56:04 -0400212
Kang Xi6c762392018-05-30 09:27:34 -0400213 def execute_cmds_so_db(self, cmds):
214 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
215 self.hosts['so'], self.so_db_port)
216
217 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
218 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
Kang Xi11d278c2018-04-06 16:56:04 -0400219 cursor = cnx.cursor()
220 for cmd in cmds:
221 self.logger.debug(cmd)
222 cursor.execute(cmd)
223 self.logger.debug('%s', cursor)
224 cnx.commit()
225 cursor.close()
226 cnx.close()
227
228 def find_file(self, file_name_keyword, file_ext, search_dir):
229 """
230 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
231 :param file_ext: e.g., csar, json
232 :param search_dir path to search
233 :return: path name of the file
234 """
235 file_name_keyword = file_name_keyword.lower()
236 file_ext = file_ext.lower()
237 if not file_ext.startswith('.'):
238 file_ext = '.' + file_ext
239
240 filenamepath = None
241 for file_name in os.listdir(search_dir):
242 file_name_lower = file_name.lower()
243 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
244 if filenamepath:
245 self.logger.error('Multiple files found for *{0}*.{1} in '
246 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
247 sys.exit()
248 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
249
250 if filenamepath:
251 return filenamepath
252 else:
253 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
254 sys.exit()
255
256 @staticmethod
257 def network_name_to_subnet_name(network_name):
258 """
259 :param network_name: example: vcpe_net_cpe_signal_201711281221
260 :return: vcpe_net_cpe_signal_subnet_201711281221
261 """
262 fields = network_name.split('_')
263 fields.insert(-1, 'subnet')
264 return '_'.join(fields)
265
266 def set_network_name(self, network_name):
267 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
268 openstackcmd = 'openstack ' + param
269 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
270 os.popen(cmd)
271
272 def set_subnet_name(self, network_name):
273 """
274 Example: network_name = vcpe_net_cpe_signal_201711281221
275 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
276 :return:
277 """
278 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
279 openstackcmd = 'openstack ' + param
280
281 # expected results: | subnets | subnet_id |
282 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
283 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
284 subnet_id = subnet_info[2].strip()
285 subnet_name = self.network_name_to_subnet_name(network_name)
286 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
287 os.popen(cmd)
288 self.logger.info("Subnet name set to: " + subnet_name)
289 return True
290 else:
291 self.logger.error("Can't get subnet info from network name: " + network_name)
292 return False
293
294 def is_node_in_aai(self, node_type, node_uuid):
295 key = None
296 search_node_type = None
297 if node_type == 'service':
298 search_node_type = 'service-instance'
299 key = 'service-instance-id'
300 elif node_type == 'vnf':
301 search_node_type = 'generic-vnf'
302 key = 'vnf-id'
303 else:
304 logging.error('Invalid node_type: ' + node_type)
305 sys.exit()
306
Kang Xi0e0a1d62018-07-23 16:53:54 -0400307 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
308 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
Kang Xi11d278c2018-04-06 16:56:04 -0400309
Kang Xi268b74d2018-05-23 15:53:42 -0400310 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
Kang Xi11d278c2018-04-06 16:56:04 -0400311 requests.packages.urllib3.disable_warnings()
312 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
313 response = r.json()
314 self.logger.debug('aai query: ' + url)
315 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
316 return 'result-data' in response
317
318 @staticmethod
319 def extract_ip_from_str(net_addr, net_addr_len, sz):
320 """
321 :param net_addr: e.g. 10.5.12.0
322 :param net_addr_len: e.g. 24
323 :param sz: a string
324 :return: the first IP address matching the network, e.g. 10.5.12.3
325 """
326 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
327 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
328 for ip in ip_list:
329 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
330 if this_net == network:
331 return str(ip)
332 return None
333
Yang Xu75b0f5e2018-11-14 14:04:20 -0500334 def get_pod_node_oam_ip(self, pod):
335 """
Yang Xu64339a82018-12-30 05:32:21 +0000336 :Assuming kubectl is available and configured by default config (~/.kube/config)
337 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
338 :return pod's cluster node oam ip (10.0.0.0/16)
Yang Xu75b0f5e2018-11-14 14:04:20 -0500339 """
Yang Xu64339a82018-12-30 05:32:21 +0000340 ret = None
341 config.load_kube_config()
342 api = client.CoreV1Api()
343 kslogger = logging.getLogger('kubernetes')
344 kslogger.setLevel(logging.INFO)
345 res = api.list_pod_for_all_namespaces()
346 for i in res.items:
347 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000348 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 +0000349 ret = i.status.host_ip
350 break
351
352 if ret is None:
353 ret = raw_input("Enter sdnc-sdnc-0 pod cluster node OAM IP address(10.0.0.0/16): ")
354 return ret
355
356 def get_pod_node_public_ip(self, pod):
357 """
358 :Assuming kubectl is available and configured by default config (~/.kube/config)
359 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
360 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
361 """
362 ret = None
363 config.load_kube_config()
364 api = client.CoreV1Api()
365 kslogger = logging.getLogger('kubernetes')
366 kslogger.setLevel(logging.INFO)
367 res = api.list_pod_for_all_namespaces()
368 for i in res.items:
369 if pod in i.metadata.name:
Yang Xu65b84a42018-12-31 17:48:02 +0000370 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
371 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
Yang Xu64339a82018-12-30 05:32:21 +0000372 break
373
374 if ret is None:
375 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 -0500376 return ret
377
Yang Xu65b84a42018-12-31 17:48:02 +0000378 def get_vm_public_ip_by_nova(self, vm):
379 """
380 This method uses openstack nova api to retrieve vm public ip
381 :param vm: vm name
382 :return vm public ip
383 """
384 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
385 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
386 for i in nova.servers.list():
387 if i.name == vm:
388 for k, v in i.networks.items():
389 for ip in v:
390 if IPAddress(ip) in subnet:
391 return ip
392 return None
393
Kang Xi11d278c2018-04-06 16:56:04 -0400394 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
395 """
396 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
397 :param net_addr: e.g. 10.12.5.0
398 :param net_addr_len: e.g. 24
399 :return: dictionary {keyword: ip}
400 """
401 if not net_addr:
402 net_addr = self.external_net_addr
403
404 if not net_addr_len:
405 net_addr_len = self.external_net_prefix_len
406
407 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
408 openstackcmd = 'nova ' + param + ' list'
409 self.logger.debug(openstackcmd)
410
Kang Xi11d278c2018-04-06 16:56:04 -0400411 results = os.popen(openstackcmd).read()
Kang Xi6c762392018-05-30 09:27:34 -0400412 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
413 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
414 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
415 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
Kang Xi0e0a1d62018-07-23 16:53:54 -0400416 if self.oom_mode:
417 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
Kang Xi6c762392018-05-30 09:27:34 -0400418
Kang Xi11d278c2018-04-06 16:56:04 -0400419 if len(ip_dict) != len(keywords):
420 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
421 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
Yang Xu64339a82018-12-30 05:32:21 +0000422 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
Kang Xi268b74d2018-05-23 15:53:42 -0400423# sys.exit()
Kang Xi11d278c2018-04-06 16:56:04 -0400424 return ip_dict
425
Kang Xi0e0a1d62018-07-23 16:53:54 -0400426 def get_oom_onap_vm_ip(self, keywords):
427 vm_ip = {}
428 onap_vm_list = set(['so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
429 for vm in keywords:
430 if vm in onap_vm_list:
431 vm_ip[vm] = self.oom_so_sdnc_aai_ip
432 return vm_ip
433
Kang Xi6c762392018-05-30 09:27:34 -0400434 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
435 vm_ip_dict = {}
436 for line in novalist_results.split('\n'):
437 fields = line.split('|')
438 if len(fields) == 8:
439 vm_name = fields[2]
440 ip_info = fields[-2]
441 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
442 vm_ip_dict[vm_name] = ip
443
444 return vm_ip_dict
445
446 def remove_old_vms(self, vm_list, prefix):
447 """
448 For vms with format name_timestamp, only keep the one with the latest timestamp.
449 E.g.,
450 zdcpe1cpe01brgemu01_201805222148 (drop this)
451 zdcpe1cpe01brgemu01_201805222229 (keep this)
452 zdcpe1cpe01gw01_201805162201
453 """
454 new_vm_list = []
455 same_type_vm_dict = {}
456 for vm in vm_list:
457 fields = vm.split('_')
458 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
459 if vm > same_type_vm_dict.get(fields[0], '0'):
460 same_type_vm_dict[fields[0]] = vm
461 else:
462 new_vm_list.append(vm)
463
464 new_vm_list.extend(same_type_vm_dict.values())
465 return new_vm_list
466
467 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
468 vm_ip_dict = {}
469 for keyword in vm_name_keyword_list:
470 for vm, ip in all_vm_ip_dict.items():
471 if keyword in vm:
472 vm_ip_dict[keyword] = ip
473 break
474 return vm_ip_dict
475
Kang Xi11d278c2018-04-06 16:56:04 -0400476 def del_vgmux_ves_mode(self):
477 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
478 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
479 self.logger.debug('%s', r)
480
481 def del_vgmux_ves_collector(self):
482 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
483 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
484 self.logger.debug('%s', r)
485
486 def set_vgmux_ves_collector(self ):
487 url = self.vpp_ves_url.format(self.hosts['mux'])
488 data = {'config':
Kang Xi268b74d2018-05-23 15:53:42 -0400489 {'server-addr': self.hosts[self.dcae_ves_collector_name],
Kang Xi0e0a1d62018-07-23 16:53:54 -0400490 'server-port': '30235' if self.oom_mode else '8081',
Kang Xi11d278c2018-04-06 16:56:04 -0400491 'read-interval': '10',
492 'is-add':'1'
493 }
494 }
495 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
496 self.logger.debug('%s', r)
497
498 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
499 url = self.vpp_ves_url.format(self.hosts['mux'])
500 data = {"mode":
501 {"working-mode": "demo",
502 "base-packet-loss": str(lossrate),
503 "source-name": vg_vnf_instance_name
504 }
505 }
506 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
507 self.logger.debug('%s', r)
508
509 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
510 def get_vxlan_interfaces(self, ip, print_info=False):
511 url = self.vpp_inf_url.format(ip)
512 self.logger.debug('url is this: %s', url)
513 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
514 data = r.json()['interfaces']['interface']
515 if print_info:
516 for inf in data:
517 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
518 print(json.dumps(inf, indent=4, sort_keys=True))
519
520 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
521
522 # delete all VxLAN interfaces of each hosts
523 def delete_vxlan_interfaces(self, host_dic):
524 for host, ip in host_dic.items():
525 deleted = False
526 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
527 inf_list = self.get_vxlan_interfaces(ip)
528 for inf in inf_list:
529 deleted = True
530 time.sleep(2)
531 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
532 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
533 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
534
535 for inf in inf_list:
536 deleted = True
537 time.sleep(2)
538 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
539 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
540 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
541
542 if len(self.get_vxlan_interfaces(ip)) > 0:
543 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
544 return False
545
546 if not deleted:
547 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
548 return True
549
550 @staticmethod
551 def save_object(obj, filepathname):
552 with open(filepathname, 'wb') as fout:
553 pickle.dump(obj, fout)
554
555 @staticmethod
556 def load_object(filepathname):
557 with open(filepathname, 'rb') as fin:
558 return pickle.load(fin)
559
Kang Xi6c762392018-05-30 09:27:34 -0400560 @staticmethod
561 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
562 with open(vnf_template_file) as json_input:
563 json_data = json.load(json_input)
564 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
565 for param in param_list:
566 if param['vnf-parameter-name'] in vnf_parameter_name_list:
567 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
568 number = int(ipaddr_or_vni[-1])
569 if 254 == number:
570 number = 10
571 else:
572 number = number + 1
573 ipaddr_or_vni[-1] = str(number)
574 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
575
576 assert json_data is not None
577 with open(vnf_template_file, 'w') as json_output:
578 json.dump(json_data, json_output, indent=4, sort_keys=True)
579
Kang Xi11d278c2018-04-06 16:56:04 -0400580 def save_preload_data(self, preload_data):
581 self.save_object(preload_data, self.preload_dict_file)
582
583 def load_preload_data(self):
584 return self.load_object(self.preload_dict_file)
585
586 def save_vgmux_vnf_name(self, vgmux_vnf_name):
587 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
588
589 def load_vgmux_vnf_name(self):
590 return self.load_object(self.vgmux_vnf_name_file)
591