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