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