Fix for missing VNFC rel information
[demo.git] / tutorials / vFWDT / workflow / workflow.py
1 '''
2 /*-
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
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
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.
17 *
18 * ============LICENSE_END=========================================================
19 */
20 '''
21
22 import os
23 import json
24 import sys
25 import uuid
26 import time
27 import copy
28 import warnings
29 import contextlib
30 import requests
31 import simplejson
32 import http.server
33 import threading
34 from datetime import datetime
35 from datetime import timedelta
36 from simple_rest_client.api import API
37 from simple_rest_client.resource import Resource
38 from basicauth import encode
39 from urllib3.exceptions import InsecureRequestWarning
40
41
42 old_merge_environment_settings = requests.Session.merge_environment_settings
43
44
45 hostname_cache = []
46 ansible_inventory = {}
47 osdf_response = {"last": { "id": "id", "data": None}}
48 print_performance=False
49 stats = open("stats.csv", "w")
50 stats.write("operation;time\n")
51
52
53 class BaseServer(http.server.BaseHTTPRequestHandler):
54
55     def __init__(self, one, two, three):
56         self.osdf_resp = osdf_response
57         super().__init__(one, two, three)
58
59     def _set_headers(self):
60         self.send_response(200)
61         self.send_header('Content-type', 'application/json')
62         self.end_headers()
63
64     def do_GET(self):
65         self._set_headers()
66
67     def do_HEAD(self):
68         self._set_headers()
69
70     def do_POST(self):
71         self._set_headers()
72         self.data_string = self.rfile.read(int(self.headers['Content-Length']))
73         self.send_response(200)
74         self.end_headers()
75
76         data = simplejson.loads(self.data_string)
77         #print(json.dumps(data, indent=4))
78         self.osdf_resp["last"]["data"] = data
79         self.osdf_resp["last"]["id"] = data["requestId"]
80         with open("response.json", "w") as outfile:
81             simplejson.dump(data, outfile)
82
83
84 class timing(object):
85
86     def __init__(self, description):
87         self.description = description
88
89     def __call__(self, f):
90         def wrap(*args, **kwargs):
91             req = None
92             if f.__name__ == "appc_lcm_request" or f.__name__ == "confirm_appc_lcm_action":
93                 req = args[1]
94             description = self.description
95             if req is not None:
96                 description = self.description + ' ' + req['input']['action']
97             if description.find('>') < 0 and print_performance:
98                 print (('> {} START').format(description))
99             try:
100                 time1 = time.time()
101                 ret = f(*args, **kwargs)
102             finally:
103                 time2 = time.time()
104                 if print_performance:
105                     print ('> {} DONE {:0.3f} ms'.format(description, (time2-time1)*1000.0))
106                 stats.write("{};{:0.3f}\n".format(description, (time2-time1)*1000.0).replace(".", ","))
107             return ret
108         return wrap
109
110
111 def _run_osdf_resp_server():
112     server_address = ('', 9000)
113     httpd = http.server.HTTPServer(server_address, BaseServer)
114     print('Starting OSDF Response Server...')
115     httpd.serve_forever()
116
117
118 @contextlib.contextmanager
119 def _no_ssl_verification():
120     opened_adapters = set()
121     def merge_environment_settings(self, url, proxies, stream, verify, cert):
122         # Verification happens only once per connection so we need to close
123         # all the opened adapters once we're done. Otherwise, the effects of
124         # verify=False persist beyond the end of this context manager.
125         opened_adapters.add(self.get_adapter(url))
126
127         settings = old_merge_environment_settings(self, url, proxies, stream, verify, cert)
128         settings['verify'] = False
129
130         return settings
131
132     requests.Session.merge_environment_settings = merge_environment_settings
133     try:
134         with warnings.catch_warnings():
135             warnings.simplefilter('ignore', InsecureRequestWarning)
136             yield
137     finally:
138         requests.Session.merge_environment_settings = old_merge_environment_settings
139
140         for adapter in opened_adapters:
141             try:
142                 adapter.close()
143             except:
144                 pass
145
146
147 def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
148     # some strings that we will encounter frequently
149     rel_lst = "relationship-list"
150     rkey = "relationship-key"
151     rval = "relationship-value"
152     rdata = "relationship-data"
153     response = list()
154     if match_dict:
155         m_key = match_dict.get('key')
156         m_value = match_dict.get('value')
157     else:
158         m_key = None
159         m_value = None
160     rel_dict = data.get(rel_lst)
161     if rel_dict:  # check if data has relationship lists
162         for key, rel_list in rel_dict.items(): # pylint: disable=W0612
163             for rel in rel_list:
164                 if rel.get("related-to") == related_to:
165                     dval = None
166                     matched = False
167                     link = rel.get("related-link")
168                     property = ""
169                     if rel.get("related-to-property") is not None:
170                         property = rel.get("related-to-property")[0]['property-value']
171                     r_data = rel.get(rdata, [])
172                     if search_key:
173                         for rd in r_data:
174                             if rd.get(rkey) == search_key:
175                                 dval = rd.get(rval)
176                                 if not match_dict:  # return first match
177                                     response.append(
178                                         {"link": link, "property": property, "d_value": dval}
179                                     )
180                                     break  # go to next relation
181                             if rd.get(rkey) == m_key \
182                                     and rd.get(rval) == m_value:
183                                 matched = True
184                         if match_dict and matched:  # if matching required
185                             response.append(
186                                 {"link": link, "property": property, "d_value": dval}
187                             )
188                             # matched, return search value corresponding
189                             # to the matched r_data group
190                     else:  # no search key; just return the link
191                         response.append(
192                             {"link": link, "property": property, "d_value": dval}
193                         )
194     if response:
195         response.append(
196             {"link": None, "property": None, "d_value": None}
197         )
198     return response
199
200
201 class AAIApiResource(Resource):
202     actions = {
203         'generic_vnf': {'method': 'GET', 'url': 'network/generic-vnfs/generic-vnf/{}'},
204         'vf_module': {'method': 'GET', 'url': 'network/generic-vnfs/generic-vnf/{}/vf-modules/vf-module/{}'},
205         'vnfc': {'method': 'GET', 'url': 'network/vnfcs/vnfc/{}'},
206         'vnfc_put': {'method': 'PUT', 'url': 'network/vnfcs/vnfc/{}'},
207         'vnfc_patch': {'method': 'PATCH', 'url': 'network/vnfcs/vnfc/{}'},
208         'link': {'method': 'GET', 'url': '{}'},
209         'service_instance': {'method': 'GET',
210                              'url': 'business/customers/customer/{}/service-subscriptions/service-subscription/{}/service-instances/service-instance/{}'}
211     }
212
213
214 class HASApiResource(Resource):
215     actions = {
216         'plans': {'method': 'POST', 'url': 'plans/'},
217         'plan': {'method': 'GET', 'url': 'plans/{}'}
218     }
219
220
221 class OSDFApiResource(Resource):
222     actions = {
223         'placement': {'method': 'POST', 'url': 'placement'}
224     }
225
226
227 class APPCLcmApiResource(Resource):
228     actions = {
229         'distribute_traffic': {'method': 'POST', 'url': 'appc-provider-lcm:distribute-traffic/'},
230         'distribute_traffic_check': {'method': 'POST', 'url': 'appc-provider-lcm:distribute-traffic-check/'},
231         'upgrade_software': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-software/'},
232         'upgrade_pre_check': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-pre-check/'},
233         'upgrade_post_check': {'method': 'POST', 'url': 'appc-provider-lcm:upgrade-post-check/'},
234         'action_status': {'method': 'POST', 'url': 'appc-provider-lcm:action-status/'},
235         'check_lock': {'method': 'POST', 'url': 'appc-provider-lcm:check-lock/'},
236         'lock': {'method': 'POST', 'url': 'appc-provider-lcm:lock/'},
237         'unlock': {'method': 'POST', 'url': 'appc-provider-lcm:unlock/'}
238     }
239
240
241 def _init_python_aai_api(onap_ip, content_type='application/json'):
242     api = API(
243         api_root_url="https://{}:30233/aai/v14/".format(onap_ip),
244         params={},
245         headers={
246             'Authorization': encode("AAI", "AAI"),
247             'X-FromAppId': 'SCRIPT',
248             'Accept': 'application/json',
249             'Content-Type': content_type,
250             'X-TransactionId': str(uuid.uuid4()),
251         },
252         timeout=30,
253         append_slash=False,
254         json_encode_body=True # encode body as json
255     )
256     api.add_resource(resource_name='aai', resource_class=AAIApiResource)
257     return api
258
259
260 def _init_python_has_api(onap_ip):
261     api = API(
262         api_root_url="https://{}:30275/v1/".format(onap_ip),
263         params={},
264         headers={
265             'Authorization': encode("admin1", "plan.15"),
266             'X-FromAppId': 'SCRIPT',
267             'Accept': 'application/json',
268             'Content-Type': 'application/json',
269             'X-TransactionId': str(uuid.uuid4()),
270         },
271         timeout=30,
272         append_slash=False,
273         json_encode_body=True # encode body as json
274     )
275     api.add_resource(resource_name='has', resource_class=HASApiResource)
276     return api
277
278
279 def _init_python_osdf_api(onap_ip):
280     api = API(
281         api_root_url="https://{}:30248/api/oof/v1/".format(onap_ip),
282         params={},
283         headers={
284             'Authorization': encode("test", "testpwd"),
285             'X-FromAppId': 'SCRIPT',
286             'Accept': 'application/json',
287             'Content-Type': 'application/json',
288             'X-TransactionId': str(uuid.uuid4()),
289         },
290         timeout=30,
291         append_slash=False,
292         json_encode_body=True # encode body as json
293     )
294     api.add_resource(resource_name='osdf', resource_class=OSDFApiResource)
295     return api
296
297
298 def _init_python_appc_lcm_api(onap_ip):
299     api = API(
300         api_root_url="https://{}:30230/restconf/operations/".format(onap_ip),
301         params={},
302         headers={
303             'Authorization': encode("admin", "Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U"),
304             'X-FromAppId': 'SCRIPT',
305             'Accept': 'application/json',
306             'Content-Type': 'application/json',
307         },
308         timeout=300,
309         append_slash=False,
310         json_encode_body=True # encode body as json
311     )
312     api.add_resource(resource_name='lcm', resource_class=APPCLcmApiResource)
313     return api
314
315
316 @timing("Load AAI Data")
317 def load_aai_data(vfw_vnf_id, onap_ip):
318     api = _init_python_aai_api(onap_ip)
319     aai_data = {}
320     aai_data['service-info'] = {'global-customer-id': '', 'service-instance-id': '', 'service-type': ''}
321     aai_data['vfw-model-info'] = {'model-invariant-id': '', 'model-version-id': '', 'vnf-name': '', 'vnf-type': ''}
322     aai_data['vpgn-model-info'] = {'model-invariant-id': '', 'model-version-id': '', 'vnf-name': '', 'vnf-type': ''}
323     with _no_ssl_verification():
324         response = api.aai.generic_vnf(vfw_vnf_id, body=None, params={'depth': 2}, headers={})
325         aai_data['vfw-model-info']['model-invariant-id'] = response.body.get('model-invariant-id')
326         aai_data['vfw-model-info']['model-version-id'] = response.body.get('model-version-id')
327         aai_data['vfw-model-info']['vnf-name'] = response.body.get('vnf-name')
328         aai_data['vfw-model-info']['vnf-type'] = response.body.get('vnf-type')
329         aai_data['vf-module-id'] = response.body['vf-modules']['vf-module'][0]['vf-module-id']
330
331         related_to = "service-instance"
332         search_key = "customer.global-customer-id"
333         rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
334         aai_data['service-info']['global-customer-id'] = rl_data_list[0]['d_value']
335
336         search_key = "service-subscription.service-type"
337         rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
338         aai_data['service-info']['service-type'] = rl_data_list[0]['d_value']
339
340         search_key = "service-instance.service-instance-id"
341         rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
342         aai_data['service-info']['service-instance-id'] = rl_data_list[0]['d_value']
343
344         service_link = rl_data_list[0]['link']
345         response = api.aai.link(service_link, body=None, params={}, headers={})
346
347         related_to = "generic-vnf"
348         search_key = "generic-vnf.vnf-id"
349         rl_data_list = _get_aai_rel_link_data(data=response.body, related_to=related_to, search_key=search_key)
350         for i in range(0, len(rl_data_list)):
351             vnf_id = rl_data_list[i]['d_value']
352
353             if vnf_id != vfw_vnf_id:
354                 vnf_link = rl_data_list[i]['link']
355                 response = api.aai.link(vnf_link, body=None, params={}, headers={})
356                 if aai_data['vfw-model-info']['model-invariant-id'] != response.body.get('model-invariant-id'):
357                     aai_data['vpgn-model-info']['model-invariant-id'] = response.body.get('model-invariant-id')
358                     aai_data['vpgn-model-info']['model-version-id'] = response.body.get('model-version-id')
359                     aai_data['vpgn-model-info']['vnf-name'] = response.body.get('vnf-name')
360                     aai_data['vpgn-model-info']['vnf-type'] = response.body.get('vnf-type')
361                     break
362     return aai_data
363
364
365 @timing("> OSDF REQ")
366 def _osdf_request(rancher_ip, onap_ip, aai_data, exclude, use_oof_cache):
367     dirname = os.path.join('templates/oof-cache/', aai_data['vf-module-id'])
368     if exclude:
369         file = os.path.join(dirname, 'sample-osdf-excluded.json')
370     else:
371         file = os.path.join(dirname, 'sample-osdf-required.json')
372     if use_oof_cache and os.path.exists(file):
373         migrate_from = json.loads(open(file).read())
374         return migrate_from
375
376     print('Making OSDF request for excluded {}'.format(str(exclude)))
377     api = _init_python_osdf_api(onap_ip)
378     request_id = str(uuid.uuid4())
379     transaction_id = str(uuid.uuid4())
380     callback_url = "http://{}:9000/osdfCallback/".format(str(rancher_ip))
381     template = json.loads(open('templates/osdfRequest.json').read())
382     template["requestInfo"]["transactionId"] = transaction_id
383     template["requestInfo"]["requestId"] = request_id
384     template["requestInfo"]["callbackUrl"] = callback_url
385     template["serviceInfo"]["serviceInstanceId"] = aai_data['service-info']['service-instance-id']
386     template["placementInfo"]["requestParameters"]["chosenCustomerId"] = aai_data['service-info']['global-customer-id']
387     template["placementInfo"]["placementDemands"][0]["resourceModelInfo"]["modelInvariantId"] =\
388         aai_data['vfw-model-info']['model-invariant-id']
389     template["placementInfo"]["placementDemands"][0]["resourceModelInfo"]["modelVersionId"] =\
390         aai_data['vfw-model-info']['model-version-id']
391     template["placementInfo"]["placementDemands"][1]["resourceModelInfo"]["modelInvariantId"] =\
392         aai_data['vpgn-model-info']['model-invariant-id']
393     template["placementInfo"]["placementDemands"][1]["resourceModelInfo"]["modelVersionId"] =\
394         aai_data['vpgn-model-info']['model-version-id']
395     if exclude:
396         template["placementInfo"]["placementDemands"][0]["excludedCandidates"][0]["identifiers"].\
397             append(aai_data['vf-module-id'])
398         del template["placementInfo"]["placementDemands"][0]["requiredCandidates"]
399     else:
400         template["placementInfo"]["placementDemands"][0]["requiredCandidates"][0]["identifiers"].\
401             append(aai_data['vf-module-id'])
402         del template["placementInfo"]["placementDemands"][0]["excludedCandidates"]
403
404     #print(json.dumps(template, indent=4))
405
406     with _no_ssl_verification():
407         response = api.osdf.placement(body=template, params={}, headers={}) # pylint: disable=W0612
408         #if response.body.get('error_message') is not None:
409         #    raise Exception(response.body['error_message']['explanation'])
410
411     counter = 0
412     while counter < 600 and osdf_response["last"]["id"] != request_id:
413         time.sleep(1)
414         if counter % 20 == 0:
415             print("solving")
416         counter += 1
417
418     if osdf_response["last"]["id"] == request_id:
419         status = osdf_response["last"]["data"]["requestStatus"]
420         if status == "completed":
421             result = {
422                 "solution": osdf_response["last"]["data"]["solutions"]["placementSolutions"]
423             }
424             if not os.path.exists(dirname):
425                 os.makedirs(dirname)
426             f = open(file, 'w+')
427             f.write(json.dumps(result, indent=4))
428             f.close()
429             return result
430         else:
431             message = osdf_response["last"]["data"]["statusMessage"]
432             raise Exception("OOF request {}: {}".format(status, message))
433     else:
434         raise Exception("No response for OOF request")
435
436
437 @timing("> HAS REQ")
438 def _has_request(onap_ip, aai_data, exclude, use_oof_cache):
439     dirname = os.path.join('templates/oof-cache/', aai_data['vf-module-id'])
440     if exclude:
441         file = os.path.join(dirname, 'sample-has-excluded.json')
442     else:
443         file = os.path.join(dirname, 'sample-has-required.json')
444     if use_oof_cache and os.path.exists(file):
445         migrate_from = json.loads(open(file).read())
446         return migrate_from
447
448     print('Making HAS request for excluded {}'.format(str(exclude)))
449     api = _init_python_has_api(onap_ip)
450     request_id = str(uuid.uuid4())
451     template = json.loads(open('templates/hasRequest.json').read())
452     result = {}
453     template['name'] = request_id
454     node = template['template']['parameters']
455     node['chosen_customer_id'] = aai_data['service-info']['global-customer-id']
456     node['service_id'] = aai_data['service-info']['service-instance-id']
457     node = template['template']['demands']['vFW-SINK'][0]
458     node['filtering_attributes']['model-invariant-id'] = aai_data['vfw-model-info']['model-invariant-id']
459     node['filtering_attributes']['model-version-id'] = aai_data['vfw-model-info']['model-version-id']
460     if exclude:
461         node['excluded_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
462         del node['required_candidates']
463     else:
464         node['required_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
465         del node['excluded_candidates']
466     node = template['template']['demands']['vPGN'][0]
467     node['filtering_attributes']['model-invariant-id'] = aai_data['vpgn-model-info']['model-invariant-id']
468     node['filtering_attributes']['model-version-id'] = aai_data['vpgn-model-info']['model-version-id']
469
470     #print(json.dumps(template, indent=4))
471
472     with _no_ssl_verification():
473         response = api.has.plans(body=template, params={}, headers={})
474         if response.body.get('error_message') is not None:
475             raise Exception(response.body['error_message']['explanation'])
476         else:
477             plan_id = response.body['id']
478             response = api.has.plan(plan_id, body=None, params={}, headers={})
479             status = response.body['plans'][0]['status']
480             while status != 'done' and status != 'error':
481                 print(status)
482                 response = api.has.plan(plan_id, body=None, params={}, headers={})
483                 status = response.body['plans'][0]['status']
484             if status == 'done':
485                 result = response.body['plans'][0]['recommendations'][0]
486             else:
487                 raise Exception(response.body['plans'][0]['message'])
488
489     if not os.path.exists(dirname):
490         os.makedirs(dirname)
491     f = open(file, 'w+')
492     f.write(json.dumps(result, indent=4))
493     f.close()
494     return result
495
496
497 def _extract_has_appc_identifiers(has_result, demand, onap_ip):
498     if demand == 'vPGN':
499         v_server = has_result[demand]['attributes']['vservers'][0]
500     else:
501         if len(has_result[demand]['attributes']['vservers'][0]['l-interfaces']) == 4:
502             v_server = has_result[demand]['attributes']['vservers'][0]
503         else:
504             v_server = has_result[demand]['attributes']['vservers'][1]
505     for itf in v_server['l-interfaces']:
506         if itf['ipv4-addresses'][0].startswith("10.0."):
507             ip = itf['ipv4-addresses'][0]
508             break
509
510     if v_server['vserver-name'] in hostname_cache and demand != 'vPGN':
511         v_server['vserver-name'] = v_server['vserver-name'].replace("01", "02")
512     hostname_cache.append(v_server['vserver-name'])
513
514     api = _init_python_aai_api(onap_ip) # pylint: disable=W0612
515     vnfc_type = demand.lower()
516 #    with _no_ssl_verification():
517 #        response = api.aai.vnfc(v_server['vserver-name'], body=None, params={}, headers={})
518 #        vnfc_type = response.body.get('nfc-naming-code')
519
520     config = {
521         'vnf-id': has_result[demand]['attributes']['nf-id'],
522         'vf-module-id': has_result[demand]['attributes']['vf-module-id'],
523         'ip': ip,
524         'vserver-id': v_server['vserver-id'],
525         'vserver-name': v_server['vserver-name'],
526         'vnfc-type': vnfc_type,
527         'physical-location-id': has_result[demand]['attributes']['physical-location-id']
528     }
529     ansible_inventory_entry = "{} ansible_ssh_host={} ansible_ssh_user=ubuntu".format(config['vserver-name'], config['ip'])
530     if demand.lower() not in ansible_inventory:
531         ansible_inventory[demand.lower()] = {}
532     ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
533
534     _verify_vnfc_data(api, onap_ip, config)
535     return config
536
537
538 def _verify_vnfc_data(aai_api, onap_ip, config, root=None):
539     vnfc_name = config['vserver-name']
540     oam_ip = config['ip']
541     with _no_ssl_verification():
542         response = aai_api.aai.vnfc(vnfc_name, body=None, params=None, headers={})
543     #print(json.dumps(response.body))
544     if "ipaddress-v4-oam-vip" not in response.body and oam_ip != "":
545         print("VNFC IP information update for {}".format(vnfc_name))
546         api = _init_python_aai_api(onap_ip, 'application/merge-patch+json')
547         with _no_ssl_verification():
548             response = api.aai.vnfc_patch(vnfc_name, body={"ipaddress-v4-oam-vip": oam_ip, "vnfc-name": vnfc_name}, params=None, headers={})
549     if "relationship-list" not in response.body:
550         print("VNFC REL information update for {}".format(vnfc_name))
551         vserver_info = {
552             "link": "",
553             "owner": "",
554             "region": "",
555             "tenant": "",
556             "id": ""
557         }
558         with _no_ssl_verification():
559             vf_module = aai_api.aai.vf_module(config['vnf-id'], config['vf-module-id'], body=None, params={'depth': 2}, headers={}).body
560         related_to = "vserver"
561         search_key = "cloud-region.cloud-owner"
562         rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
563         vserver_info["owner"] = rl_data_list[0]['d_value']
564
565         search_key = "cloud-region.cloud-region-id"
566         rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
567         vserver_info["region"] = rl_data_list[0]['d_value']
568
569         search_key = "tenant.tenant-id"
570         rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
571         vserver_info["tenant"] = rl_data_list[0]['d_value']
572
573         search_key = "vserver.vserver-id"
574         rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
575         for relation in rl_data_list:
576             vserver_info["id"] = relation['d_value']
577             vserver_info["link"] = relation['link']
578
579             rel_data = {
580                 "related-to": "vserver",
581                 "related-link": vserver_info["link"],
582                 "relationship-data": [
583                     {
584                         "relationship-key": "cloud-region.cloud-owner",
585                         "relationship-value": vserver_info["owner"]
586                     },
587                     {
588                         "relationship-key": "cloud-region.cloud-region-id",
589                         "relationship-value": vserver_info["region"]
590                     },
591                     {
592                         "relationship-key": "tenant.tenant-id",
593                         "relationship-value": vserver_info["tenant"]
594                     },
595                     {
596                         "relationship-key": "vserver.vserver-id",
597                         "relationship-value": vserver_info["id"]
598                     }
599                 ]
600             }
601             #print(json.dumps(rel_data, indent=4))
602             if config['vserver-id'] == relation['d_value']:
603                 with _no_ssl_verification():
604                     response = aai_api.aai.vnfc_put("{}/relationship-list/relationship".format(vnfc_name), body=rel_data, params=None, headers={})
605             elif root is None and relation['d_value'] is not None:
606                 new_config = copy.deepcopy(config)
607                 new_config['vserver-name'] = relation['property']
608                 new_config['vserver-id'] = relation['d_value']
609                 new_config['ip'] = ""
610                 _verify_vnfc_data(aai_api, onap_ip, new_config, vnfc_name)
611     with _no_ssl_verification():
612         response = aai_api.aai.vnfc(vnfc_name, body=None, params=None, headers={})
613         #print(json.dumps(response.body))
614
615
616 def _extract_osdf_appc_identifiers(has_result, demand, onap_ip):
617     if demand == 'vPGN':
618         v_server = has_result[demand]['vservers'][0]
619     else:
620         if len(has_result[demand]['vservers'][0]['l-interfaces']) == 4:
621             v_server = has_result[demand]['vservers'][0]
622         else:
623             v_server = has_result[demand]['vservers'][1]
624     for itf in v_server['l-interfaces']:
625         if itf['ipv4-addresses'][0].startswith("10.0."):
626             ip = itf['ipv4-addresses'][0]
627             break
628
629     if v_server['vserver-name'] in hostname_cache and demand != 'vPGN':
630         v_server['vserver-name'] = v_server['vserver-name'].replace("01", "02")
631     hostname_cache.append(v_server['vserver-name'])
632
633     api = _init_python_aai_api(onap_ip)
634     vnfc_type = demand.lower(),
635     with _no_ssl_verification():
636         response = api.aai.vnfc(v_server['vserver-name'], body=None, params={}, headers={})
637         vnfc_type = response.body.get('nfc-naming-code')
638
639     config = {
640         'vnf-id': has_result[demand]['nf-id'],
641         'vf-module-id': has_result[demand]['vf-module-id'],
642         'ip': ip,
643         'vserver-id': v_server['vserver-id'],
644         'vserver-name': v_server['vserver-name'],
645         'vnfc-type': vnfc_type,
646         'physical-location-id': has_result[demand]['locationId']
647     }
648     ansible_inventory_entry = "{} ansible_ssh_host={} ansible_ssh_user=ubuntu".format(config['vserver-name'], config['ip'])
649     if demand.lower() not in ansible_inventory:
650         ansible_inventory[demand.lower()] = {}
651     ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
652
653     _verify_vnfc_data(api, onap_ip, config)
654
655     return config
656
657
658 def _extract_has_appc_dt_config(has_result, demand):
659     if demand == 'vPGN':
660         return {}
661     else:
662         config = {
663             "nf-type": has_result[demand]['attributes']['nf-type'],
664             "nf-name": has_result[demand]['attributes']['nf-name'],
665             "vf-module-name": has_result[demand]['attributes']['vf-module-name'],
666             "vnf-type": has_result[demand]['attributes']['vnf-type'],
667             "service_instance_id": "319e60ef-08b1-47aa-ae92-51b97f05e1bc",
668             "cloudClli": has_result[demand]['attributes']['physical-location-id'],
669             "nf-id": has_result[demand]['attributes']['nf-id'],
670             "vf-module-id": has_result[demand]['attributes']['vf-module-id'],
671             "aic_version": has_result[demand]['attributes']['aic_version'],
672             "ipv4-oam-address": has_result[demand]['attributes']['ipv4-oam-address'],
673             "vnfHostName": has_result[demand]['candidate']['host_id'],
674             "cloudOwner": has_result[demand]['candidate']['cloud_owner'],
675             "isRehome": has_result[demand]['candidate']['is_rehome'],
676             "locationId": has_result[demand]['candidate']['location_id'],
677             "locationType": has_result[demand]['candidate']['location_type'],
678             'vservers': has_result[demand]['attributes']['vservers']
679         }
680         return config
681
682
683 def _extract_osdf_appc_dt_config(osdf_result, demand):
684     if demand == 'vPGN':
685         return {}
686     else:
687         return osdf_result[demand]
688
689
690 def _build_config_from_has(has_result, onap_ip):
691     v_pgn_result = _extract_has_appc_identifiers(has_result, 'vPGN', onap_ip)
692     v_fw_result = _extract_has_appc_identifiers(has_result, 'vFW-SINK', onap_ip)
693     dt_config = _extract_has_appc_dt_config(has_result, 'vFW-SINK')
694
695     config = {
696         'vPGN': v_pgn_result,
697         'vFW-SINK': v_fw_result
698     }
699     #print(json.dumps(config, indent=4))
700     config['dt-config'] = {
701         'destinations': [dt_config]
702     }
703     return config
704
705
706 def _adapt_osdf_result(osdf_result):
707     result = {}
708     demand = _build_osdf_result_demand(osdf_result["solution"][0][0])
709     result[demand["name"]] = demand["value"]
710     demand = _build_osdf_result_demand(osdf_result["solution"][0][1])
711     result[demand["name"]] = demand["value"]
712     return result
713
714
715 def _build_osdf_result_demand(solution):
716     result = {}
717     result["name"] = solution["resourceModuleName"]
718     value = {"candidateId": solution["solution"]["identifiers"][0]}
719     for info in solution["assignmentInfo"]:
720         value[info["key"]] = info["value"]
721     result["value"] = value
722     return result
723
724
725 def _build_config_from_osdf(osdf_result, onap_ip):
726     osdf_result = _adapt_osdf_result(osdf_result)
727     v_pgn_result = _extract_osdf_appc_identifiers(osdf_result, 'vPGN', onap_ip)
728     v_fw_result = _extract_osdf_appc_identifiers(osdf_result, 'vFW-SINK', onap_ip)
729     dt_config = _extract_osdf_appc_dt_config(osdf_result, 'vFW-SINK')
730
731     config = {
732         'vPGN': v_pgn_result,
733         'vFW-SINK': v_fw_result
734     }
735     #print(json.dumps(config, indent=4))
736     config['dt-config'] = {
737         'destinations': [dt_config]
738     }
739     return config
740
741
742 def _build_appc_lcm_dt_payload(demand, oof_config, action, traffic_presence):
743     is_check = traffic_presence is not None
744     oof_config = copy.deepcopy(oof_config)
745     #if is_vpkg:
746     #    node_list = "[ {} ]".format(oof_config['vPGN']['vserver-id'])
747     #else:
748     #    node_list = "[ {} ]".format(oof_config['vFW-SINK']['vserver-id'])
749     book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
750     config = oof_config[demand]
751     #node = {
752     #    'site': config['physical-location-id'],
753     #    'vnfc_type': config['vnfc-type'],
754     #    'vm_info': [{
755     #        'ne_id': config['vserver-name'],
756     #        'fixed_ip_address': config['ip']
757     #   }]
758     #}
759     #node_list = list()
760     #node_list.append(node)
761
762     if is_check:
763         oof_config['dt-config']['trafficpresence'] = traffic_presence
764
765     file_content = oof_config['dt-config']
766
767     config = {
768         "configuration-parameters": {
769             "file_parameter_content":  json.dumps(file_content)
770         },
771         "request-parameters": {
772             "vserver-id": config['vserver-id']
773         }
774     }
775     if book_name != '':
776         config["configuration-parameters"]["book_name"] = book_name
777     payload = json.dumps(config)
778     return payload
779
780
781 def _build_appc_lcm_upgrade_payload(demand, oof_config, action, old_version, new_version):
782     oof_config = copy.deepcopy(oof_config)
783     book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
784     config = oof_config[demand]
785
786     file_content = {}  #oof_config['dt-config']
787
788     config = {
789         "configuration-parameters": {
790             "file_parameter_content":  json.dumps(file_content),
791             "existing-software-version": old_version,
792             "new-software-version": new_version
793         },
794         "request-parameters": {
795             "vserver-id": config['vserver-id']
796         }
797     }
798     if book_name != '':
799         config["configuration-parameters"]["book_name"] = book_name
800     payload = json.dumps(config)
801     return payload
802
803
804 def _build_appc_lcm_status_body(req):
805     payload = {
806         'request-id': req['input']['common-header']['request-id'],
807         'sub-request-id': req['input']['common-header']['sub-request-id'],
808         'originator-id': req['input']['common-header']['originator-id']
809     }
810     payload = json.dumps(payload)
811     template = json.loads(open('templates/appcRestconfLcm.json').read())
812     template['input']['action'] = 'ActionStatus'
813     template['input']['payload'] = payload
814     template['input']['common-header']['request-id'] = req['input']['common-header']['request-id']
815     template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
816     template['input']['action-identifiers']['vnf-id'] = req['input']['action-identifiers']['vnf-id']
817     return template
818
819
820 @timing("> DT REQ BODY")
821 def _build_appc_lcm_lock_request_body(is_vpkg, config, req_id, action):
822     if is_vpkg:
823         demand = 'vPGN'
824     else:
825         demand = 'vFW-SINK'
826     return _build_appc_lcm_request_body(None, demand, config, req_id, action)
827
828
829 @timing("> DT REQ BODY")
830 def _build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
831     if is_vpkg:
832         demand = 'vPGN'
833     else:
834         demand = 'vFW-SINK'
835     payload = _build_appc_lcm_dt_payload(demand, config, action, traffic_presence)
836     return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
837
838
839 @timing("> UP REQ BODY")
840 def _build_appc_lcm_upgrade_request_body(config, req_id, action, old_version, new_version):
841     demand = 'vFW-SINK'
842     payload = _build_appc_lcm_upgrade_payload(demand, config, action, old_version, new_version)
843     return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
844
845
846 def _build_appc_lcm_request_body(payload, demand, config, req_id, action):
847     #print(config[demand])
848     template = json.loads(open('templates/appcRestconfLcm.json').read())
849     template['input']['action'] = action
850     if payload is not None:
851         template['input']['payload'] = payload
852     else:
853         del template['input']['payload']
854     template['input']['common-header']['request-id'] = req_id
855     template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
856     template['input']['action-identifiers']['vnf-id'] = config[demand]['vnf-id']
857     return template
858
859
860 def _set_appc_lcm_timestamp(body, timestamp=None):
861     if timestamp is None:
862         t = datetime.utcnow() + timedelta(seconds=-10)
863         timestamp = t.strftime('%Y-%m-%dT%H:%M:%S.244Z')
864     body['input']['common-header']['timestamp'] = timestamp
865
866
867 @timing("Load OOF Data and Build APPC REQ")
868 def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version=None):
869     if_has = False
870
871     if if_has:
872         migrate_from = _has_request(onap_ip, aai_data, False, use_oof_cache)
873
874         if if_close_loop_vfw:
875             migrate_to = migrate_from
876         else:
877             migrate_to = _has_request(onap_ip, aai_data, True, use_oof_cache)
878
879         migrate_from = _build_config_from_has(migrate_from, onap_ip)
880         migrate_to = _build_config_from_has(migrate_to, onap_ip)
881     else:
882         migrate_from = _osdf_request(rancher_ip, onap_ip, aai_data, False, use_oof_cache)
883
884         if if_close_loop_vfw:
885             migrate_to = migrate_from
886         else:
887             migrate_to = _osdf_request(rancher_ip, onap_ip, aai_data, True, use_oof_cache)
888
889         migrate_from = _build_config_from_osdf(migrate_from, onap_ip)
890         migrate_to = _build_config_from_osdf(migrate_to, onap_ip)
891
892     #print(json.dumps(migrate_from, indent=4))
893     #print(json.dumps(migrate_to, indent=4))
894     req_id = str(uuid.uuid4())
895     result = list()
896     old_version = "2.0"
897     if_dt_only = new_version is None
898     if new_version is not None and new_version != "1.0":
899         old_version = "1.0"
900
901     requests = list()
902     include_lock = True
903
904     if include_lock:
905         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'CheckLock'), "breakOnFailure": True,
906                       "description": "Check vPGN Lock Status"})
907         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'CheckLock'), "breakOnFailure": True,
908                       "description": "Check vFW-1 Lock Status"})
909         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'CheckLock'), "breakOnFailure": True,
910                       "description": "Check vFW-2 Lock "})
911
912         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'Lock'), "breakOnFailure": True,
913                       "description": "Lock vPGN"})
914         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'Lock'), "breakOnFailure": True,
915                       "description": "Lock vFW-1"})
916         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'Lock'), "breakOnFailure": True,
917                       "description": "Lock vFW-2"})
918
919     if if_dt_only:
920         payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
921         payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
922         payload_dt_check_vfw_from = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck',
923                                                                     False)
924         payload_dt_check_vfw_to = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
925
926         requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
927         requests.append({"payload": payload_dt_check_vfw_from, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
928         requests.append({"payload": payload_dt_check_vfw_to, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
929         result.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
930                       "workflow": {"requests": requests, "description": "Migrate Traffic and Verify"}})
931     else:
932         #_build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
933         payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
934         payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
935         payload_dt_vpkg_from = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTraffic')
936
937         payload_dt_check_vfw_from_absent = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', False)
938         payload_dt_check_vfw_to_present = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
939         payload_dt_check_vfw_to_absent = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', False)
940         payload_dt_check_vfw_from_present = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', True)
941
942         payload_old_version_check_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePreCheck', old_version, new_version)
943         payload_new_version_check_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePostCheck', old_version, new_version)
944         payload_upgrade_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradeSoftware', old_version, new_version)
945
946         migrate_requests = list()
947         migrate_requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
948         migrate_requests.append({"payload": payload_dt_check_vfw_from_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
949         migrate_requests.append({"payload": payload_dt_check_vfw_to_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
950
951         requests.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
952                         "workflow": {"requests": migrate_requests, "description": "Migrate Traffic and Verify"}})
953         requests.append({"payload": payload_upgrade_vfw_from, "breakOnFailure": True, "description": "Upgrading Software on source vFW"})
954         requests.append({"payload": payload_new_version_check_vfw_from, "breakOnFailure": True, "description": "Check current software version on source vFW"})
955         requests.append({"payload": payload_dt_vpkg_from, "breakOnFailure": True, "description": "Migrating destination vFW traffic to source vFW"})
956         requests.append({"payload": payload_dt_check_vfw_to_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the destination vFW"})
957         requests.append({"payload": payload_dt_check_vfw_from_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the source vFW"})
958
959         result.append({"payload": payload_old_version_check_vfw_from, "breakOnFailure": False, "description": "Check current software version on source vFW",
960                       "workflow": {"requests": requests, "description": "Migrate Traffic and Upgrade Software"}})
961
962     if include_lock:
963         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'Unlock'), "breakOnFailure": False,
964                       "description": "Unlock vPGN"})
965         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'Unlock'), "breakOnFailure": False,
966                       "description": "Unlock vFW-1"})
967         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'Unlock'), "breakOnFailure": False,
968                       "description": "Unlock vFW-2"})
969
970     return result
971
972
973 @timing("> Execute APPC REQ")
974 def appc_lcm_request(onap_ip, req):
975     #print(req)
976     api = _init_python_appc_lcm_api(onap_ip)
977     with _no_ssl_verification():
978     #print(json.dumps(req, indent=4))
979         if req['input']['action'] == "DistributeTraffic":
980             result = api.lcm.distribute_traffic(body=req, params={}, headers={})
981         elif req['input']['action'] == "DistributeTrafficCheck":
982             result = api.lcm.distribute_traffic_check(body=req, params={}, headers={})
983         elif req['input']['action'] == "UpgradeSoftware":
984             result = api.lcm.upgrade_software(body=req, params={}, headers={})
985         elif req['input']['action'] == "UpgradePreCheck":
986             result = api.lcm.upgrade_pre_check(body=req, params={}, headers={})
987         elif req['input']['action'] == "UpgradePostCheck":
988             result = api.lcm.upgrade_post_check(body=req, params={}, headers={})
989         elif req['input']['action'] == "CheckLock":
990             result = api.lcm.check_lock(body=req, params={}, headers={})
991         elif req['input']['action'] == "Lock":
992             result = api.lcm.lock(body=req, params={}, headers={})
993         elif req['input']['action'] == "Unlock":
994             result = api.lcm.unlock(body=req, params={}, headers={})
995         else:
996             raise Exception("{} action not supported".format(req['input']['action']))
997
998     if result.body['output']['status']['code'] == 400:
999         if req['input']['action'] == "CheckLock":
1000             if result.body['output']['locked'] == "FALSE":
1001                 print("UNLOCKED")
1002             else:
1003                 print("LOCKED")
1004                 result.body['output']['status']['code'] = 401
1005         else:
1006             print("SUCCESSFUL")
1007     elif result.body['output']['status']['code'] == 100:
1008         print("ACCEPTED")
1009     elif result.body['output']['status']['code'] >= 300 and result.body['output']['status']['code'] < 400:
1010         print("APPC LCM <<{}>> REJECTED [{} - {}]".format(req['input']['action'], result.body['output']['status']['code'],
1011                                          result.body['output']['status']['message']))
1012     elif result.body['output']['status']['code'] > 400 and result.body['output']['status']['code'] < 500:
1013         print("APPC LCM <<{}>> FAILED [{} - {}]".format(req['input']['action'], result.body['output']['status']['code'],
1014                                          result.body['output']['status']['message']))
1015 #    elif result.body['output']['status']['code'] == 311:
1016 #        timestamp = result.body['output']['common-header']['timestamp']
1017 #        _set_appc_lcm_timestamp(req, timestamp)
1018 #        appc_lcm_request(onap_ip, req)
1019 #        return
1020     else:
1021         raise Exception("{} - {}".format(result.body['output']['status']['code'],
1022                                          result.body['output']['status']['message']))
1023     #print(result)
1024     return result.body['output']['status']['code']
1025
1026
1027 def appc_lcm_status_request(onap_ip, req):
1028     api = _init_python_appc_lcm_api(onap_ip)
1029     status_body = _build_appc_lcm_status_body(req)
1030     _set_appc_lcm_timestamp(status_body)
1031     #print("CHECK STATUS")
1032     with _no_ssl_verification():
1033         result = api.lcm.action_status(body=status_body, params={}, headers={})
1034
1035     if result.body['output']['status']['code'] == 400:
1036         status = json.loads(result.body['output']['payload'])
1037         return status
1038     else:
1039         raise Exception("{} - {}".format(result.body['output']['status']['code'],
1040                                          result.body['output']['status']['message']))
1041
1042
1043 @timing("> Confirm APPC REQ")
1044 def confirm_appc_lcm_action(onap_ip, req, check_appc_result):
1045     print("APPC LCM << {} >> [Status]".format(req['input']['action']))
1046
1047     while True:
1048         time.sleep(2)
1049         status = appc_lcm_status_request(onap_ip, req)
1050         print(status['status'])
1051         if status['status'] == 'SUCCESSFUL':
1052             return True
1053         elif status['status'] == 'IN_PROGRESS':
1054             continue
1055         elif check_appc_result:
1056             print("APPC LCM <<{}>> [{} - {}]".format(req['input']['action'], status['status'], status['status-reason']))
1057             return False
1058         else:
1059             return True
1060
1061
1062 @timing("Execute APPC LCM REQs")
1063 def _execute_lcm_requests(workflow, onap_ip, check_result):
1064     lcm_requests = workflow["requests"]
1065     print("WORKFLOW << {} >>".format(workflow["description"]))
1066     for i in range(len(lcm_requests)):
1067         req = lcm_requests[i]["payload"]
1068         #print(json.dumps(req, indent=4))
1069         print("APPC LCM << {} >> [{}]".format(req['input']['action'], lcm_requests[i]["description"]))
1070         _set_appc_lcm_timestamp(req)
1071         conf_result = False
1072         result = appc_lcm_request(onap_ip, req)
1073         #print("Result {}".format(result))
1074
1075         if result == 100:
1076             conf_result = confirm_appc_lcm_action(onap_ip, req, check_result)
1077             #time.sleep(30)
1078         elif result == 400:
1079             conf_result = True
1080
1081         if not conf_result:
1082             if lcm_requests[i]["breakOnFailure"]:
1083                 raise Exception("APPC LCM << {} >> FAILED".format(req['input']['action']))
1084             elif "workflow" in lcm_requests[i]:
1085                 print("WORKFLOW << {} >> SKIP".format(lcm_requests[i]["workflow"]["description"]))
1086         elif "workflow" in lcm_requests[i]:
1087             _execute_lcm_requests(lcm_requests[i]["workflow"], onap_ip, check_result)
1088
1089
1090 def _generate_cdt_artifact_request(req_id, artifact, action, vnfc_type):
1091     req = {
1092       'input': {
1093           'design-request': {
1094               'request-id': req_id,
1095               'action': "uploadArtifact",
1096               'payload': json.dumps(artifact['payload'])
1097           }
1098        }
1099     }
1100
1101     file = "{}_{}_{}.json".format(artifact['type'], action.lower(), vnfc_type)
1102     dirname = "templates/cdt-requests"
1103     #print(file)
1104     if not os.path.exists(dirname):
1105         os.makedirs(dirname)
1106     f = open("{}/{}".format(dirname, file), 'w')
1107     f.write(json.dumps(req, indent=4))
1108     f.close()
1109
1110     return req
1111
1112
1113 def _get_name_of_artifact(prefix, action, vnf_type):
1114     return "{}_{}_{}_0.0.1V.json".format(prefix, action, vnf_type)
1115
1116
1117 def _set_artifact_payload(vnf_type, vnfc_type, action, artifact):
1118     sw_upgrade = False
1119     if action == "DistributeTraffic" or action == "DistributeTrafficCheck" or action == "AllAction":
1120         pass
1121     elif action == "UpgradeSoftware" or action == "UpgradePreCheck" or action == "UpgradePostCheck":
1122         sw_upgrade = True
1123     else:
1124         raise Exception("{} action not supported".format(action))
1125
1126     artifact_contents = ''
1127     if artifact['type'] == 'config_template':
1128         file = 'templates/cdt-templates/templates/action-template.json'
1129         template_file = 'templates/cdt-templates/{}/{}'
1130         if sw_upgrade:
1131             template_file = template_file.format(vnfc_type, 'upgrade.json')
1132         else:
1133             template_file = template_file.format(vnfc_type, 'traffic.json')
1134         #print("Template for action {} in {}".format(action, template_file))
1135         #print(json.dumps(json.loads(open(template_file).read()), indent=4))
1136         artifact_contents = json.dumps(json.loads(open(template_file).read()), indent=4).replace("\n", "\r\n")
1137     elif artifact['type'] == 'parameter_definitions':
1138         file = 'templates/cdt-templates/templates/{}'
1139         if sw_upgrade:
1140             file = file.format('upgrade-params.json')
1141         else:
1142             file = file.format('traffic-params.json')
1143     elif artifact['type'] == 'param_values':
1144         file = 'templates/cdt-templates/templates/{}'
1145         if sw_upgrade:
1146             file = file.format('upgrade-params-list.json')
1147         else:
1148             file = file.format('traffic-params-list.json')
1149     elif artifact['type'] == 'reference_template':
1150         file = 'templates/cdt-templates/templates/reference-all-actions.json'
1151     else:
1152         raise Exception("{} not supported artifact type".format(artifact['type']))
1153
1154     payload = json.loads(open(file).read())
1155     payload['vnf-type'] = vnf_type
1156     payload['artifact-name'] = artifact['name']
1157     payload['action'] = action
1158
1159     if artifact['type'] == 'config_template':
1160         payload['artifact-contents'] = artifact_contents
1161     artifact['payload'] = payload
1162
1163
1164 def _generate_artifacts_for_cdt(vnf_type, vnf_type_formatted, vnfc_type, action):
1165     artifacts = []
1166     artifacts.append({
1167         'name': _get_name_of_artifact("template", action, vnf_type_formatted),
1168         'type': 'config_template',
1169         'payload': {'test': 'test'}
1170     })
1171     artifacts.append({
1172         'name': _get_name_of_artifact("pd", action, vnf_type_formatted),
1173         'type': 'parameter_definitions',
1174         'payload': {'test': 'test'}
1175     })
1176     artifacts.append({
1177         'name': _get_name_of_artifact("param", action, vnf_type_formatted),
1178         'type': 'param_values',
1179         'payload': {'test': 'test'}
1180     })
1181
1182     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[0])
1183     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[1])
1184     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[2])
1185
1186     return artifacts
1187
1188
1189 def _generate_cdt_payloads_for_vnf(vnf_info, vnfc_type, actions):
1190     req_id = str(uuid.uuid4()).replace('-','')
1191     vnf_type_formatted = vnf_info['vnf-type'].replace(' ','').replace('/', '_')
1192     artifacts = {
1193         'AllAction': [{
1194             'name': _get_name_of_artifact("reference", 'AllAction', vnf_type_formatted),
1195             'type': 'reference_template'
1196         }]
1197     }
1198
1199     all_action_artifact = artifacts['AllAction'][0]
1200
1201     _set_artifact_payload(vnf_info['vnf-type'], vnfc_type, 'AllAction', all_action_artifact)
1202
1203     for action in actions:
1204         action_artifacts = _generate_artifacts_for_cdt(vnf_info['vnf-type'], vnf_type_formatted, vnfc_type, action)
1205         artifacts[action] = action_artifacts
1206
1207     all_action_artifacts = list()
1208
1209     for action in artifacts:
1210         artifact_list = list()
1211         action_info = {
1212             'action': action,
1213             'action-level': "vnf",
1214             'scope': {
1215                  'vnf-type': vnf_info['vnf-type'],
1216                  'vnfc-type-list': [],
1217                  'vnfc-type': ""
1218             },
1219             'artifact-list': artifact_list
1220         }
1221
1222         if action != 'AllAction':
1223             action_info.update({
1224                 'template': "Y",
1225                 'vm': [],
1226                 'device-protocol': "ANSIBLE",
1227                 'user-name': "admin",
1228                 'port-number': "8000",
1229                 'scopeType': "vnf-type"
1230             })
1231
1232         for action_artifact in artifacts[action]:
1233             artifact_list.append({'artifact-name': action_artifact['name'], 'artifact-type': action_artifact['type']})
1234             if action != 'AllAction':
1235                 req = _generate_cdt_artifact_request(req_id, action_artifact, action, vnfc_type) # pylint: disable=W0612
1236                 #print(json.dumps(req, indent=4))
1237
1238         #print(json.dumps(action_info, indent=4))
1239         all_action_artifacts.append(action_info)
1240
1241     all_action_artifact['payload']['artifact-contents'] = json.dumps({'reference_data': all_action_artifacts})
1242     req = _generate_cdt_artifact_request(req_id, all_action_artifact, 'AllAction', vnfc_type)
1243     #print(json.dumps(req, indent=4))
1244
1245
1246 def _generate_cdt_payloads(aai_data):
1247     vfw_actions = ["DistributeTrafficCheck", "UpgradeSoftware", "UpgradePreCheck", "UpgradePostCheck", "UpgradeSoftware"]
1248     vpgn_actions = ["DistributeTraffic", "DistributeTrafficCheck"]
1249     _generate_cdt_payloads_for_vnf(aai_data["vfw-model-info"], "vfw-sink", vfw_actions)
1250     _generate_cdt_payloads_for_vnf(aai_data["vpgn-model-info"], "vpgn", vpgn_actions)
1251
1252
1253 def execute_workflow(vfw_vnf_id, rancher_ip, onap_ip, use_oof_cache, if_close_loop_vfw, info_only, check_result, new_version=None):
1254     print("\nExecuting workflow for VNF ID '{}' on Rancher with IP {} and ONAP with IP {}".format(
1255         vfw_vnf_id, rancher_ip, onap_ip))
1256     print("\nOOF Cache {}, is CL vFW {}, only info {}, check LCM result {}".format(use_oof_cache, if_close_loop_vfw,
1257                                                                                    info_only, check_result))
1258     if new_version is not None:
1259         print("\nNew vFW software version {}\n".format(new_version))
1260
1261     x = threading.Thread(target=_run_osdf_resp_server, daemon=True)
1262     x.start()
1263     aai_data = load_aai_data(vfw_vnf_id, onap_ip)
1264     print("\nvFWDT Service Information:")
1265     print(json.dumps(aai_data, indent=4))
1266     lcm_requests = build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version)
1267     print("\nAnsible Inventory:")
1268     inventory = "[host]\nlocalhost   ansible_connection=local\n"
1269     for key in ansible_inventory:
1270         inventory += str("[{}]\n").format(key)
1271         for host in ansible_inventory[key]:
1272             inventory += str("{}\n").format(ansible_inventory[key][host])
1273
1274     print(inventory)
1275     f = open("Ansible_inventory", 'w+')
1276     f.write(inventory)
1277     f.close()
1278
1279     _generate_cdt_payloads(aai_data)
1280
1281     if info_only:
1282         return
1283     print("\nDistribute Traffic Workflow Execution:")
1284
1285     _execute_lcm_requests({"requests": lcm_requests, "description": "Migrate vFW Traffic Conditionally"}, onap_ip, check_result)
1286
1287
1288
1289 help = """\npython3 workflow.py <VNF-ID> <RANCHER-NODE-IP> <K8S-NODE-IP> <IF-CACHE> <IF-VFWCL> <INITIAL-ONLY> <CHECK-STATUS> <VERSION>
1290 \n<VNF-ID> - vnf-id of vFW VNF instance that traffic should be migrated out from
1291 <RANCHER-NODE-IP> - External IP of ONAP Rancher Node i.e. 10.12.5.160 (If Rancher Node is missing this is NFS node)
1292 <K8S-NODE-IP> - External IP of ONAP K8s Worker Node i.e. 10.12.5.212
1293 <IF-CACHE> - If script should use and build OOF response cache (cache it speed-ups further executions of script)
1294 <IF-VFWCL> - If instead of vFWDT service instance vFW or vFWCL one is used (should be False always)
1295 <INITIAL-ONLY> - If only configuration information will be collected (True for initial phase and False for full execution of workflow)
1296 <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)
1297 <VERSION> - New version of vFW - for tests '1.0' or '2.0'. Ommit when traffic distribution only\n"""
1298
1299 for key in sys.argv:
1300     if key == "-h" or key == "--help":
1301         print(help)
1302         sys.exit()
1303
1304 new_version = None
1305 if len(sys.argv) > 8:
1306     new_version = sys.argv[8]
1307
1308 try:
1309     #vnf_id, Rancher node IP, K8s node IP, use OOF cache, if close loop vfw, if info_only, if check APPC result
1310     execute_workflow(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4].lower() == 'true', sys.argv[5].lower() == 'true',
1311                      sys.argv[6].lower() == 'true', sys.argv[7].lower() == 'true', new_version)
1312 finally:
1313     stats.close()