Fix for missing VNFC rel information
[demo.git] / tutorials / vFWDT / workflow / workflow.py
index 1ddcde5..6621831 100755 (executable)
 '''
 
 import os
-import ssl
 import json
 import sys
 import uuid
 import time
 import copy
-import netifaces as ni
 import warnings
 import contextlib
 import requests
 import simplejson
 import http.server
 import threading
-import urllib3
 from datetime import datetime
 from datetime import timedelta
 from simple_rest_client.api import API
 from simple_rest_client.resource import Resource
 from basicauth import encode
-from pprint import pprint
-from random import randint
 from urllib3.exceptions import InsecureRequestWarning
 
 
@@ -164,12 +159,15 @@ def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
         m_value = None
     rel_dict = data.get(rel_lst)
     if rel_dict:  # check if data has relationship lists
-        for key, rel_list in rel_dict.items():
+        for key, rel_list in rel_dict.items(): # pylint: disable=W0612
             for rel in rel_list:
                 if rel.get("related-to") == related_to:
                     dval = None
                     matched = False
                     link = rel.get("related-link")
+                    property = ""
+                    if rel.get("related-to-property") is not None:
+                        property = rel.get("related-to-property")[0]['property-value']
                     r_data = rel.get(rdata, [])
                     if search_key:
                         for rd in r_data:
@@ -177,7 +175,7 @@ def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
                                 dval = rd.get(rval)
                                 if not match_dict:  # return first match
                                     response.append(
-                                        {"link": link, "d_value": dval}
+                                        {"link": link, "property": property, "d_value": dval}
                                     )
                                     break  # go to next relation
                             if rd.get(rkey) == m_key \
@@ -185,17 +183,17 @@ def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
                                 matched = True
                         if match_dict and matched:  # if matching required
                             response.append(
-                                {"link": link, "d_value": dval}
+                                {"link": link, "property": property, "d_value": dval}
                             )
                             # matched, return search value corresponding
                             # to the matched r_data group
                     else:  # no search key; just return the link
                         response.append(
-                            {"link": link, "d_value": dval}
+                            {"link": link, "property": property, "d_value": dval}
                         )
-    if len(response) == 0:
+    if response:
         response.append(
-            {"link": None, "d_value": None}
+            {"link": None, "property": None, "d_value": None}
         )
     return response
 
@@ -203,7 +201,10 @@ def _get_aai_rel_link_data(data, related_to, search_key=None, match_dict=None):
 class AAIApiResource(Resource):
     actions = {
         'generic_vnf': {'method': 'GET', 'url': 'network/generic-vnfs/generic-vnf/{}'},
+        'vf_module': {'method': 'GET', 'url': 'network/generic-vnfs/generic-vnf/{}/vf-modules/vf-module/{}'},
         'vnfc': {'method': 'GET', 'url': 'network/vnfcs/vnfc/{}'},
+        'vnfc_put': {'method': 'PUT', 'url': 'network/vnfcs/vnfc/{}'},
+        'vnfc_patch': {'method': 'PATCH', 'url': 'network/vnfcs/vnfc/{}'},
         'link': {'method': 'GET', 'url': '{}'},
         'service_instance': {'method': 'GET',
                              'url': 'business/customers/customer/{}/service-subscriptions/service-subscription/{}/service-instances/service-instance/{}'}
@@ -237,7 +238,7 @@ class APPCLcmApiResource(Resource):
     }
 
 
-def _init_python_aai_api(onap_ip):
+def _init_python_aai_api(onap_ip, content_type='application/json'):
     api = API(
         api_root_url="https://{}:30233/aai/v14/".format(onap_ip),
         params={},
@@ -245,7 +246,7 @@ def _init_python_aai_api(onap_ip):
             'Authorization': encode("AAI", "AAI"),
             'X-FromAppId': 'SCRIPT',
             'Accept': 'application/json',
-            'Content-Type': 'application/json',
+            'Content-Type': content_type,
             'X-TransactionId': str(uuid.uuid4()),
         },
         timeout=30,
@@ -403,7 +404,7 @@ def _osdf_request(rancher_ip, onap_ip, aai_data, exclude, use_oof_cache):
     #print(json.dumps(template, indent=4))
 
     with _no_ssl_verification():
-        response = api.osdf.placement(body=template, params={}, headers={})
+        response = api.osdf.placement(body=template, params={}, headers={}) # pylint: disable=W0612
         #if response.body.get('error_message') is not None:
         #    raise Exception(response.body['error_message']['explanation'])
 
@@ -454,8 +455,8 @@ def _has_request(onap_ip, aai_data, exclude, use_oof_cache):
     node['chosen_customer_id'] = aai_data['service-info']['global-customer-id']
     node['service_id'] = aai_data['service-info']['service-instance-id']
     node = template['template']['demands']['vFW-SINK'][0]
-    node['attributes']['model-invariant-id'] = aai_data['vfw-model-info']['model-invariant-id']
-    node['attributes']['model-version-id'] = aai_data['vfw-model-info']['model-version-id']
+    node['filtering_attributes']['model-invariant-id'] = aai_data['vfw-model-info']['model-invariant-id']
+    node['filtering_attributes']['model-version-id'] = aai_data['vfw-model-info']['model-version-id']
     if exclude:
         node['excluded_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
         del node['required_candidates']
@@ -463,8 +464,8 @@ def _has_request(onap_ip, aai_data, exclude, use_oof_cache):
         node['required_candidates'][0]['candidate_id'][0] = aai_data['vf-module-id']
         del node['excluded_candidates']
     node = template['template']['demands']['vPGN'][0]
-    node['attributes']['model-invariant-id'] = aai_data['vpgn-model-info']['model-invariant-id']
-    node['attributes']['model-version-id'] = aai_data['vpgn-model-info']['model-version-id']
+    node['filtering_attributes']['model-invariant-id'] = aai_data['vpgn-model-info']['model-invariant-id']
+    node['filtering_attributes']['model-version-id'] = aai_data['vpgn-model-info']['model-version-id']
 
     #print(json.dumps(template, indent=4))
 
@@ -510,7 +511,7 @@ def _extract_has_appc_identifiers(has_result, demand, onap_ip):
         v_server['vserver-name'] = v_server['vserver-name'].replace("01", "02")
     hostname_cache.append(v_server['vserver-name'])
 
-    api = _init_python_aai_api(onap_ip)
+    api = _init_python_aai_api(onap_ip) # pylint: disable=W0612
     vnfc_type = demand.lower()
 #    with _no_ssl_verification():
 #        response = api.aai.vnfc(v_server['vserver-name'], body=None, params={}, headers={})
@@ -529,9 +530,89 @@ def _extract_has_appc_identifiers(has_result, demand, onap_ip):
     if demand.lower() not in ansible_inventory:
         ansible_inventory[demand.lower()] = {}
     ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
+
+    _verify_vnfc_data(api, onap_ip, config)
     return config
 
 
+def _verify_vnfc_data(aai_api, onap_ip, config, root=None):
+    vnfc_name = config['vserver-name']
+    oam_ip = config['ip']
+    with _no_ssl_verification():
+        response = aai_api.aai.vnfc(vnfc_name, body=None, params=None, headers={})
+    #print(json.dumps(response.body))
+    if "ipaddress-v4-oam-vip" not in response.body and oam_ip != "":
+        print("VNFC IP information update for {}".format(vnfc_name))
+        api = _init_python_aai_api(onap_ip, 'application/merge-patch+json')
+        with _no_ssl_verification():
+            response = api.aai.vnfc_patch(vnfc_name, body={"ipaddress-v4-oam-vip": oam_ip, "vnfc-name": vnfc_name}, params=None, headers={})
+    if "relationship-list" not in response.body:
+        print("VNFC REL information update for {}".format(vnfc_name))
+        vserver_info = {
+            "link": "",
+            "owner": "",
+            "region": "",
+            "tenant": "",
+            "id": ""
+        }
+        with _no_ssl_verification():
+            vf_module = aai_api.aai.vf_module(config['vnf-id'], config['vf-module-id'], body=None, params={'depth': 2}, headers={}).body
+        related_to = "vserver"
+        search_key = "cloud-region.cloud-owner"
+        rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
+        vserver_info["owner"] = rl_data_list[0]['d_value']
+
+        search_key = "cloud-region.cloud-region-id"
+        rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
+        vserver_info["region"] = rl_data_list[0]['d_value']
+
+        search_key = "tenant.tenant-id"
+        rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
+        vserver_info["tenant"] = rl_data_list[0]['d_value']
+
+        search_key = "vserver.vserver-id"
+        rl_data_list = _get_aai_rel_link_data(data=vf_module, related_to=related_to, search_key=search_key)
+        for relation in rl_data_list:
+            vserver_info["id"] = relation['d_value']
+            vserver_info["link"] = relation['link']
+
+            rel_data = {
+                "related-to": "vserver",
+                "related-link": vserver_info["link"],
+                "relationship-data": [
+                    {
+                        "relationship-key": "cloud-region.cloud-owner",
+                        "relationship-value": vserver_info["owner"]
+                    },
+                    {
+                        "relationship-key": "cloud-region.cloud-region-id",
+                        "relationship-value": vserver_info["region"]
+                    },
+                    {
+                        "relationship-key": "tenant.tenant-id",
+                        "relationship-value": vserver_info["tenant"]
+                    },
+                    {
+                        "relationship-key": "vserver.vserver-id",
+                        "relationship-value": vserver_info["id"]
+                    }
+                ]
+            }
+            #print(json.dumps(rel_data, indent=4))
+            if config['vserver-id'] == relation['d_value']:
+                with _no_ssl_verification():
+                    response = aai_api.aai.vnfc_put("{}/relationship-list/relationship".format(vnfc_name), body=rel_data, params=None, headers={})
+            elif root is None and relation['d_value'] is not None:
+                new_config = copy.deepcopy(config)
+                new_config['vserver-name'] = relation['property']
+                new_config['vserver-id'] = relation['d_value']
+                new_config['ip'] = ""
+                _verify_vnfc_data(aai_api, onap_ip, new_config, vnfc_name)
+    with _no_ssl_verification():
+        response = aai_api.aai.vnfc(vnfc_name, body=None, params=None, headers={})
+        #print(json.dumps(response.body))
+
+
 def _extract_osdf_appc_identifiers(has_result, demand, onap_ip):
     if demand == 'vPGN':
         v_server = has_result[demand]['vservers'][0]
@@ -568,6 +649,9 @@ def _extract_osdf_appc_identifiers(has_result, demand, onap_ip):
     if demand.lower() not in ansible_inventory:
         ansible_inventory[demand.lower()] = {}
     ansible_inventory[demand.lower()][config['vserver-name']] = ansible_inventory_entry
+
+    _verify_vnfc_data(api, onap_ip, config)
+
     return config
 
 
@@ -782,7 +866,7 @@ def _set_appc_lcm_timestamp(body, timestamp=None):
 
 @timing("Load OOF Data and Build APPC REQ")
 def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache, if_close_loop_vfw, new_version=None):
-    if_has = True
+    if_has = False
 
     if if_has:
         migrate_from = _has_request(onap_ip, aai_data, False, use_oof_cache)
@@ -809,10 +893,10 @@ def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache,
     #print(json.dumps(migrate_to, indent=4))
     req_id = str(uuid.uuid4())
     result = list()
-    old_version = 2.0
+    old_version = "2.0"
     if_dt_only = new_version is None
     if new_version is not None and new_version != "1.0":
-        old_version = 1.0
+        old_version = "1.0"
 
     requests = list()
     include_lock = True
@@ -888,7 +972,7 @@ def build_appc_lcms_requests_body(rancher_ip, onap_ip, aai_data, use_oof_cache,
 
 @timing("> Execute APPC REQ")
 def appc_lcm_request(onap_ip, req):
-    print(req)
+    #print(req)
     api = _init_python_appc_lcm_api(onap_ip)
     with _no_ssl_verification():
     #print(json.dumps(req, indent=4))
@@ -944,7 +1028,7 @@ def appc_lcm_status_request(onap_ip, req):
     api = _init_python_appc_lcm_api(onap_ip)
     status_body = _build_appc_lcm_status_body(req)
     _set_appc_lcm_timestamp(status_body)
-    print("CHECK STATUS")
+    #print("CHECK STATUS")
     with _no_ssl_verification():
         result = api.lcm.action_status(body=status_body, params={}, headers={})
 
@@ -980,28 +1064,190 @@ def _execute_lcm_requests(workflow, onap_ip, check_result):
     lcm_requests = workflow["requests"]
     print("WORKFLOW << {} >>".format(workflow["description"]))
     for i in range(len(lcm_requests)):
-       req = lcm_requests[i]["payload"]
-       #print(json.dumps(req, indent=4))
-       print("APPC LCM << {} >> [{}]".format(req['input']['action'], lcm_requests[i]["description"]))
-       _set_appc_lcm_timestamp(req)
-       conf_result = False
-       result = appc_lcm_request(onap_ip, req)
-       print("Result {}".format(result))
-
-       if result == 100:
-           conf_result = confirm_appc_lcm_action(onap_ip, req, check_result)
-           #time.sleep(30)
-       elif result == 400:
-           conf_result = True
-
-       if not conf_result:
-           if lcm_requests[i]["breakOnFailure"]:
-               raise Exception("APPC LCM << {} >> FAILED".format(req['input']['action']))
-           elif "workflow" in lcm_requests[i]:
-               print("WORKFLOW << {} >> SKIP".format(lcm_requests[i]["workflow"]["description"]))
-       elif "workflow" in lcm_requests[i]:
-           _execute_lcm_requests(lcm_requests[i]["workflow"], onap_ip, check_result)
+        req = lcm_requests[i]["payload"]
+        #print(json.dumps(req, indent=4))
+        print("APPC LCM << {} >> [{}]".format(req['input']['action'], lcm_requests[i]["description"]))
+        _set_appc_lcm_timestamp(req)
+        conf_result = False
+        result = appc_lcm_request(onap_ip, req)
+        #print("Result {}".format(result))
+
+        if result == 100:
+            conf_result = confirm_appc_lcm_action(onap_ip, req, check_result)
+            #time.sleep(30)
+        elif result == 400:
+            conf_result = True
+
+        if not conf_result:
+            if lcm_requests[i]["breakOnFailure"]:
+                raise Exception("APPC LCM << {} >> FAILED".format(req['input']['action']))
+            elif "workflow" in lcm_requests[i]:
+                print("WORKFLOW << {} >> SKIP".format(lcm_requests[i]["workflow"]["description"]))
+        elif "workflow" in lcm_requests[i]:
+            _execute_lcm_requests(lcm_requests[i]["workflow"], onap_ip, check_result)
+
+
+def _generate_cdt_artifact_request(req_id, artifact, action, vnfc_type):
+    req = {
+      'input': {
+          'design-request': {
+              'request-id': req_id,
+              'action': "uploadArtifact",
+              'payload': json.dumps(artifact['payload'])
+          }
+       }
+    }
 
+    file = "{}_{}_{}.json".format(artifact['type'], action.lower(), vnfc_type)
+    dirname = "templates/cdt-requests"
+    #print(file)
+    if not os.path.exists(dirname):
+        os.makedirs(dirname)
+    f = open("{}/{}".format(dirname, file), 'w')
+    f.write(json.dumps(req, indent=4))
+    f.close()
+
+    return req
+
+
+def _get_name_of_artifact(prefix, action, vnf_type):
+    return "{}_{}_{}_0.0.1V.json".format(prefix, action, vnf_type)
+
+
+def _set_artifact_payload(vnf_type, vnfc_type, action, artifact):
+    sw_upgrade = False
+    if action == "DistributeTraffic" or action == "DistributeTrafficCheck" or action == "AllAction":
+        pass
+    elif action == "UpgradeSoftware" or action == "UpgradePreCheck" or action == "UpgradePostCheck":
+        sw_upgrade = True
+    else:
+        raise Exception("{} action not supported".format(action))
+
+    artifact_contents = ''
+    if artifact['type'] == 'config_template':
+        file = 'templates/cdt-templates/templates/action-template.json'
+        template_file = 'templates/cdt-templates/{}/{}'
+        if sw_upgrade:
+            template_file = template_file.format(vnfc_type, 'upgrade.json')
+        else:
+            template_file = template_file.format(vnfc_type, 'traffic.json')
+        #print("Template for action {} in {}".format(action, template_file))
+        #print(json.dumps(json.loads(open(template_file).read()), indent=4))
+        artifact_contents = json.dumps(json.loads(open(template_file).read()), indent=4).replace("\n", "\r\n")
+    elif artifact['type'] == 'parameter_definitions':
+        file = 'templates/cdt-templates/templates/{}'
+        if sw_upgrade:
+            file = file.format('upgrade-params.json')
+        else:
+            file = file.format('traffic-params.json')
+    elif artifact['type'] == 'param_values':
+        file = 'templates/cdt-templates/templates/{}'
+        if sw_upgrade:
+            file = file.format('upgrade-params-list.json')
+        else:
+            file = file.format('traffic-params-list.json')
+    elif artifact['type'] == 'reference_template':
+        file = 'templates/cdt-templates/templates/reference-all-actions.json'
+    else:
+        raise Exception("{} not supported artifact type".format(artifact['type']))
+
+    payload = json.loads(open(file).read())
+    payload['vnf-type'] = vnf_type
+    payload['artifact-name'] = artifact['name']
+    payload['action'] = action
+
+    if artifact['type'] == 'config_template':
+        payload['artifact-contents'] = artifact_contents
+    artifact['payload'] = payload
+
+
+def _generate_artifacts_for_cdt(vnf_type, vnf_type_formatted, vnfc_type, action):
+    artifacts = []
+    artifacts.append({
+        'name': _get_name_of_artifact("template", action, vnf_type_formatted),
+        'type': 'config_template',
+        'payload': {'test': 'test'}
+    })
+    artifacts.append({
+        'name': _get_name_of_artifact("pd", action, vnf_type_formatted),
+        'type': 'parameter_definitions',
+        'payload': {'test': 'test'}
+    })
+    artifacts.append({
+        'name': _get_name_of_artifact("param", action, vnf_type_formatted),
+        'type': 'param_values',
+        'payload': {'test': 'test'}
+    })
+
+    _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[0])
+    _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[1])
+    _set_artifact_payload(vnf_type, vnfc_type, action, artifacts[2])
+
+    return artifacts
+
+
+def _generate_cdt_payloads_for_vnf(vnf_info, vnfc_type, actions):
+    req_id = str(uuid.uuid4()).replace('-','')
+    vnf_type_formatted = vnf_info['vnf-type'].replace(' ','').replace('/', '_')
+    artifacts = {
+        'AllAction': [{
+            'name': _get_name_of_artifact("reference", 'AllAction', vnf_type_formatted),
+            'type': 'reference_template'
+        }]
+    }
+
+    all_action_artifact = artifacts['AllAction'][0]
+
+    _set_artifact_payload(vnf_info['vnf-type'], vnfc_type, 'AllAction', all_action_artifact)
+
+    for action in actions:
+        action_artifacts = _generate_artifacts_for_cdt(vnf_info['vnf-type'], vnf_type_formatted, vnfc_type, action)
+        artifacts[action] = action_artifacts
+
+    all_action_artifacts = list()
+
+    for action in artifacts:
+        artifact_list = list()
+        action_info = {
+            'action': action,
+            'action-level': "vnf",
+            'scope': {
+                 'vnf-type': vnf_info['vnf-type'],
+                 'vnfc-type-list': [],
+                 'vnfc-type': ""
+            },
+            'artifact-list': artifact_list
+        }
+
+        if action != 'AllAction':
+            action_info.update({
+                'template': "Y",
+                'vm': [],
+                'device-protocol': "ANSIBLE",
+                'user-name': "admin",
+                'port-number': "8000",
+                'scopeType': "vnf-type"
+            })
+
+        for action_artifact in artifacts[action]:
+            artifact_list.append({'artifact-name': action_artifact['name'], 'artifact-type': action_artifact['type']})
+            if action != 'AllAction':
+                req = _generate_cdt_artifact_request(req_id, action_artifact, action, vnfc_type) # pylint: disable=W0612
+                #print(json.dumps(req, indent=4))
+
+        #print(json.dumps(action_info, indent=4))
+        all_action_artifacts.append(action_info)
+
+    all_action_artifact['payload']['artifact-contents'] = json.dumps({'reference_data': all_action_artifacts})
+    req = _generate_cdt_artifact_request(req_id, all_action_artifact, 'AllAction', vnfc_type)
+    #print(json.dumps(req, indent=4))
+
+
+def _generate_cdt_payloads(aai_data):
+    vfw_actions = ["DistributeTrafficCheck", "UpgradeSoftware", "UpgradePreCheck", "UpgradePostCheck", "UpgradeSoftware"]
+    vpgn_actions = ["DistributeTraffic", "DistributeTrafficCheck"]
+    _generate_cdt_payloads_for_vnf(aai_data["vfw-model-info"], "vfw-sink", vfw_actions)
+    _generate_cdt_payloads_for_vnf(aai_data["vpgn-model-info"], "vpgn", vpgn_actions)
 
 
 def execute_workflow(vfw_vnf_id, rancher_ip, onap_ip, use_oof_cache, if_close_loop_vfw, info_only, check_result, new_version=None):
@@ -1030,6 +1276,8 @@ def execute_workflow(vfw_vnf_id, rancher_ip, onap_ip, use_oof_cache, if_close_lo
     f.write(inventory)
     f.close()
 
+    _generate_cdt_payloads(aai_data)
+
     if info_only:
         return
     print("\nDistribute Traffic Workflow Execution:")