3 * ============LICENSE_START=======================================================
4 * Copyright (C) 2019 Orange
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * ============LICENSE_END=========================================================
28 import netifaces as ni
35 from datetime import datetime
36 from datetime import timedelta
37 from simple_rest_client.api import API
38 from simple_rest_client.resource import Resource
39 from basicauth import encode
40 from pprint import pprint
41 from random import randint
42 from urllib3.exceptions import InsecureRequestWarning
45 old_merge_environment_settings = requests.Session.merge_environment_settings
48 ansible_inventory = {}
49 osdf_response = {"last": { "id": "id", "data": None}}
52 class BaseServer(http.server.BaseHTTPRequestHandler):
54 def __init__(self, one, two, three):
55 self.osdf_resp = osdf_response
56 super().__init__(one, two, three)
58 def _set_headers(self):
59 self.send_response(200)
60 self.send_header('Content-type', 'application/json')
71 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
72 self.send_response(200)
75 data = simplejson.loads(self.data_string)
76 #print(json.dumps(data, indent=4))
77 self.osdf_resp["last"]["data"] = data
78 self.osdf_resp["last"]["id"] = data["requestId"]
79 with open("response.json", "w") as outfile:
80 simplejson.dump(data, outfile)
83 def _run_osdf_resp_server():
84 server_address = ('', 9000)
85 httpd = http.server.HTTPServer(server_address, BaseServer)
86 print('Starting OSDF Response Server...')
89 @contextlib.contextmanager
90 def _no_ssl_verification():
91 opened_adapters = set()
93 def merge_environment_settings(self, url, proxies, stream, verify, cert):
94 # Verification happens only once per connection so we need to close
95 # all the opened adapters once we're done. Otherwise, the effects of
96 # verify=False persist beyond the end of this context manager.
97 opened_adapters.add(self.get_adapter(url))
99 settings = old_merge_environment_settings(self, url, proxies, stream, verify, cert)
100 settings['verify'] = False
104 requests.Session.merge_environment_settings = merge_environment_settings
107 with warnings.catch_warnings():
108 warnings.simplefilter('ignore', InsecureRequestWarning)
111 requests.Session.merge_environment_settings = old_merge_environment_settings
113 for adapter in opened_adapters:
120 def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
121 # some strings that we will encounter frequently
122 rel_lst = "relationship-list"
123 rkey = "relationship-key"
124 rval = "relationship-value"
125 rdata = "relationship-data"
128 m_key = match_dict.get('key')
129 m_value = match_dict.get('value')
133 rel_dict = data.get(rel_lst)
134 if rel_dict: # check if data has relationship lists
135 for key, rel_list in rel_dict.items():
137 if rel.get("related-to") == related_to:
140 link = rel.get("related-link")
141 r_data = rel.get(rdata, [])
144 if rd.get(rkey) == search_key:
146 if not match_dict: # return first match
148 {"link": link, "d_value": dval}
150 break # go to next relation
151 if rd.get(rkey) == m_key \
152 and rd.get(rval) == m_value:
154 if match_dict and matched: # if matching required
156 {"link": link, "d_value": dval}
158 # matched, return search value corresponding
159 # to the matched r_data group
160 else: # no search key; just return the link
162 {"link": link, "d_value": dval}
164 if len(response) == 0:
166 {"link": None, "d_value": None}
171 class AAIApiResource(Resource):
173 'generic_vnf': {'method': 'GET', 'url': 'network/generic-vnfs/generic-vnf/{}'},
174 'link': {'method': 'GET', 'url': '{}'},
175 'service_instance': {'method': 'GET',
176 'url': 'business/customers/customer/{}/service-subscriptions/service-subscription/{}/service-instances/service-instance/{}'}
180 class HASApiResource(Resource):
182 'plans': {'method': 'POST', 'url': 'plans/'},
183 'plan': {'method': 'GET', 'url': 'plans/{}'}
187 class OSDFApiResource(Resource):
189 'placement': {'method': 'POST', 'url': 'placement'}
193 class APPCLcmApiResource(Resource):
195 'distribute_traffic': {'method': 'POST', 'url': 'appc-provider-lcm:distribute-traffic/'},
196 'distribute_traffic_check': {'method': 'POST', 'url': 'appc-provider-lcm:distribute-traffic-check/'},
197 'upgrade_software': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-software/'},
198 'upgrade_pre_check': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-pre-check/'},
199 'upgrade_post_check': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-post-check/'},
200 'action_status': {'method': 'POST', 'url': 'appc-provider-lcm:action-status/'},
204 def _init_python_aai_api(onap_ip):
206 api_root_url="https://{}:30233/aai/v14/".format(onap_ip),
209 'Authorization': encode("AAI", "AAI"),
210 'X-FromAppId': 'SCRIPT',
211 'Accept': 'application/json',
212 'Content-Type': 'application/json',
213 'X-TransactionId': str(uuid.uuid4()),
217 json_encode_body=True # encode body as json
219 api.add_resource(resource_name='aai', resource_class=AAIApiResource)
223 def _init_python_has_api(onap_ip):
225 api_root_url="https://{}:30275/v1/".format(onap_ip),
228 'Authorization': encode("admin1", "plan.15"),
229 'X-FromAppId': 'SCRIPT',
230 'Accept': 'application/json',
231 'Content-Type': 'application/json',
232 'X-TransactionId': str(uuid.uuid4()),
236 json_encode_body=True # encode body as json
238 api.add_resource(resource_name='has', resource_class=HASApiResource)
242 def _init_python_osdf_api(onap_ip):
244 api_root_url="https://{}:30248/api/oof/v1/".format(onap_ip),
247 'Authorization': encode("test", "testpwd"),
248 'X-FromAppId': 'SCRIPT',
249 'Accept': 'application/json',
250 'Content-Type': 'application/json',
251 'X-TransactionId': str(uuid.uuid4()),
255 json_encode_body=True # encode body as json
257 api.add_resource(resource_name='osdf', resource_class=OSDFApiResource)
261 def _init_python_appc_lcm_api(onap_ip):
263 api_root_url="https://{}:30230/restconf/operations/".format(onap_ip),
266 'Authorization': encode("admin", "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"),
267 'X-FromAppId': 'SCRIPT',
268 'Accept': 'application/json',
269 'Content-Type': 'application/json',
273 json_encode_body=True # encode body as json
275 api.add_resource(resource_name='lcm', resource_class=APPCLcmApiResource)
279 def load_aai_data(vfw_vnf_id, onap_ip):
280 api = _init_python_aai_api(onap_ip)
282 aai_data['service-info'] = {'global-customer-id': '', 'service-instance-id': '', 'service-type': ''}
283 aai_data['vfw-model-info'] = {'model-invariant-id': '', 'model-version-id': '', 'vnf-name': '', 'vnf-type': ''}
284 aai_data['vpgn-model-info'] = {'model-invariant-id': '', 'model-version-id': '', 'vnf-name': '', 'vnf-type': ''}
285 with _no_ssl_verification():
286 response = api.aai.generic_vnf(vfw_vnf_id, body=None, params={'depth': 2}, headers={})
287 aai_data['vfw-model-info']['model-invariant-id'] = response.body.get('model-invariant-id')
288 aai_data['vfw-model-info']['model-version-id'] = response.body.get('model-version-id')
289 aai_data['vfw-model-info']['vnf-name'] = response.body.get('vnf-name')
290 aai_data['vfw-model-info']['vnf-type'] = response.body.get('vnf-type')
291 aai_data['vf-module-id'] = response.body['vf-modules']['vf-module'][0]['vf-module-id']
293 related_to = "service-instance"
294 search_key = "customer.global-customer-id"
295 rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
296 aai_data['service-info']['global-customer-id'] = rl_data_list[0]['d_value']
298 search_key = "service-subscription.service-type"
299 rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
300 aai_data['service-info']['service-type'] = rl_data_list[0]['d_value']
302 search_key = "service-instance.service-instance-id"
303 rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
304 aai_data['service-info']['service-instance-id'] = rl_data_list[0]['d_value']
306 service_link = rl_data_list[0]['link']
307 response = api.aai.link(service_link, body=None, params={}, headers={})
309 related_to = "generic-vnf"
310 search_key = "generic-vnf.vnf-id"
311 rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
312 for i in range(0, len(rl_data_list)):
313 vnf_id = rl_data_list[i]['d_value']
315 if vnf_id != vfw_vnf_id:
316 vnf_link = rl_data_list[i]['link']
317 response = api.aai.link(vnf_link, body=None, params={}, headers={})
318 if aai_data['vfw-model-info']['model-invariant-id'] != response.body.get('model-invariant-id'):
319 aai_data['vpgn-model-info']['model-invariant-id'] = response.body.get('model-invariant-id')
320 aai_data['vpgn-model-info']['model-version-id'] = response.body.get('model-version-id')
321 aai_data['vpgn-model-info']['vnf-name'] = response.body.get('vnf-name')
322 aai_data['vpgn-model-info']['vnf-type'] = response.body.get('vnf-type')
327 def _osdf_request(rancher_ip, onap_ip, aai_data, exclude, use_oof_cache):
328 dirname = os.path.join('templates/oof-cache/', aai_data['vf-module-id'])
330 file = os.path.join(dirname, 'sample-osdf-excluded.json')
332 file = os.path.join(dirname, 'sample-osdf-required.json')
333 if use_oof_cache and os.path.exists(file):
334 migrate_from = json.loads(open(file).read())
337 print('Making OSDF request for excluded {}'.format(str(exclude)))
338 api = _init_python_osdf_api(onap_ip)
339 request_id = str(uuid.uuid4())
340 transaction_id = str(uuid.uuid4())
341 callback_url = "http://{}:9000/osdfCallback/".format(str(rancher_ip))
342 template = json.loads(open('templates/osdfRequest.json').read())
343 template["requestInfo"]["transactionId"] = transaction_id
344 template["requestInfo"]["requestId"] = request_id
345 template["requestInfo"]["callbackUrl"] = callback_url
346 template["serviceInfo"]["serviceInstanceId"] = aai_data['service-info']['service-instance-id']
347 template["placementInfo"]["requestParameters"]["chosenCustomerId"] = aai_data['service-info']['global-customer-id']
348 template["placementInfo"]["placementDemands"][0]["resourceModelInfo"]["modelInvariantId"] =\
349 aai_data['vfw-model-info']['model-invariant-id']
350 template["placementInfo"]["placementDemands"][0]["resourceModelInfo"]["modelVersionId"] =\
351 aai_data['vfw-model-info']['model-version-id']
352 template["placementInfo"]["placementDemands"][1]["resourceModelInfo"]["modelInvariantId"] =\
353 aai_data['vpgn-model-info']['model-invariant-id']
354 template["placementInfo"]["placementDemands"][1]["resourceModelInfo"]["modelVersionId"] =\
355 aai_data['vpgn-model-info']['model-version-id']
357 template["placementInfo"]["placementDemands"][0]["excludedCandidates"][0]["identifiers"].\
358 append(aai_data['vf-module-id'])
359 del template["placementInfo"]["placementDemands"][0]["requiredCandidates"]
361 template["placementInfo"]["placementDemands"][0]["requiredCandidates"][0]["identifiers"].\
362 append(aai_data['vf-module-id'])
363 del template["placementInfo"]["placementDemands"][0]["excludedCandidates"]
365 #print(json.dumps(template, indent=4))
367 with _no_ssl_verification():
368 response = api.osdf.placement(body=template, params={}, headers={})
369 #if response.body.get('error_message') is not None:
370 # raise Exception(response.body['error_message']['explanation'])
373 while counter < 600 and osdf_response["last"]["id"] != request_id:
375 if counter % 20 == 0:
379 if osdf_response["last"]["id"] == request_id:
380 status = osdf_response["last"]["data"]["requestStatus"]
381 if status == "completed":
383 "solution": osdf_response["last"]["data"]["solutions"]["placementSolutions"]
385 if not os.path.exists(dirname):
388 f.write(json.dumps(result, indent=4))
392 message = osdf_response["last"]["data"]["statusMessage"]
393 raise Exception("OOF request {}: {}".format(status, message))
395 raise Exception("No response for OOF request")
398 def _has_request(onap_ip, aai_data, exclude, use_oof_cache):
399 dirname = os.path.join('templates/oof-cache/', aai_data['vf-module-id'])
401 file = os.path.join(dirname, 'sample-has-excluded.json')
403 file = os.path.join(dirname, 'sample-has-required.json')
404 if use_oof_cache and os.path.exists(file):
405 migrate_from = json.loads(open(file).read())
408 print('Making HAS request for excluded {}'.format(str(exclude)))
409 api = _init_python_has_api(onap_ip)
410 request_id = str(uuid.uuid4())
411 template = json.loads(open('templates/hasRequest.json').read())
413 template['name'] = request_id
414 node = template['template']['parameters']
415 node['chosen_customer_id'] = aai_data['service-info']['global-customer-id']
416 node['service_id'] = aai_data['service-info']['service-instance-id']
417 node = template['template']['demands']['vFW-SINK'][0]
418 node['attributes']['model-invariant-id'] = aai_data['vfw-model-info']['model-invariant-id']
419 node['attributes']['model-version-id'] = aai_data['vfw-model-info']['model-version-id']
421 node['excluded_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
422 del node['required_candidates']
424 node['required_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
425 del node['excluded_candidates']
426 node = template['template']['demands']['vPGN'][0]
427 node['attributes']['model-invariant-id'] = aai_data['vpgn-model-info']['model-invariant-id']
428 node['attributes']['model-version-id'] = aai_data['vpgn-model-info']['model-version-id']
430 #print(json.dumps(template, indent=4))
432 with _no_ssl_verification():
433 response = api.has.plans(body=template, params={}, headers={})
434 if response.body.get('error_message') is not None:
435 raise Exception(response.body['error_message']['explanation'])
437 plan_id = response.body['id']
438 response = api.has.plan(plan_id, body=None, params={}, headers={})
439 status = response.body['plans'][0]['status']
440 while status != 'done' and status != 'error':
442 response = api.has.plan(plan_id, body=None, params={}, headers={})
443 status = response.body['plans'][0]['status']
445 result = response.body['plans'][0]['recommendations'][0]
447 raise Exception(response.body['plans'][0]['message'])
449 if not os.path.exists(dirname):
452 f.write(json.dumps(result, indent=4))
457 def _extract_has_appc_identifiers(has_result, demand):
459 v_server = has_result[demand]['attributes']['vservers'][0]
461 if len(has_result[demand]['attributes']['vservers'][0]['l-interfaces']) == 4:
462 v_server = has_result[demand]['attributes']['vservers'][0]
464 v_server = has_result[demand]['attributes']['vservers'][1]
465 for itf in v_server['l-interfaces']:
466 if itf['ipv4-addresses'][0].startswith("10.0."):
467 ip = itf['ipv4-addresses'][0]
470 if v_server['vserver-name'] in hostname_cache and demand != 'vPGN':
471 v_server['vserver-name'] = v_server['vserver-name'].replace("01", "02")
472 hostname_cache.append(v_server['vserver-name'])
475 'vnf-id': has_result[demand]['attributes']['nf-id'],
476 'vf-module-id': has_result[demand]['attributes']['vf-module-id'],
478 'vserver-id': v_server['vserver-id'],
479 'vserver-name': v_server['vserver-name'],
480 'vnfc-type': demand.lower(),
481 'physical-location-id': has_result[demand]['attributes']['physical-location-id']
483 ansible_inventory_entry = "{} ansible_ssh_host={} ansible_ssh_user=ubuntu".format(config['vserver-name'], config['ip'])
484 if demand.lower() not in ansible_inventory:
485 ansible_inventory[demand.lower()] = {}
486 ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
490 def _extract_osdf_appc_identifiers(has_result, demand):
492 v_server = has_result[demand]['vservers'][0]
494 if len(has_result[demand]['vservers'][0]['l-interfaces']) == 4:
495 v_server = has_result[demand]['vservers'][0]
497 v_server = has_result[demand]['vservers'][1]
498 for itf in v_server['l-interfaces']:
499 if itf['ipv4-addresses'][0].startswith("10.0."):
500 ip = itf['ipv4-addresses'][0]
503 if v_server['vserver-name'] in hostname_cache and demand != 'vPGN':
504 v_server['vserver-name'] = v_server['vserver-name'].replace("01", "02")
505 hostname_cache.append(v_server['vserver-name'])
508 'vnf-id': has_result[demand]['nf-id'],
509 'vf-module-id': has_result[demand]['vf-module-id'],
511 'vserver-id': v_server['vserver-id'],
512 'vserver-name': v_server['vserver-name'],
513 'vnfc-type': demand.lower(),
514 'physical-location-id': has_result[demand]['locationId']
516 ansible_inventory_entry = "{} ansible_ssh_host={} ansible_ssh_user=ubuntu".format(config['vserver-name'], config['ip'])
517 if demand.lower() not in ansible_inventory:
518 ansible_inventory[demand.lower()] = {}
519 ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
523 def _extract_has_appc_dt_config(has_result, demand):
528 "nf-type": has_result[demand]['attributes']['nf-type'],
529 "nf-name": has_result[demand]['attributes']['nf-name'],
530 "vf-module-name": has_result[demand]['attributes']['vf-module-name'],
531 "vnf-type": has_result[demand]['attributes']['vnf-type'],
532 "service_instance_id": "319e60ef-08b1-47aa-ae92-51b97f05e1bc",
533 "cloudClli": has_result[demand]['attributes']['physical-location-id'],
534 "nf-id": has_result[demand]['attributes']['nf-id'],
535 "vf-module-id": has_result[demand]['attributes']['vf-module-id'],
536 "aic_version": has_result[demand]['attributes']['aic_version'],
537 "ipv4-oam-address": has_result[demand]['attributes']['ipv4-oam-address'],
538 "vnfHostName": has_result[demand]['candidate']['host_id'],
539 "ipv6-oam-address": has_result[demand]['attributes']['ipv6-oam-address'],
540 "cloudOwner": has_result[demand]['candidate']['cloud_owner'],
541 "isRehome": has_result[demand]['candidate']['is_rehome'],
542 "locationId": has_result[demand]['candidate']['location_id'],
543 "locationType": has_result[demand]['candidate']['location_type'],
544 'vservers': has_result[demand]['attributes']['vservers']
549 def _extract_osdf_appc_dt_config(osdf_result, demand):
553 return osdf_result[demand]
556 def _build_config_from_has(has_result):
557 v_pgn_result = _extract_has_appc_identifiers(has_result, 'vPGN')
558 v_fw_result = _extract_has_appc_identifiers(has_result, 'vFW-SINK')
559 dt_config = _extract_has_appc_dt_config(has_result, 'vFW-SINK')
562 'vPGN': v_pgn_result,
563 'vFW-SINK': v_fw_result
565 #print(json.dumps(config, indent=4))
566 config['dt-config'] = {
567 'destinations': [dt_config]
572 def _adapt_osdf_result(osdf_result):
574 demand = _build_osdf_result_demand(osdf_result["solution"][0][0])
575 result[demand["name"]] = demand["value"]
576 demand = _build_osdf_result_demand(osdf_result["solution"][0][1])
577 result[demand["name"]] = demand["value"]
581 def _build_osdf_result_demand(solution):
583 result["name"] = solution["resourceModuleName"]
584 value = {"candidateId": solution["solution"]["identifiers"][0]}
585 for info in solution["assignmentInfo"]:
586 value[info["key"]] = info["value"]
587 result["value"] = value
591 def _build_config_from_osdf(osdf_result):
592 osdf_result = _adapt_osdf_result(osdf_result)
593 v_pgn_result = _extract_osdf_appc_identifiers(osdf_result, 'vPGN')
594 v_fw_result = _extract_osdf_appc_identifiers(osdf_result, 'vFW-SINK')
595 dt_config = _extract_osdf_appc_dt_config(osdf_result, 'vFW-SINK')
598 'vPGN': v_pgn_result,
599 'vFW-SINK': v_fw_result
601 #print(json.dumps(config, indent=4))
602 config['dt-config'] = {
603 'destinations': [dt_config]
608 def _build_appc_lcm_dt_payload(demand, oof_config, action, traffic_presence):
609 is_check = traffic_presence is not None
610 oof_config = copy.deepcopy(oof_config)
612 # node_list = "[ {} ]".format(oof_config['vPGN']['vserver-id'])
614 # node_list = "[ {} ]".format(oof_config['vFW-SINK']['vserver-id'])
615 book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
616 config = oof_config[demand]
618 # 'site': config['physical-location-id'],
619 # 'vnfc_type': config['vnfc-type'],
621 # 'ne_id': config['vserver-name'],
622 # 'fixed_ip_address': config['ip']
626 #node_list.append(node)
629 oof_config['dt-config']['trafficpresence'] = traffic_presence
631 file_content = oof_config['dt-config']
634 "configuration-parameters": {
635 #"node_list": node_list,
636 "ne_id": config['vserver-name'],
637 "fixed_ip_address": config['ip'],
638 "file_parameter_content": json.dumps(file_content)
642 config["configuration-parameters"]["book_name"] = book_name
643 payload = json.dumps(config)
647 def _build_appc_lcm_upgrade_payload(demand, oof_config, action, old_version, new_version):
648 oof_config = copy.deepcopy(oof_config)
649 book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
650 config = oof_config[demand]
652 file_content = {} #oof_config['dt-config']
655 "configuration-parameters": {
656 #"node_list": node_list,
657 "ne_id": config['vserver-name'],
658 "fixed_ip_address": config['ip'],
659 "file_parameter_content": json.dumps(file_content),
660 "existing-software-version": old_version,
661 "new-software-version": new_version
665 config["configuration-parameters"]["book_name"] = book_name
666 payload = json.dumps(config)
670 def _build_appc_lcm_status_body(req):
672 'request-id': req['input']['common-header']['request-id'],
673 'sub-request-id': req['input']['common-header']['sub-request-id'],
674 'originator-id': req['input']['common-header']['originator-id']
676 payload = json.dumps(payload)
677 template = json.loads(open('templates/appcRestconfLcm.json').read())
678 template['input']['action'] = 'ActionStatus'
679 template['input']['payload'] = payload
680 template['input']['common-header']['request-id'] = req['input']['common-header']['request-id']
681 template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
682 template['input']['action-identifiers']['vnf-id'] = req['input']['action-identifiers']['vnf-id']
686 def _build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
691 payload = _build_appc_lcm_dt_payload(demand, config, action, traffic_presence)
692 return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
695 def _build_appc_lcm_upgrade_request_body(config, req_id, action, old_version, new_version):
697 payload = _build_appc_lcm_upgrade_payload(demand, config, action, old_version, new_version)
698 return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
701 def _build_appc_lcm_request_body(payload, demand, config, req_id, action):
702 template = json.loads(open('templates/appcRestconfLcm.json').read())
703 template['input']['action'] = action
704 template['input']['payload'] = payload
705 template['input']['common-header']['request-id'] = req_id
706 template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
707 template['input']['action-identifiers']['vnf-id'] = config[demand]['vnf-id']
711 def _set_appc_lcm_timestamp(body, timestamp=None):
712 if timestamp is None:
713 t = datetime.utcnow() + timedelta(seconds=-10)
714 timestamp = t.strftime('%Y-%m-%dT%H:%M:%S.244Z')
715 body['input']['common-header']['timestamp'] = timestamp
718 def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version=None):
722 migrate_from = _has_request(onap_ip, aai_data, False, use_oof_cache)
724 if if_close_loop_vfw:
725 migrate_to = migrate_from
727 migrate_to = _has_request(onap_ip, aai_data, True, use_oof_cache)
729 migrate_from = _build_config_from_has(migrate_from)
730 migrate_to = _build_config_from_has(migrate_to)
732 migrate_from = _osdf_request(rancher_ip, onap_ip, aai_data, False, use_oof_cache)
734 if if_close_loop_vfw:
735 migrate_to = migrate_from
737 migrate_to = _osdf_request(rancher_ip, onap_ip, aai_data, True, use_oof_cache)
739 migrate_from = _build_config_from_osdf(migrate_from)
740 migrate_to = _build_config_from_osdf(migrate_to)
742 #print(json.dumps(migrate_from, indent=4))
743 #print(json.dumps(migrate_to, indent=4))
744 req_id = str(uuid.uuid4())
747 if_dt_only = new_version is None
748 if new_version is not None and new_version != "1.0":
752 #_build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
753 payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
754 payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
755 payload_dt_check_vfw_from = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck',
757 payload_dt_check_vfw_to = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
760 requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
761 requests.append({"payload": payload_dt_check_vfw_from, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
762 requests.append({"payload": payload_dt_check_vfw_to, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
763 result.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
764 "workflow": {"requests": requests, "description": "Migrate Traffic and Verify"}})
766 #_build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
767 payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
768 payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
769 payload_dt_vpkg_from = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTraffic')
771 payload_dt_check_vfw_from_absent = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', False)
772 payload_dt_check_vfw_to_present = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
773 payload_dt_check_vfw_to_absent = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', False)
774 payload_dt_check_vfw_from_present = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', True)
776 payload_old_version_check_vfw_from = _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePreCheck', old_version, new_version)
777 payload_new_version_check_vfw_from = _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePostCheck', old_version, new_version)
778 payload_upgrade_vfw_from = _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradeSoftware', old_version, new_version)
781 migrate_requests = list()
782 migrate_requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
783 migrate_requests.append({"payload": payload_dt_check_vfw_from_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
784 migrate_requests.append({"payload": payload_dt_check_vfw_to_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
786 requests.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
787 "workflow": {"requests": migrate_requests, "description": "Migrate Traffic and Verify"}})
788 requests.append({"payload": payload_upgrade_vfw_from, "breakOnFailure": True, "description": "Upgrading Software on source vFW"})
789 requests.append({"payload": payload_new_version_check_vfw_from, "breakOnFailure": True, "description": "Check current software version on source vFW"})
790 requests.append({"payload": payload_dt_vpkg_from, "breakOnFailure": True, "description": "Migrating destination vFW traffic to source vFW"})
791 requests.append({"payload": payload_dt_check_vfw_to_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the destination vFW"})
792 requests.append({"payload": payload_dt_check_vfw_from_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the source vFW"})
794 result.append({"payload": payload_old_version_check_vfw_from, "breakOnFailure": False, "description": "Check current software version on source vFW",
795 "workflow": {"requests": requests, "description": "Migrate Traffic and Upgrade Software"}})
800 def appc_lcm_request(onap_ip, req):
801 api = _init_python_appc_lcm_api(onap_ip)
802 with _no_ssl_verification():
803 #print(json.dumps(req, indent=4))
804 if req['input']['action'] == "DistributeTraffic":
805 result = api.lcm.distribute_traffic(body=req, params={}, headers={})
806 elif req['input']['action'] == "DistributeTrafficCheck":
807 result = api.lcm.distribute_traffic_check(body=req, params={}, headers={})
808 elif req['input']['action'] == "UpgradeSoftware":
809 result = api.lcm.upgrade_software(body=req, params={}, headers={})
810 elif req['input']['action'] == "UpgradePreCheck":
811 result = api.lcm.upgrade_pre_check(body=req, params={}, headers={})
812 elif req['input']['action'] == "UpgradePostCheck":
813 result = api.lcm.upgrade_post_check(body=req, params={}, headers={})
815 raise Exception("{} action not supported".format(req['input']['action']))
817 if result.body['output']['status']['code'] == 400:
819 elif result.body['output']['status']['code'] == 100:
821 # elif result.body['output']['status']['code'] == 311:
822 # timestamp = result.body['output']['common-header']['timestamp']
823 # _set_appc_lcm_timestamp(req, timestamp)
824 # appc_lcm_request(onap_ip, req)
827 raise Exception("{} - {}".format(result.body['output']['status']['code'],
828 result.body['output']['status']['message']))
830 return result.body['output']['status']['code']
833 def appc_lcm_status_request(onap_ip, req):
834 api = _init_python_appc_lcm_api(onap_ip)
835 status_body = _build_appc_lcm_status_body(req)
836 _set_appc_lcm_timestamp(status_body)
838 with _no_ssl_verification():
839 result = api.lcm.action_status(body=status_body, params={}, headers={})
841 if result.body['output']['status']['code'] == 400:
842 status = json.loads(result.body['output']['payload'])
845 raise Exception("{} - {}".format(result.body['output']['status']['code'],
846 result.body['output']['status']['message']))
849 def confirm_appc_lcm_action(onap_ip, req, check_appc_result):
850 print("APPC LCM << {} >> [Status]".format(req['input']['action']))
854 status = appc_lcm_status_request(onap_ip, req)
855 print(status['status'])
856 if status['status'] == 'SUCCESSFUL':
858 elif status['status'] == 'IN_PROGRESS':
860 elif check_appc_result:
861 print("APPC LCM <<{}>> [{} - {}]".format(req['input']['action'], status['status'], status['status-reason']))
867 def _execute_lcm_requests(workflow, onap_ip, check_result):
868 lcm_requests = workflow["requests"]
869 print("WORKFLOW << {} >>".format(workflow["description"]))
870 for i in range(len(lcm_requests)):
871 req = lcm_requests[i]["payload"]
872 #print(json.dumps(req, indent=4))
873 print("APPC LCM << {} >> [{}]".format(req['input']['action'], lcm_requests[i]["description"]))
874 _set_appc_lcm_timestamp(req)
875 result = appc_lcm_request(onap_ip, req)
877 conf_result = confirm_appc_lcm_action(onap_ip, req, check_result)
879 if lcm_requests[i]["breakOnFailure"]:
880 raise Exception("APPC LCM << {} >> FAILED".format(req['input']['action']))
881 elif "workflow" in lcm_requests[i]:
882 print("WORKFLOW << {} >> SKIP".format(lcm_requests[i]["workflow"]["description"]))
883 elif "workflow" in lcm_requests[i]:
884 _execute_lcm_requests(lcm_requests[i]["workflow"], onap_ip, check_result)
890 def execute_workflow(vfw_vnf_id, rancher_ip, onap_ip, use_oof_cache, if_close_loop_vfw, info_only, check_result, new_version=None):
891 print("\nExecuting workflow for VNF ID '{}' on Rancher with IP {} and ONAP with IP {}".format(
892 vfw_vnf_id, rancher_ip, onap_ip))
893 print("\nOOF Cache {}, is CL vFW {}, only info {}, check LCM result {}".format(use_oof_cache, if_close_loop_vfw,
894 info_only, check_result))
895 if new_version is not None:
896 print("\nNew vFW software version {}\n".format(new_version))
898 x = threading.Thread(target=_run_osdf_resp_server, daemon=True)
900 aai_data = load_aai_data(vfw_vnf_id, onap_ip)
901 print("\nvFWDT Service Information:")
902 print(json.dumps(aai_data, indent=4))
903 lcm_requests = build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version)
904 print("\nAnsible Inventory:")
905 inventory = "[host]\nlocalhost ansible_connection=local\n"
906 for key in ansible_inventory:
907 inventory += str("[{}]\n").format(key)
908 for host in ansible_inventory[key]:
909 inventory += str("{}\n").format(ansible_inventory[key][host])
912 f = open("Ansible_inventory", 'w+')
918 print("\nDistribute Traffic Workflow Execution:")
920 _execute_lcm_requests({"requests": lcm_requests, "description": "Migrate vFW Traffic Conditionally"}, onap_ip, check_result)
923 help = """\npython3 workflow.py <VNF-ID> <RANCHER-NODE-IP> <K8S-NODE-IP> <IF-CACHE> <IF-VFWCL> <INITIAL-ONLY> <CHECK-STATUS> <VERSION>
924 \n<VNF-ID> - vnf-id of vFW VNF instance that traffic should be migrated out from
925 <RANCHER-NODE-IP> - External IP of ONAP Rancher Node i.e. 10.12.5.160 (If Rancher Node is missing this is NFS node)
926 <K8S-NODE-IP> - External IP of ONAP K8s Worker Node i.e. 10.12.5.212
927 <IF-CACHE> - If script should use and build OOF response cache (cache it speed-ups further executions of script)
928 <IF-VFWCL> - If instead of vFWDT service instance vFW or vFWCL one is used (should be False always)
929 <INITIAL-ONLY> - If only configuration information will be collected (True for initial phase and False for full execution of workflow)
930 <CHECK-STATUS> - If APPC LCM action status should be verified and FAILURE should stop workflow (when False FAILED status of LCM action does not stop execution of further LCM actions)
931 <VERSION> - New version of vFW - for tests '1.0' or '2.0'. Ommit when traffic distribution only\n"""
934 if key == "-h" or key == "--help":
939 if len(sys.argv) > 8:
940 new_version = sys.argv[8]
942 #vnf_id, Rancher node IP, K8s node IP, use OOF cache, if close loop vfw, if info_only, if check APPC result
943 execute_workflow(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4].lower() == 'true', sys.argv[5].lower() == 'true',
944 sys.argv[6].lower() == 'true', sys.argv[7].lower() == 'true', new_version)