Fixed secrets used for appc
[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
580     _verify_vnfc_data(api, onap_ip, config['vserver-name'], config['ip'])
581
582     return config
583
584
585 def _extract_has_appc_dt_config(has_result, demand):
586     if demand == 'vPGN':
587         return {}
588     else:
589         config = {
590             "nf-type": has_result[demand]['attributes']['nf-type'],
591             "nf-name": has_result[demand]['attributes']['nf-name'],
592             "vf-module-name": has_result[demand]['attributes']['vf-module-name'],
593             "vnf-type": has_result[demand]['attributes']['vnf-type'],
594             "service_instance_id": "319e60ef-08b1-47aa-ae92-51b97f05e1bc",
595             "cloudClli": has_result[demand]['attributes']['physical-location-id'],
596             "nf-id": has_result[demand]['attributes']['nf-id'],
597             "vf-module-id": has_result[demand]['attributes']['vf-module-id'],
598             "aic_version": has_result[demand]['attributes']['aic_version'],
599             "ipv4-oam-address": has_result[demand]['attributes']['ipv4-oam-address'],
600             "vnfHostName": has_result[demand]['candidate']['host_id'],
601             "cloudOwner": has_result[demand]['candidate']['cloud_owner'],
602             "isRehome": has_result[demand]['candidate']['is_rehome'],
603             "locationId": has_result[demand]['candidate']['location_id'],
604             "locationType": has_result[demand]['candidate']['location_type'],
605             'vservers': has_result[demand]['attributes']['vservers']
606         }
607         return config
608
609
610 def _extract_osdf_appc_dt_config(osdf_result, demand):
611     if demand == 'vPGN':
612         return {}
613     else:
614         return osdf_result[demand]
615
616
617 def _build_config_from_has(has_result, onap_ip):
618     v_pgn_result = _extract_has_appc_identifiers(has_result, 'vPGN', onap_ip)
619     v_fw_result = _extract_has_appc_identifiers(has_result, 'vFW-SINK', onap_ip)
620     dt_config = _extract_has_appc_dt_config(has_result, 'vFW-SINK')
621
622     config = {
623         'vPGN': v_pgn_result,
624         'vFW-SINK': v_fw_result
625     }
626     #print(json.dumps(config, indent=4))
627     config['dt-config'] = {
628         'destinations': [dt_config]
629     }
630     return config
631
632
633 def _adapt_osdf_result(osdf_result):
634     result = {}
635     demand = _build_osdf_result_demand(osdf_result["solution"][0][0])
636     result[demand["name"]] = demand["value"]
637     demand = _build_osdf_result_demand(osdf_result["solution"][0][1])
638     result[demand["name"]] = demand["value"]
639     return result
640
641
642 def _build_osdf_result_demand(solution):
643     result = {}
644     result["name"] = solution["resourceModuleName"]
645     value = {"candidateId": solution["solution"]["identifiers"][0]}
646     for info in solution["assignmentInfo"]:
647         value[info["key"]] = info["value"]
648     result["value"] = value
649     return result
650
651
652 def _build_config_from_osdf(osdf_result, onap_ip):
653     osdf_result = _adapt_osdf_result(osdf_result)
654     v_pgn_result = _extract_osdf_appc_identifiers(osdf_result, 'vPGN', onap_ip)
655     v_fw_result = _extract_osdf_appc_identifiers(osdf_result, 'vFW-SINK', onap_ip)
656     dt_config = _extract_osdf_appc_dt_config(osdf_result, 'vFW-SINK')
657
658     config = {
659         'vPGN': v_pgn_result,
660         'vFW-SINK': v_fw_result
661     }
662     #print(json.dumps(config, indent=4))
663     config['dt-config'] = {
664         'destinations': [dt_config]
665     }
666     return config
667
668
669 def _build_appc_lcm_dt_payload(demand, oof_config, action, traffic_presence):
670     is_check = traffic_presence is not None
671     oof_config = copy.deepcopy(oof_config)
672     #if is_vpkg:
673     #    node_list = "[ {} ]".format(oof_config['vPGN']['vserver-id'])
674     #else:
675     #    node_list = "[ {} ]".format(oof_config['vFW-SINK']['vserver-id'])
676     book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
677     config = oof_config[demand]
678     #node = {
679     #    'site': config['physical-location-id'],
680     #    'vnfc_type': config['vnfc-type'],
681     #    'vm_info': [{
682     #        'ne_id': config['vserver-name'],
683     #        'fixed_ip_address': config['ip']
684     #   }]
685     #}
686     #node_list = list()
687     #node_list.append(node)
688
689     if is_check:
690         oof_config['dt-config']['trafficpresence'] = traffic_presence
691
692     file_content = oof_config['dt-config']
693
694     config = {
695         "configuration-parameters": {
696             "file_parameter_content":  json.dumps(file_content)
697         },
698         "request-parameters": {
699             "vserver-id": config['vserver-id']
700         }
701     }
702     if book_name != '':
703         config["configuration-parameters"]["book_name"] = book_name
704     payload = json.dumps(config)
705     return payload
706
707
708 def _build_appc_lcm_upgrade_payload(demand, oof_config, action, old_version, new_version):
709     oof_config = copy.deepcopy(oof_config)
710     book_name = "{}/latest/ansible/{}/site.yml".format(demand.lower(), action.lower())
711     config = oof_config[demand]
712
713     file_content = {}  #oof_config['dt-config']
714
715     config = {
716         "configuration-parameters": {
717             "file_parameter_content":  json.dumps(file_content),
718             "existing-software-version": old_version,
719             "new-software-version": new_version
720         },
721         "request-parameters": {
722             "vserver-id": config['vserver-id']
723         }
724     }
725     if book_name != '':
726         config["configuration-parameters"]["book_name"] = book_name
727     payload = json.dumps(config)
728     return payload
729
730
731 def _build_appc_lcm_status_body(req):
732     payload = {
733         'request-id': req['input']['common-header']['request-id'],
734         'sub-request-id': req['input']['common-header']['sub-request-id'],
735         'originator-id': req['input']['common-header']['originator-id']
736     }
737     payload = json.dumps(payload)
738     template = json.loads(open('templates/appcRestconfLcm.json').read())
739     template['input']['action'] = 'ActionStatus'
740     template['input']['payload'] = payload
741     template['input']['common-header']['request-id'] = req['input']['common-header']['request-id']
742     template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
743     template['input']['action-identifiers']['vnf-id'] = req['input']['action-identifiers']['vnf-id']
744     return template
745
746
747 @timing("> DT REQ BODY")
748 def _build_appc_lcm_lock_request_body(is_vpkg, config, req_id, action):
749     if is_vpkg:
750         demand = 'vPGN'
751     else:
752         demand = 'vFW-SINK'
753     return _build_appc_lcm_request_body(None, demand, config, req_id, action)
754
755
756 @timing("> DT REQ BODY")
757 def _build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
758     if is_vpkg:
759         demand = 'vPGN'
760     else:
761         demand = 'vFW-SINK'
762     payload = _build_appc_lcm_dt_payload(demand, config, action, traffic_presence)
763     return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
764
765
766 @timing("> UP REQ BODY")
767 def _build_appc_lcm_upgrade_request_body(config, req_id, action, old_version, new_version):
768     demand = 'vFW-SINK'
769     payload = _build_appc_lcm_upgrade_payload(demand, config, action, old_version, new_version)
770     return _build_appc_lcm_request_body(payload, demand, config, req_id, action)
771
772
773 def _build_appc_lcm_request_body(payload, demand, config, req_id, action):
774     #print(config[demand])
775     template = json.loads(open('templates/appcRestconfLcm.json').read())
776     template['input']['action'] = action
777     if payload is not None:
778         template['input']['payload'] = payload
779     else:
780         del template['input']['payload']
781     template['input']['common-header']['request-id'] = req_id
782     template['input']['common-header']['sub-request-id'] = str(uuid.uuid4())
783     template['input']['action-identifiers']['vnf-id'] = config[demand]['vnf-id']
784     return template
785
786
787 def _set_appc_lcm_timestamp(body, timestamp=None):
788     if timestamp is None:
789         t = datetime.utcnow() + timedelta(seconds=-10)
790         timestamp = t.strftime('%Y-%m-%dT%H:%M:%S.244Z')
791     body['input']['common-header']['timestamp'] = timestamp
792
793
794 @timing("Load OOF Data and Build APPC REQ")
795 def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version=None):
796     if_has = False
797
798     if if_has:
799         migrate_from = _has_request(onap_ip, aai_data, False, use_oof_cache)
800
801         if if_close_loop_vfw:
802             migrate_to = migrate_from
803         else:
804             migrate_to = _has_request(onap_ip, aai_data, True, use_oof_cache)
805
806         migrate_from = _build_config_from_has(migrate_from, onap_ip)
807         migrate_to = _build_config_from_has(migrate_to, onap_ip)
808     else:
809         migrate_from = _osdf_request(rancher_ip, onap_ip, aai_data, False, use_oof_cache)
810
811         if if_close_loop_vfw:
812             migrate_to = migrate_from
813         else:
814             migrate_to = _osdf_request(rancher_ip, onap_ip, aai_data, True, use_oof_cache)
815
816         migrate_from = _build_config_from_osdf(migrate_from, onap_ip)
817         migrate_to = _build_config_from_osdf(migrate_to, onap_ip)
818
819     #print(json.dumps(migrate_from, indent=4))
820     #print(json.dumps(migrate_to, indent=4))
821     req_id = str(uuid.uuid4())
822     result = list()
823     old_version = "2.0"
824     if_dt_only = new_version is None
825     if new_version is not None and new_version != "1.0":
826         old_version = "1.0"
827
828     requests = list()
829     include_lock = True
830
831     if include_lock:
832         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'CheckLock'), "breakOnFailure": True,
833                       "description": "Check vPGN Lock Status"})
834         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'CheckLock'), "breakOnFailure": True,
835                       "description": "Check vFW-1 Lock Status"})
836         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'CheckLock'), "breakOnFailure": True,
837                       "description": "Check vFW-2 Lock "})
838
839         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'Lock'), "breakOnFailure": True,
840                       "description": "Lock vPGN"})
841         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'Lock'), "breakOnFailure": True,
842                       "description": "Lock vFW-1"})
843         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'Lock'), "breakOnFailure": True,
844                       "description": "Lock vFW-2"})
845
846     if if_dt_only:
847         payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
848         payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
849         payload_dt_check_vfw_from = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck',
850                                                                     False)
851         payload_dt_check_vfw_to = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
852
853         requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
854         requests.append({"payload": payload_dt_check_vfw_from, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
855         requests.append({"payload": payload_dt_check_vfw_to, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
856         result.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
857                       "workflow": {"requests": requests, "description": "Migrate Traffic and Verify"}})
858     else:
859         #_build_appc_lcm_dt_request_body(is_vpkg, config, req_id, action, traffic_presence=None):
860         payload_dt_check_vpkg = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTrafficCheck', True)
861         payload_dt_vpkg_to = _build_appc_lcm_dt_request_body(True, migrate_to, req_id, 'DistributeTraffic')
862         payload_dt_vpkg_from = _build_appc_lcm_dt_request_body(True, migrate_from, req_id, 'DistributeTraffic')
863
864         payload_dt_check_vfw_from_absent = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', False)
865         payload_dt_check_vfw_to_present = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', True)
866         payload_dt_check_vfw_to_absent = _build_appc_lcm_dt_request_body(False, migrate_to, req_id, 'DistributeTrafficCheck', False)
867         payload_dt_check_vfw_from_present = _build_appc_lcm_dt_request_body(False, migrate_from, req_id, 'DistributeTrafficCheck', True)
868
869         payload_old_version_check_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePreCheck', old_version, new_version)
870         payload_new_version_check_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradePostCheck', old_version, new_version)
871         payload_upgrade_vfw_from =  _build_appc_lcm_upgrade_request_body(migrate_from, req_id, 'UpgradeSoftware', old_version, new_version)
872
873         migrate_requests = list()
874         migrate_requests.append({"payload": payload_dt_vpkg_to, "breakOnFailure": True, "description": "Migrating source vFW traffic to destination vFW"})
875         migrate_requests.append({"payload": payload_dt_check_vfw_from_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the source vFW"})
876         migrate_requests.append({"payload": payload_dt_check_vfw_to_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the destination vFW"})
877
878         requests.append({"payload": payload_dt_check_vpkg, "breakOnFailure": False, "description": "Check current traffic destination on vPGN",
879                         "workflow": {"requests": migrate_requests, "description": "Migrate Traffic and Verify"}})
880         requests.append({"payload": payload_upgrade_vfw_from, "breakOnFailure": True, "description": "Upgrading Software on source vFW"})
881         requests.append({"payload": payload_new_version_check_vfw_from, "breakOnFailure": True, "description": "Check current software version on source vFW"})
882         requests.append({"payload": payload_dt_vpkg_from, "breakOnFailure": True, "description": "Migrating destination vFW traffic to source vFW"})
883         requests.append({"payload": payload_dt_check_vfw_to_absent, "breakOnFailure": True, "description": "Checking traffic has been stopped on the destination vFW"})
884         requests.append({"payload": payload_dt_check_vfw_from_present, "breakOnFailure": True, "description": "Checking traffic has appeared on the source vFW"})
885
886         result.append({"payload": payload_old_version_check_vfw_from, "breakOnFailure": False, "description": "Check current software version on source vFW",
887                       "workflow": {"requests": requests, "description": "Migrate Traffic and Upgrade Software"}})
888
889     if include_lock:
890         result.append({"payload": _build_appc_lcm_lock_request_body(True, migrate_from, req_id, 'Unlock'), "breakOnFailure": False,
891                       "description": "Unlock vPGN"})
892         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_from, req_id, 'Unlock'), "breakOnFailure": False,
893                       "description": "Unlock vFW-1"})
894         result.append({"payload": _build_appc_lcm_lock_request_body(False, migrate_to, req_id, 'Unlock'), "breakOnFailure": False,
895                       "description": "Unlock vFW-2"})
896
897     return result
898
899
900 @timing("> Execute APPC REQ")
901 def appc_lcm_request(onap_ip, req):
902     #print(req)
903     api = _init_python_appc_lcm_api(onap_ip)
904     with _no_ssl_verification():
905     #print(json.dumps(req, indent=4))
906         if req['input']['action'] == "DistributeTraffic":
907             result = api.lcm.distribute_traffic(body=req, params={}, headers={})
908         elif req['input']['action'] == "DistributeTrafficCheck":
909             result = api.lcm.distribute_traffic_check(body=req, params={}, headers={})
910         elif req['input']['action'] == "UpgradeSoftware":
911             result = api.lcm.upgrade_software(body=req, params={}, headers={})
912         elif req['input']['action'] == "UpgradePreCheck":
913             result = api.lcm.upgrade_pre_check(body=req, params={}, headers={})
914         elif req['input']['action'] == "UpgradePostCheck":
915             result = api.lcm.upgrade_post_check(body=req, params={}, headers={})
916         elif req['input']['action'] == "CheckLock":
917             result = api.lcm.check_lock(body=req, params={}, headers={})
918         elif req['input']['action'] == "Lock":
919             result = api.lcm.lock(body=req, params={}, headers={})
920         elif req['input']['action'] == "Unlock":
921             result = api.lcm.unlock(body=req, params={}, headers={})
922         else:
923             raise Exception("{} action not supported".format(req['input']['action']))
924
925     if result.body['output']['status']['code'] == 400:
926         if req['input']['action'] == "CheckLock":
927             if result.body['output']['locked'] == "FALSE":
928                 print("UNLOCKED")
929             else:
930                 print("LOCKED")
931                 result.body['output']['status']['code'] = 401
932         else:
933             print("SUCCESSFUL")
934     elif result.body['output']['status']['code'] == 100:
935         print("ACCEPTED")
936     elif result.body['output']['status']['code'] >= 300 and result.body['output']['status']['code'] < 400:
937         print("APPC LCM <<{}>> REJECTED [{} - {}]".format(req['input']['action'], result.body['output']['status']['code'],
938                                          result.body['output']['status']['message']))
939     elif result.body['output']['status']['code'] > 400 and result.body['output']['status']['code'] < 500:
940         print("APPC LCM <<{}>> FAILED [{} - {}]".format(req['input']['action'], result.body['output']['status']['code'],
941                                          result.body['output']['status']['message']))
942 #    elif result.body['output']['status']['code'] == 311:
943 #        timestamp = result.body['output']['common-header']['timestamp']
944 #        _set_appc_lcm_timestamp(req, timestamp)
945 #        appc_lcm_request(onap_ip, req)
946 #        return
947     else:
948         raise Exception("{} - {}".format(result.body['output']['status']['code'],
949                                          result.body['output']['status']['message']))
950     #print(result)
951     return result.body['output']['status']['code']
952
953
954 def appc_lcm_status_request(onap_ip, req):
955     api = _init_python_appc_lcm_api(onap_ip)
956     status_body = _build_appc_lcm_status_body(req)
957     _set_appc_lcm_timestamp(status_body)
958     #print("CHECK STATUS")
959     with _no_ssl_verification():
960         result = api.lcm.action_status(body=status_body, params={}, headers={})
961
962     if result.body['output']['status']['code'] == 400:
963         status = json.loads(result.body['output']['payload'])
964         return status
965     else:
966         raise Exception("{} - {}".format(result.body['output']['status']['code'],
967                                          result.body['output']['status']['message']))
968
969
970 @timing("> Confirm APPC REQ")
971 def confirm_appc_lcm_action(onap_ip, req, check_appc_result):
972     print("APPC LCM << {} >> [Status]".format(req['input']['action']))
973
974     while True:
975         time.sleep(2)
976         status = appc_lcm_status_request(onap_ip, req)
977         print(status['status'])
978         if status['status'] == 'SUCCESSFUL':
979             return True
980         elif status['status'] == 'IN_PROGRESS':
981             continue
982         elif check_appc_result:
983             print("APPC LCM <<{}>> [{} - {}]".format(req['input']['action'], status['status'], status['status-reason']))
984             return False
985         else:
986             return True
987
988
989 @timing("Execute APPC LCM REQs")
990 def _execute_lcm_requests(workflow, onap_ip, check_result):
991     lcm_requests = workflow["requests"]
992     print("WORKFLOW << {} >>".format(workflow["description"]))
993     for i in range(len(lcm_requests)):
994         req = lcm_requests[i]["payload"]
995         #print(json.dumps(req, indent=4))
996         print("APPC LCM << {} >> [{}]".format(req['input']['action'], lcm_requests[i]["description"]))
997         _set_appc_lcm_timestamp(req)
998         conf_result = False
999         result = appc_lcm_request(onap_ip, req)
1000         #print("Result {}".format(result))
1001
1002         if result == 100:
1003             conf_result = confirm_appc_lcm_action(onap_ip, req, check_result)
1004             #time.sleep(30)
1005         elif result == 400:
1006             conf_result = True
1007
1008         if not conf_result:
1009             if lcm_requests[i]["breakOnFailure"]:
1010                 raise Exception("APPC LCM << {} >> FAILED".format(req['input']['action']))
1011             elif "workflow" in lcm_requests[i]:
1012                 print("WORKFLOW << {} >> SKIP".format(lcm_requests[i]["workflow"]["description"]))
1013         elif "workflow" in lcm_requests[i]:
1014             _execute_lcm_requests(lcm_requests[i]["workflow"], onap_ip, check_result)
1015
1016
1017 def _generate_cdt_artifact_request(req_id, artifact, action, vnfc_type):
1018     req = {
1019       'input': {
1020           'design-request': {
1021               'request-id': req_id,
1022               'action': "uploadArtifact",
1023               'payload': json.dumps(artifact['payload'])
1024           }
1025        }
1026     }
1027
1028     file = "{}_{}_{}.json".format(artifact['type'], action.lower(), vnfc_type)
1029     dirname = "templates/cdt-requests"
1030     #print(file)
1031     if not os.path.exists(dirname):
1032         os.makedirs(dirname)
1033     f = open("{}/{}".format(dirname, file), 'w')
1034     f.write(json.dumps(req, indent=4))
1035     f.close()
1036
1037     return req
1038
1039
1040 def _get_name_of_artifact(prefix, action, vnf_type):
1041     return "{}_{}_{}_0.0.1V.json".format(prefix, action, vnf_type)
1042
1043
1044 def _set_artifact_payload(vnf_type, vnfc_type, action, artifact):
1045     sw_upgrade = False
1046     if action == "DistributeTraffic" or action == "DistributeTrafficCheck" or action == "AllAction":
1047         pass
1048     elif action == "UpgradeSoftware" or action == "UpgradePreCheck" or action == "UpgradePostCheck":
1049         sw_upgrade = True
1050     else:
1051         raise Exception("{} action not supported".format(action))
1052
1053     artifact_contents = ''
1054     if artifact['type'] == 'config_template':
1055         file = 'templates/cdt-templates/templates/action-template.json'
1056         template_file = 'templates/cdt-templates/{}/{}'
1057         if sw_upgrade:
1058             template_file = template_file.format(vnfc_type, 'upgrade.json')
1059         else:
1060             template_file = template_file.format(vnfc_type, 'traffic.json')
1061         #print("Template for action {} in {}".format(action, template_file))
1062         #print(json.dumps(json.loads(open(template_file).read()), indent=4))
1063         artifact_contents = json.dumps(json.loads(open(template_file).read()), indent=4).replace("\n", "\r\n")
1064     elif artifact['type'] == 'parameter_definitions':
1065         file = 'templates/cdt-templates/templates/{}'
1066         if sw_upgrade:
1067             file = file.format('upgrade-params.json')
1068         else:
1069             file = file.format('traffic-params.json')
1070     elif artifact['type'] == 'param_values':
1071         file = 'templates/cdt-templates/templates/{}'
1072         if sw_upgrade:
1073             file = file.format('upgrade-params-list.json')
1074         else:
1075             file = file.format('traffic-params-list.json')
1076     elif artifact['type'] == 'reference_template':
1077         file = 'templates/cdt-templates/templates/reference-all-actions.json'
1078     else:
1079         raise Exception("{} not supported artifact type".format(artifact['type']))
1080
1081     payload = json.loads(open(file).read())
1082     payload['vnf-type'] = vnf_type
1083     payload['artifact-name'] = artifact['name']
1084     payload['action'] = action
1085
1086     if artifact['type'] == 'config_template':
1087         payload['artifact-contents'] = artifact_contents
1088     artifact['payload'] = payload
1089
1090
1091 def _generate_artifacts_for_cdt(vnf_type, vnf_type_formatted, vnfc_type, action):
1092     artifacts = []
1093     artifacts.append({
1094         'name': _get_name_of_artifact("template", action, vnf_type_formatted),
1095         'type': 'config_template',
1096         'payload': {'test': 'test'}
1097     })
1098     artifacts.append({
1099         'name': _get_name_of_artifact("pd", action, vnf_type_formatted),
1100         'type': 'parameter_definitions',
1101         'payload': {'test': 'test'}
1102     })
1103     artifacts.append({
1104         'name': _get_name_of_artifact("param", action, vnf_type_formatted),
1105         'type': 'param_values',
1106         'payload': {'test': 'test'}
1107     })
1108
1109     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[0])
1110     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[1])
1111     _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[2])
1112
1113     return artifacts
1114
1115
1116 def _generate_cdt_payloads_for_vnf(vnf_info, vnfc_type, actions):
1117     req_id = str(uuid.uuid4()).replace('-','')
1118     vnf_type_formatted = vnf_info['vnf-type'].replace(' ','').replace('/', '_')
1119     artifacts = {
1120         'AllAction': [{
1121             'name': _get_name_of_artifact("reference", 'AllAction', vnf_type_formatted),
1122             'type': 'reference_template'
1123         }]
1124     }
1125
1126     all_action_artifact = artifacts['AllAction'][0]
1127
1128     _set_artifact_payload(vnf_info['vnf-type'], vnfc_type, 'AllAction', all_action_artifact)
1129
1130     for action in actions:
1131         action_artifacts = _generate_artifacts_for_cdt(vnf_info['vnf-type'], vnf_type_formatted, vnfc_type, action)
1132         artifacts[action] = action_artifacts
1133
1134     all_action_artifacts = list()
1135
1136     for action in artifacts:
1137         artifact_list = list()
1138         action_info = {
1139             'action': action,
1140             'action-level': "vnf",
1141             'scope': {
1142                  'vnf-type': vnf_info['vnf-type'],
1143                  'vnfc-type-list': [],
1144                  'vnfc-type': ""
1145             },
1146             'artifact-list': artifact_list
1147         }
1148
1149         if action != 'AllAction':
1150             action_info.update({
1151                 'template': "Y",
1152                 'vm': [],
1153                 'device-protocol': "ANSIBLE",
1154                 'user-name': "admin",
1155                 'port-number': "8000",
1156                 'scopeType': "vnf-type"
1157             })
1158
1159         for action_artifact in artifacts[action]:
1160             artifact_list.append({'artifact-name': action_artifact['name'], 'artifact-type': action_artifact['type']})
1161             if action != 'AllAction':
1162                 req = _generate_cdt_artifact_request(req_id, action_artifact, action, vnfc_type) # pylint: disable=W0612
1163                 #print(json.dumps(req, indent=4))
1164
1165         #print(json.dumps(action_info, indent=4))
1166         all_action_artifacts.append(action_info)
1167
1168     all_action_artifact['payload']['artifact-contents'] = json.dumps({'reference_data': all_action_artifacts})
1169     req = _generate_cdt_artifact_request(req_id, all_action_artifact, 'AllAction', vnfc_type)
1170     #print(json.dumps(req, indent=4))
1171
1172
1173 def _generate_cdt_payloads(aai_data):
1174     vfw_actions = ["DistributeTrafficCheck", "UpgradeSoftware", "UpgradePreCheck", "UpgradePostCheck", "UpgradeSoftware"]
1175     vpgn_actions = ["DistributeTraffic", "DistributeTrafficCheck"]
1176     _generate_cdt_payloads_for_vnf(aai_data["vfw-model-info"], "vfw-sink", vfw_actions)
1177     _generate_cdt_payloads_for_vnf(aai_data["vpgn-model-info"], "vpgn", vpgn_actions)
1178
1179
1180 def execute_workflow(vfw_vnf_id, rancher_ip, onap_ip, use_oof_cache, if_close_loop_vfw, info_only, check_result, new_version=None):
1181     print("\nExecuting workflow for VNF ID '{}' on Rancher with IP {} and ONAP with IP {}".format(
1182         vfw_vnf_id, rancher_ip, onap_ip))
1183     print("\nOOF Cache {}, is CL vFW {}, only info {}, check LCM result {}".format(use_oof_cache, if_close_loop_vfw,
1184                                                                                    info_only, check_result))
1185     if new_version is not None:
1186         print("\nNew vFW software version {}\n".format(new_version))
1187
1188     x = threading.Thread(target=_run_osdf_resp_server, daemon=True)
1189     x.start()
1190     aai_data = load_aai_data(vfw_vnf_id, onap_ip)
1191     print("\nvFWDT Service Information:")
1192     print(json.dumps(aai_data, indent=4))
1193     lcm_requests = build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version)
1194     print("\nAnsible Inventory:")
1195     inventory = "[host]\nlocalhost   ansible_connection=local\n"
1196     for key in ansible_inventory:
1197         inventory += str("[{}]\n").format(key)
1198         for host in ansible_inventory[key]:
1199             inventory += str("{}\n").format(ansible_inventory[key][host])
1200
1201     print(inventory)
1202     f = open("Ansible_inventory", 'w+')
1203     f.write(inventory)
1204     f.close()
1205
1206     _generate_cdt_payloads(aai_data)
1207
1208     if info_only:
1209         return
1210     print("\nDistribute Traffic Workflow Execution:")
1211
1212     _execute_lcm_requests({"requests": lcm_requests, "description": "Migrate vFW Traffic Conditionally"}, onap_ip, check_result)
1213
1214
1215
1216 help = """\npython3 workflow.py <VNF-ID> <RANCHER-NODE-IP> <K8S-NODE-IP> <IF-CACHE> <IF-VFWCL> <INITIAL-ONLY> <CHECK-STATUS> <VERSION>
1217 \n<VNF-ID> - vnf-id of vFW VNF instance that traffic should be migrated out from
1218 <RANCHER-NODE-IP> - External IP of ONAP Rancher Node i.e. 10.12.5.160 (If Rancher Node is missing this is NFS node)
1219 <K8S-NODE-IP> - External IP of ONAP K8s Worker Node i.e. 10.12.5.212
1220 <IF-CACHE> - If script should use and build OOF response cache (cache it speed-ups further executions of script)
1221 <IF-VFWCL> - If instead of vFWDT service instance vFW or vFWCL one is used (should be False always)
1222 <INITIAL-ONLY> - If only configuration information will be collected (True for initial phase and False for full execution of workflow)
1223 <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)
1224 <VERSION> - New version of vFW - for tests '1.0' or '2.0'. Ommit when traffic distribution only\n"""
1225
1226 for key in sys.argv:
1227     if key == "-h" or key == "--help":
1228         print(help)
1229         sys.exit()
1230
1231 new_version = None
1232 if len(sys.argv) > 8:
1233     new_version = sys.argv[8]
1234
1235 try:
1236     #vnf_id, Rancher node IP, K8s node IP, use OOF cache, if close loop vfw, if info_only, if check APPC result
1237     execute_workflow(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4].lower() == 'true', sys.argv[5].lower() == 'true',
1238                      sys.argv[6].lower() == 'true', sys.argv[7].lower() == 'true', new_version)
1239 finally:
1240     stats.close()