Bartek Grzybowski | fbbdbec | 2019-09-25 16:37:05 +0200 | [diff] [blame] | 1 | #!/usr/bin/env python |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 2 | |
| 3 | import requests |
| 4 | import json |
| 5 | import sys |
| 6 | from datetime import datetime |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 7 | import csar_parser |
| 8 | import logging |
| 9 | import base64 |
| 10 | |
| 11 | |
| 12 | class Preload: |
| 13 | def __init__(self, vcpecommon): |
| 14 | self.logger = logging.getLogger(__name__) |
Yang Xu | 1b31a1d | 2019-06-26 01:50:15 -0400 | [diff] [blame] | 15 | self.logger.setLevel(logging.DEBUG) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 16 | self.vcpecommon = vcpecommon |
| 17 | |
| 18 | def replace(self, sz, replace_dict): |
| 19 | for old_string, new_string in replace_dict.items(): |
| 20 | sz = sz.replace(old_string, new_string) |
| 21 | if self.vcpecommon.template_variable_symbol in sz: |
| 22 | self.logger.error('Error! Cannot find a value to replace ' + sz) |
| 23 | return sz |
| 24 | |
| 25 | def generate_json(self, template_file, replace_dict): |
| 26 | with open(template_file) as json_input: |
| 27 | json_data = json.load(json_input) |
| 28 | stk = [json_data] |
Bartek Grzybowski | 23f22e8 | 2020-03-05 13:21:59 +0100 | [diff] [blame] | 29 | while stk: |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 30 | data = stk.pop() |
| 31 | for k, v in data.items(): |
| 32 | if type(v) is dict: |
| 33 | stk.append(v) |
| 34 | elif type(v) is list: |
| 35 | stk.extend(v) |
Bartek Grzybowski | 4be94a6 | 2020-03-05 11:41:08 +0100 | [diff] [blame] | 36 | elif type(v) is str or type(v) is unicode: # pylint: disable=E0602 |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 37 | if self.vcpecommon.template_variable_symbol in v: |
| 38 | data[k] = self.replace(v, replace_dict) |
| 39 | else: |
| 40 | self.logger.warning('Unexpected line in template: %s. Look for value %s', template_file, v) |
| 41 | return json_data |
| 42 | |
| 43 | def reset_sniro(self): |
| 44 | self.logger.debug('Clearing SNIRO data') |
| 45 | r = requests.post(self.vcpecommon.sniro_url + '/reset', headers=self.vcpecommon.sniro_headers) |
| 46 | if 2 != r.status_code / 100: |
| 47 | self.logger.debug(r.content) |
| 48 | self.logger.error('Clearing SNIRO date failed.') |
Bartek Grzybowski | 68d93c2 | 2019-10-30 10:55:55 +0100 | [diff] [blame] | 49 | sys.exit(1) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 50 | |
| 51 | def preload_sniro(self, template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, vbrg_ar_name, |
| 52 | vgmux_svc_instance_uuid, vbrg_svc_instance_uuid): |
| 53 | self.reset_sniro() |
| 54 | self.logger.info('Preloading SNIRO for homing service') |
| 55 | replace_dict = {'${tunnelxconn_ar_name}': tunnelxconn_ar_name, |
| 56 | '${vgw_name}': vgw_name, |
| 57 | '${brg_ar_name}': vbrg_ar_name, |
| 58 | '${vgmux_svc_instance_uuid}': vgmux_svc_instance_uuid, |
| 59 | '${vbrg_svc_instance_uuid}': vbrg_svc_instance_uuid |
| 60 | } |
| 61 | sniro_data = self.generate_json(template_sniro_data, replace_dict) |
| 62 | self.logger.debug('SNIRO data:') |
| 63 | self.logger.debug(json.dumps(sniro_data, indent=4, sort_keys=True)) |
| 64 | |
| 65 | base64_sniro_data = base64.b64encode(json.dumps(sniro_data)) |
| 66 | self.logger.debug('SNIRO data: 64') |
| 67 | self.logger.debug(base64_sniro_data) |
| 68 | replace_dict = {'${base64_sniro_data}': base64_sniro_data, '${sniro_ip}': self.vcpecommon.hosts['robot']} |
| 69 | sniro_request = self.generate_json(template_sniro_request, replace_dict) |
| 70 | self.logger.debug('SNIRO request:') |
| 71 | self.logger.debug(json.dumps(sniro_request, indent=4, sort_keys=True)) |
| 72 | |
| 73 | r = requests.post(self.vcpecommon.sniro_url, headers=self.vcpecommon.sniro_headers, json=sniro_request) |
| 74 | if 2 != r.status_code / 100: |
| 75 | response = r.json() |
| 76 | self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) |
| 77 | self.logger.error('SNIRO preloading failed.') |
Bartek Grzybowski | 68d93c2 | 2019-10-30 10:55:55 +0100 | [diff] [blame] | 78 | sys.exit(1) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 79 | |
| 80 | return True |
| 81 | |
| 82 | def preload_network(self, template_file, network_role, subnet_start_ip, subnet_gateway, common_dict, name_suffix): |
| 83 | """ |
| 84 | :param template_file: |
| 85 | :param network_role: cpe_signal, cpe_public, brg_bng, bng_mux, mux_gw |
| 86 | :param subnet_start_ip: |
| 87 | :param subnet_gateway: |
| 88 | :param name_suffix: e.g. '201711201311' |
| 89 | :return: |
| 90 | """ |
| 91 | network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], network_role.lower(), name_suffix]) |
| 92 | subnet_name = self.vcpecommon.network_name_to_subnet_name(network_name) |
| 93 | common_dict['${' + network_role+'_net}'] = network_name |
| 94 | common_dict['${' + network_role+'_subnet}'] = subnet_name |
| 95 | replace_dict = {'${network_role}': network_role, |
| 96 | '${service_type}': 'vCPE', |
| 97 | '${network_type}': 'Generic NeutronNet', |
| 98 | '${network_name}': network_name, |
| 99 | '${subnet_start_ip}': subnet_start_ip, |
| 100 | '${subnet_gateway}': subnet_gateway |
| 101 | } |
| 102 | self.logger.info('Preloading network ' + network_role) |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 103 | self.logger.info('template_file:' + template_file) |
| 104 | if 'networkgra' in template_file: |
Bartek Grzybowski | 3d3d3c2 | 2020-03-05 10:28:03 +0100 | [diff] [blame] | 105 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_network_gra_url) |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 106 | else: |
Bartek Grzybowski | 3d3d3c2 | 2020-03-05 10:28:03 +0100 | [diff] [blame] | 107 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_network_url) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 108 | |
| 109 | def preload(self, template_file, replace_dict, url): |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 110 | self.logger.debug('tempalte_file:'+ template_file) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 111 | json_data = self.generate_json(template_file, replace_dict) |
| 112 | self.logger.debug(json.dumps(json_data, indent=4, sort_keys=True)) |
Brian Freeman | 383a3e8 | 2019-10-03 15:25:39 -0500 | [diff] [blame] | 113 | r = requests.post(url, headers=self.vcpecommon.sdnc_headers, auth=self.vcpecommon.sdnc_userpass, json=json_data, verify=False) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 114 | response = r.json() |
| 115 | if int(response.get('output', {}).get('response-code', 0)) != 200: |
| 116 | self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) |
| 117 | self.logger.error('Preloading failed.') |
| 118 | return False |
| 119 | return True |
| 120 | |
| 121 | def preload_vgw(self, template_file, brg_mac, commont_dict, name_suffix): |
| 122 | replace_dict = {'${brg_mac}': brg_mac, |
| 123 | '${suffix}': name_suffix |
| 124 | } |
| 125 | replace_dict.update(commont_dict) |
| 126 | self.logger.info('Preloading vGW') |
| 127 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) |
| 128 | |
Brian Freeman | 81f6e9e | 2018-11-11 22:36:20 -0500 | [diff] [blame] | 129 | def preload_vgw_gra(self, template_file, brg_mac, commont_dict, name_suffix, vgw_vfmod_name_index): |
| 130 | replace_dict = {'${brg_mac}': brg_mac, |
Brian Freeman | a605bc7 | 2018-11-12 10:50:30 -0500 | [diff] [blame] | 131 | '${suffix}': name_suffix, |
Brian Freeman | 81f6e9e | 2018-11-11 22:36:20 -0500 | [diff] [blame] | 132 | '${vgw_vfmod_name_index}': vgw_vfmod_name_index |
| 133 | } |
| 134 | replace_dict.update(commont_dict) |
| 135 | self.logger.info('Preloading vGW-GRA') |
Yang Xu | 63a0afd | 2018-11-20 16:01:01 -0500 | [diff] [blame] | 136 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_gra_url) |
Brian Freeman | 81f6e9e | 2018-11-11 22:36:20 -0500 | [diff] [blame] | 137 | |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 138 | def preload_vfmodule(self, template_file, service_instance_id, vnf_model, vfmodule_model, common_dict, name_suffix , gra_api_flag): |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 139 | """ |
| 140 | :param template_file: |
| 141 | :param service_instance_id: |
| 142 | :param vnf_model: parsing results from csar_parser |
| 143 | :param vfmodule_model: parsing results from csar_parser |
| 144 | :param common_dict: |
| 145 | :param name_suffix: |
| 146 | :return: |
| 147 | """ |
| 148 | |
| 149 | # examples: |
| 150 | # vfmodule_model['modelCustomizationName']: "Vspinfra111601..base_vcpe_infra..module-0", |
| 151 | # vnf_model['modelCustomizationName']: "vspinfra111601 0", |
| 152 | |
| 153 | vfmodule_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'], |
| 154 | vfmodule_model['modelCustomizationName'].split('..')[0].lower(), name_suffix]) |
| 155 | |
| 156 | # vnf_type and generic_vnf_type are identical |
| 157 | replace_dict = {'${vnf_type}': vfmodule_model['modelCustomizationName'], |
| 158 | '${generic_vnf_type}': vfmodule_model['modelCustomizationName'], |
| 159 | '${service_type}': service_instance_id, |
| 160 | '${generic_vnf_name}': vnf_model['modelCustomizationName'], |
| 161 | '${vnf_name}': vfmodule_name, |
Brian Freeman | b205665 | 2018-11-19 15:36:37 -0500 | [diff] [blame] | 162 | '${mr_ip_addr}': self.vcpecommon.mr_ip_addr, |
| 163 | '${mr_ip_port}': self.vcpecommon.mr_ip_port, |
Yang Xu | 63a0afd | 2018-11-20 16:01:01 -0500 | [diff] [blame] | 164 | '${sdnc_oam_ip}': self.vcpecommon.sdnc_oam_ip, |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 165 | '${suffix}': name_suffix} |
| 166 | replace_dict.update(common_dict) |
| 167 | self.logger.info('Preloading VF Module ' + vfmodule_name) |
Bartek Grzybowski | 3d3d3c2 | 2020-03-05 10:28:03 +0100 | [diff] [blame] | 168 | if gra_api_flag: |
| 169 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_gra_url) |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 170 | else: |
Bartek Grzybowski | 3d3d3c2 | 2020-03-05 10:28:03 +0100 | [diff] [blame] | 171 | return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 172 | |
| 173 | def preload_all_networks(self, template_file, name_suffix): |
| 174 | common_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} |
| 175 | for network, v in self.vcpecommon.preload_network_config.items(): |
| 176 | subnet_start_ip, subnet_gateway_ip = v |
| 177 | if not self.preload_network(template_file, network, subnet_start_ip, subnet_gateway_ip, |
| 178 | common_dict, name_suffix): |
| 179 | return None |
| 180 | return common_dict |
| 181 | |
Bartek Grzybowski | 6358aa3 | 2019-10-30 13:46:43 +0100 | [diff] [blame] | 182 | def aai_region_query(self, req_method, json=None, verify=False): |
| 183 | """ |
| 184 | Perform actual AAI API request for region |
| 185 | :param req_method: request method ({'get','put'}) |
| 186 | :param json: Json payload |
| 187 | :param verify: SSL verify mode |
| 188 | :return: |
| 189 | """ |
| 190 | url, headers, auth = (self.vcpecommon.aai_region_query_url, |
| 191 | self.vcpecommon.aai_headers, |
| 192 | self.vcpecommon.aai_userpass) |
| 193 | try: |
| 194 | if req_method == 'get': |
| 195 | request = requests.get(url, headers=headers, auth=auth, |
| 196 | verify=verify) |
| 197 | elif req_method == 'put': |
| 198 | request = requests.put(url, headers=headers, auth=auth, |
| 199 | verify=verify, json=json) |
| 200 | else: |
| 201 | raise requests.exceptions.RequestException |
| 202 | except requests.exceptions.RequestException as e: |
| 203 | self.logger.error("Error connecting to AAI API. Error details: " + str(e.message)) |
| 204 | return False |
| 205 | try: |
| 206 | assert request.status_code == 200 |
| 207 | except AssertionError: |
| 208 | self.logger.error('AAI request failed. API returned http code ' + str(request.status_code)) |
| 209 | return False |
| 210 | try: |
| 211 | return request.json() |
| 212 | except ValueError as e: |
| 213 | if req_method == 'get': |
| 214 | self.logger.error('Unable to parse AAI response: ' + e.message) |
| 215 | return False |
| 216 | elif req_method == 'put': |
| 217 | return request.ok |
| 218 | else: |
| 219 | return False |
| 220 | |
| 221 | def preload_aai_data(self, template_aai_region_data): |
| 222 | """ |
| 223 | Update aai region data with identity-url |
| 224 | :param template_aai_region_data: path to region data template |
| 225 | :return: |
| 226 | """ |
| 227 | request = self.aai_region_query('get') |
| 228 | if request: |
| 229 | # Check if identity-url already updated (for idempotency) |
| 230 | self.logger.debug("Regiond data acquired from AAI:\n" + json.dumps(request,indent=4)) |
| 231 | try: |
| 232 | assert request['identity-url'] |
| 233 | except KeyError: |
| 234 | pass |
| 235 | else: |
| 236 | self.logger.info('Identity-url already present in {0} data, not updating'.format(self.vcpecommon.cloud['--os-region-name'])) |
| 237 | return |
| 238 | |
| 239 | # Get resource_version and relationship_list from region data |
| 240 | resource_version = request['resource-version'] |
| 241 | relationship_list = request['relationship-list'] |
| 242 | |
| 243 | replace_dict = {'${identity-url}': self.vcpecommon.cloud['--os-auth-url'], |
| 244 | '${identity_api_version}': self.vcpecommon.cloud['--os-identity-api-version'], |
| 245 | '${region_name}': self.vcpecommon.cloud['--os-region-name'], |
| 246 | '${resource_version}': resource_version |
| 247 | } |
| 248 | json_data = self.generate_json(template_aai_region_data, replace_dict) |
| 249 | json_data['relationship-list'] = relationship_list |
| 250 | self.logger.debug('Region update payload:\n' + json.dumps(json_data,indent=4)) |
| 251 | else: |
| 252 | sys.exit(1) |
| 253 | |
| 254 | # Update region data |
| 255 | request = self.aai_region_query('put', json_data) |
| 256 | if request: |
| 257 | self.logger.info('Successully updated identity-url in {0} ' |
| 258 | 'region'.format(self.vcpecommon.cloud['--os-region-name'])) |
| 259 | else: |
| 260 | sys.exit(1) |
| 261 | |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 262 | def test(self): |
| 263 | # this is for testing purpose |
| 264 | name_suffix = datetime.now().strftime('%Y%m%d%H%M') |
Bartek Grzybowski | 9afc5c7 | 2019-12-13 10:27:38 +0100 | [diff] [blame] | 265 | preloader = Preload(self.vcpecommon) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 266 | |
| 267 | network_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} |
| 268 | template_file = 'preload_templates/template.network.json' |
| 269 | for k, v in self.vcpecommon.preload_network_config.items(): |
| 270 | if not preloader.preload_network(template_file, k, v[0], v[1], network_dict, name_suffix): |
| 271 | break |
| 272 | |
| 273 | print('---------------------------------------------------------------') |
| 274 | print('Network related replacement dictionary:') |
| 275 | print(json.dumps(network_dict, indent=4, sort_keys=True)) |
| 276 | print('---------------------------------------------------------------') |
| 277 | |
| 278 | keys = ['infra', 'bng', 'gmux', 'brg'] |
| 279 | for key in keys: |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 280 | key_vnf= key + "_" |
| 281 | key_gra = key + "gra_" |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 282 | csar_file = self.vcpecommon.find_file(key, 'csar', 'csar') |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 283 | template_file = self.vcpecommon.find_file(key_vnf, 'json', 'preload_templates') |
| 284 | template_file_gra = self.vcpecommon.find_file(key_gra, 'json', 'preload_templates') |
| 285 | if csar_file and template_file and template_file_gra: |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 286 | parser = csar_parser.CsarParser() |
| 287 | parser.parse_csar(csar_file) |
| 288 | service_instance_id = 'test112233' |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 289 | # preload both VNF-API and GRA-API |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 290 | preloader.preload_vfmodule(template_file, service_instance_id, parser.vnf_models[0], |
Brian Freeman | 9b3d6ca | 2019-11-06 13:22:53 -0500 | [diff] [blame] | 291 | parser.vfmodule_models[0], network_dict, name_suffix, False) |
| 292 | preloader.preload_vfmodule(template_file_gra, service_instance_id, parser.vnf_models[0], |
| 293 | parser.vfmodule_models[0], network_dict, name_suffix, True) |
| 294 | |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 295 | |
| 296 | def test_sniro(self): |
| 297 | template_sniro_data = self.vcpecommon.find_file('sniro_data', 'json', 'preload_templates') |
| 298 | template_sniro_request = self.vcpecommon.find_file('sniro_request', 'json', 'preload_templates') |
| 299 | |
| 300 | vcperescust_csar = self.vcpecommon.find_file('rescust', 'csar', 'csar') |
| 301 | parser = csar_parser.CsarParser() |
| 302 | parser.parse_csar(vcperescust_csar) |
| 303 | tunnelxconn_ar_name = None |
| 304 | brg_ar_name = None |
| 305 | vgw_name = None |
| 306 | for model in parser.vnf_models: |
| 307 | if 'tunnel' in model['modelCustomizationName']: |
| 308 | tunnelxconn_ar_name = model['modelCustomizationName'] |
| 309 | elif 'brg' in model['modelCustomizationName']: |
| 310 | brg_ar_name = model['modelCustomizationName'] |
| 311 | elif 'vgw' in model['modelCustomizationName']: |
| 312 | vgw_name = model['modelCustomizationName'] |
| 313 | |
| 314 | if not (tunnelxconn_ar_name and brg_ar_name and vgw_name): |
| 315 | self.logger.error('Cannot find all names from %s.', vcperescust_csar) |
Bartek Grzybowski | 68d93c2 | 2019-10-30 10:55:55 +0100 | [diff] [blame] | 316 | sys.exit(1) |
Kang Xi | 11d278c | 2018-04-06 16:56:04 -0400 | [diff] [blame] | 317 | |
| 318 | vgmux_svc_instance_uuid = '88888888888888' |
| 319 | vbrg_svc_instance_uuid = '999999999999999' |
| 320 | |
| 321 | self.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name, |
| 322 | vgmux_svc_instance_uuid, vbrg_svc_instance_uuid) |