def readNsd(self):
        """
        Reads and returns required information from nsd/vnfd
        """
        url = f"https://{self.ip}:9999/osm/nsd/v1/ns_descriptors"
        while True:
            headers = {
                'Content-Type': 'application/yaml',
                'Accept': 'application/json',
                'Authorization': f'Bearer {self.token}',
            }
            response = requests.get(url, headers=headers, verify=False)
            if (response.status_code != 401):
                osm_nsd_list = response.json()
                new_nsd = {}
                for osm_nsd in osm_nsd_list:
                    new_nsd["nsd-id"] = osm_nsd["_id"]
                    new_nsd["nsd-name"] = osm_nsd["id"]
                    new_nsd["vnfd_list"] = []
                    new_nsd["flavor"] = {
                        "memory-mb": 0,
                        "vcpu-count": 0,
                        "storage-gb": 0
                    }
                    for osm_vnfd in osm_nsd['constituent-vnfd']:
                        data = {"name": osm_vnfd["vnfd-id-ref"]}
                        reg_vnfd = mongoUtils.find("vnfd", data)
                        if not reg_vnfd:
                            logger.warning("There is a vnfd missing from the \
NFVO repository")
                        else:
                            new_nsd["vnfd_list"].append(reg_vnfd["name"])
                            new_nsd["flavor"]["memory-mb"] +=\
                                reg_vnfd["flavor"]["memory-mb"]
                            new_nsd["flavor"]["vcpu-count"] +=\
                                reg_vnfd["flavor"]["vcpu-count"]
                            new_nsd["flavor"]["storage-gb"] +=\
                                reg_vnfd["flavor"]["storage-gb"]
                    new_nsd["nfvo_id"] = self.nfvo_id
                    new_nsd["_id"] = str(uuid.uuid4())
                    try:
                        mongoUtils.add("nsd", new_nsd)
                    except pymongo.errors.DuplicateKeyError:
                        continue
                    new_nsd = {}
                break
            else:
                self.getToken()
Exemple #2
0
 def get(self, uuid):
     """
     Returns the available resources on platform,
     used by: `katana resource location <location>`
     """
     location_id = uuid.lower()
     # Check if the location exists
     if not mongoUtils.find("location", {"id": location_id}):
         return f"Location {uuid} not found", 404
     # Get VIMs
     filter_data = {"location": location_id}
     vims = get_vims(filter_data)
     # Get Functions
     functions = get_func(filter_data)
     resources = {"VIMs": vims, "Functions": functions}
     return dumps(resources), 200
Exemple #3
0
 def delete(self, uuid):
     """
     Delete a specific vim.
     used by: `katana vim rm [uuid]`
     """
     vim = mongoUtils.get("vim", uuid)
     if vim:
         if vim["tenants"]:
             return "Cannot delete vim {} - In use".format(uuid), 400
         mongoUtils.delete("vim_obj", uuid)
         mongoUtils.delete("vim", uuid)
         # Update the location removing the VIM
         location = mongoUtils.find("location", {"id": vim["location"].lower()})
         if location:
             location["vims"].remove(vim["id"])
             mongoUtils.update("location", location["_id"], location)
         return "Deleted VIM {}".format(uuid), 200
     else:
         # if uuid is not found, return error
         return "Error: No such vim: {}".format(uuid), 404
Exemple #4
0
def delete_slice(slice_id, force=False):
    """
    Deletes the given network slice
    """

    # Update the slice status in mongo db
    slice_json = mongoUtils.get("slice", slice_id)
    slice_json["status"] = "Terminating"
    mongoUtils.update("slice", slice_json["_id"], slice_json)
    logger.info(f"{slice_json['_id']} Status: Terminating")

    # Check if slice monitoring has been enabled
    monitoring = os.getenv("KATANA_MONITORING", None)
    slice_monitoring = slice_json.get("slice_monitoring", None)
    mon_producer = None

    if monitoring:
        # Create the Kafka producer
        mon_producer = create_producer()
        mon_producer.send(
            "nfv_mon",
            value={
                "action": "katana_mon",
                "slice_info": {
                    "slice_id": slice_id,
                    "status": "terminating"
                },
            },
        )

    # *** Step-1: Radio Slice Configuration ***
    if slice_json["conf_comp"]["ems"]:
        ems_messages = slice_json.get("ems_data", None)
        if ems_messages:
            for ems_id, ems_message in ems_messages.items():
                # Find the EMS
                target_ems = mongoUtils.find("ems", {"id": ems_id})
                if not target_ems or ems_id not in slice_json["conf_comp"][
                        "ems"]:
                    # Error handling: There is no such EMS
                    logger.error(
                        "EMS {} not found - No configuration".format(ems_id))
                    continue
                target_ems_obj = pickle.loads(
                    mongoUtils.find("ems_obj", {"id": ems_id})["obj"])
                target_ems_obj.del_slice(ems_message)
    else:
        logger.info("There was not EMS configuration")

    # *** Step-2: WAN Slice ***
    wim_data = slice_json.get("wim_data", None)
    if wim_data:
        # Select WIM - Assume that there is only one registered
        wim_list = list(mongoUtils.index("wim"))
        if wim_list:
            target_wim = wim_list[0]
            target_wim_id = target_wim["id"]
            target_wim_obj = pickle.loads(
                mongoUtils.find("wim_obj", {"id": target_wim_id})["obj"])
            target_wim_obj.del_slice(slice_id)
            try:
                del target_wim["slices"][slice_json["_id"]]
            except KeyError:
                logger.warning(f"Slice {slice_id} not in WIM {target_wim_id}")
            else:
                mongoUtils.update("wim", target_wim["_id"], target_wim)
        else:
            err = "Cannot find WIM - WAN Slice will not be deleted"
            logger.warning(err)
            slice_json["status"] = "Error"
            if monitoring:
                mon_producer.send(
                    "nfv_mon",
                    value={
                        "action": "katana_mon",
                        "slice_info": {
                            "slice_id": slice_id,
                            "status": "error"
                        },
                    },
                )
            slice_json["error"] = slice_json.get("error", "") + err
            mongoUtils.update("slice", slice_json["_id"], slice_json)
    else:
        logger.info("There was no WIM configuration")

    # *** Step-3: Cloud ***
    vim_error_list = []
    try:
        total_ns_list = slice_json["total_ns_list"]
        ns_inst_info = slice_json["ns_inst_info"]
        for ns in total_ns_list:
            # Check if the NS is shared. If it is, check if there is another running slice on this
            # sharing list
            if ns["shared_function"]:
                # Find the shared list
                shared_list = mongoUtils.get("sharing_lists",
                                             ns["shared_slice_key"])
                # If there is another running slice, don't terminate the NS
                if len(shared_list["nest_list"]) > 1:
                    continue

            if ns["nsd-id"] not in slice_json["conf_comp"]["nf"]:
                logger.error(
                    f"{ns['nsd-id']} was not instantiated successfully")
                continue

            # Get the NFVO
            nfvo_id = ns["nfvo-id"]
            target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
            if not target_nfvo:
                logger.warning(
                    "NFVO with id {} was not found - NSs won't terminate".
                    format(nfvo_id))
                vim_error_list += ns["vims"]
                continue

            target_nfvo_obj = pickle.loads(
                mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"])
            # Stop the NS
            nfvo_inst_ns = ns_inst_info[ns["ns-id"]][
                ns["placement_loc"]["location"]]["nfvo_inst_ns"]
            target_nfvo_obj.deleteNs(nfvo_inst_ns)
            while True:
                if target_nfvo_obj.checkNsLife(nfvo_inst_ns):
                    break
                time.sleep(5)
    except KeyError as e:
        err = f"Error, not all NSs started or terminated correctly {e}"
        logger.warning(err)
        slice_json["status"] = "Error"
        if monitoring:
            mon_producer.send(
                "nfv_mon",
                value={
                    "action": "katana_mon",
                    "slice_info": {
                        "slice_id": slice_id,
                        "status": "error"
                    },
                },
            )
        slice_json["error"] = slice_json.get("error", "") + err
        mongoUtils.update("slice", slice_json["_id"], slice_json)

    vim_dict = slice_json.get("vim_list", {})
    for vim, vim_info in vim_dict.items():
        # Check if the VIM is shared with other slices. If yes, skip the deletion
        tenant_name = slice_id
        if vim_info["shared"]:
            shared_list = mongoUtils.get("sharing_lists",
                                         vim_info["shared_slice_list_key"])
            tenant_name = vim_info["shared_slice_list_key"]
            vim_id = vim[:-2]
            # If there is another running slice, don't delete the VIM account
            if len(shared_list["nest_list"]) > 1:
                continue
        else:
            vim_id = vim
        try:
            # Delete the new tenants from the NFVO
            for nfvo, vim_account in vim_info["nfvo_vim_account"].items():
                # Get the NFVO
                target_nfvo = mongoUtils.find("nfvo", {"id": nfvo})
                target_nfvo_obj = pickle.loads(
                    mongoUtils.find("nfvo_obj", {"id": nfvo})["obj"])
                # Delete the VIM and update nfvo db
                target_nfvo_obj.deleteVim(vim_account)
                target_nfvo["tenants"][tenant_name].remove(vim_account)
                if len(target_nfvo["tenants"][tenant_name]) == 0:
                    try:
                        del target_nfvo["tenants"][tenant_name]
                    except KeyError:
                        logger.warning(
                            f"Slice {tenant_name} not in NFO {nfvo}")
                    else:
                        mongoUtils.update("nfvo", target_nfvo["_id"],
                                          target_nfvo)
            # Delete the tenants from every vim
            if vim not in vim_error_list:
                # Get the VIM
                target_vim = mongoUtils.find("vim", {"id": vim_id})
                if not target_vim:
                    logger.warning(
                        "VIM id {} was not found - Tenant won't be deleted".
                        format(vim))
                    continue
                target_vim_obj = pickle.loads(
                    mongoUtils.find("vim_obj", {"id": vim_id})["obj"])
                if vim_info["shared"]:
                    tenant_name = vim_info["shared_slice_list_key"]
                else:
                    tenant_name = slice_json["_id"]
                target_vim_obj.delete_proj_user(
                    target_vim["tenants"][tenant_name])
                try:
                    del target_vim["tenants"][tenant_name]
                except KeyError:
                    logger.warning(f"Slice {slice_id} not in VIM {vim}")
                else:
                    mongoUtils.update("vim", target_vim["_id"], target_vim)
        except KeyError as e:
            err = f"Error, not all tenants created or removed correctly {e}"
            logger.warning(err)
            slice_json["status"] = "Error"
            if monitoring:
                mon_producer.send(
                    "nfv_mon",
                    value={
                        "action": "katana_mon",
                        "slice_info": {
                            "slice_id": slice_id,
                            "status": "error"
                        },
                    },
                )
            slice_json["error"] = slice_json.get("error", "") + err
            mongoUtils.update("slice", slice_json["_id"], slice_json)

    if "error" not in slice_json:
        mongoUtils.delete("slice", slice_json["_id"])
        if monitoring:
            mon_producer.send(
                "nfv_mon",
                value={
                    "action": "katana_mon",
                    "slice_info": {
                        "slice_id": slice_id,
                        "status": "deleted"
                    },
                },
            )
    elif "error" in slice_json and force:
        mongoUtils.delete("slice", slice_json["_id"])
        if monitoring:
            mon_producer.send(
                "nfv_mon",
                value={
                    "action": "katana_mon",
                    "slice_info": {
                        "slice_id": slice_id,
                        "status": "deleted"
                    },
                },
            )

    # Remove Slice from the tenants list on functions
    for func_id in slice_json["functions"]:
        ifunc = mongoUtils.get("func", func_id)
        try:
            ifunc["tenants"].remove(slice_json["_id"])
        except (KeyError, ValueError):
            logger.warning(f"Slice {slice_id} not in function {func_id}")
        else:
            mongoUtils.update("func", func_id, ifunc)

    # Remove Slice dashboard
    if monitoring and slice_monitoring:
        # Use the Grafana API in order to delete the new dashboard for the new slice
        grafana_url = f"http://katana-grafana:3000/api/dashboards/uid/{slice_id}"
        headers = {
            "accept": "application/json",
            "content-type": "application/json"
        }
        grafana_user = os.getenv("GF_SECURITY_ADMIN_USER", "admin")
        grafana_passwd = os.getenv("GF_SECURITY_ADMIN_PASSWORD", "admin")
        r = requests.delete(
            url=grafana_url,
            headers=headers,
            auth=(grafana_user, grafana_passwd),
        )
        logger.info(f"Deleted Grafana dashboard for slice {slice_id}")
        # Stop the threads monitoring NS status of the slice
        ns_inst_info = slice_json["ns_inst_info"]
        mon_producer.send(topic="nfv_mon",
                          value={
                              "action": "delete",
                              "ns_list": ns_inst_info
                          })

    # Check if the NSI has shared NSSIs and remove them
    try:
        for func_key, shared_list_key in slice_json["shared"]["core"].items():
            # Remove the slice from the shared list
            try:
                shared_list = mongoUtils.get("sharing_lists", shared_list_key)
                func = mongoUtils.get("func", func_key)
                if len(shared_list["nest_list"]) > 1:
                    # Remove the Slice from the list
                    shared_list["nest_list"].remove(slice_id)
                    mongoUtils.update("sharing_lists", shared_list_key,
                                      shared_list)
                    func["shared"]["sharing_list"][shared_list_key].remove(
                        slice_id)
                else:
                    # Remove the the shared list
                    mongoUtils.delete("sharing_lists", shared_list_key)
                    del func["shared"]["sharing_list"][shared_list_key]
                mongoUtils.update("func", func_key, func)
            except ValueError as e:
                pass
    except KeyError as e:
        pass

    try:
        for func_key, shared_list_key in slice_json["shared"]["radio"].items():
            # Remove the slice from the shared list
            try:
                shared_list = mongoUtils.get("sharing_lists", shared_list_key)
                func = mongoUtils.get("func", func_key)
                if len(shared_list["nest_list"]) > 1:
                    # Remove the Slice from the list
                    shared_list["nest_list"].remove(slice_id)
                    mongoUtils.update("sharing_lists", shared_list_key,
                                      shared_list)
                    func["shared"]["sharing_list"][shared_list_key].remove(
                        slice_id)
                else:
                    # Remove the the shared list
                    mongoUtils.delete("sharing_lists", shared_list_key)
                    del func["shared"]["sharing_list"][shared_list_key]
                mongoUtils.update("func", func_key, func)
            except ValueError as e:
                pass
    except KeyError as e:
        pass
Exemple #5
0
def ns_details(
    ns_list,
    edge_loc,
    vim_dict,
    total_ns_list,
    shared_function=0,
    shared_slice_list_key=None,
):
    """
    Get details for the NS that are part of the slice
    A) Find the nsd details for each NS
    B) Replace placement value with location
    C) Get the VIM for each NS
    """
    pop_list = []
    for ns in ns_list:
        try:
            if ns["placement_loc"] and not ns["placement"]:
                # Placement to Core already done - go to next NS
                continue
            else:
                # Placement to Edge - Is already done - Need to make another ns
                new_ns = copy.deepcopy(ns)
        except KeyError:
            # No placement at all
            new_ns = ns
        # 00) Check if the function is shared and if the ns is already instantiated
        new_ns["shared_function"] = shared_function
        new_ns["shared_slice_key"] = shared_slice_list_key
        # Search the nsd collection in Mongo for the nsd
        nsd = mongoUtils.find("nsd", {"nsd-id": new_ns["nsd-id"]})
        if not nsd:
            # Bootstrap the NFVOs to check for NSDs that are not in mongo
            # If again is not found, check if NS is optional.
            # If it is just remove it, else error
            nfvo_obj_list = list(mongoUtils.find_all("nfvo_obj"))
            for infvo in nfvo_obj_list:
                nfvo = pickle.loads(infvo["obj"])
                nfvo.bootstrapNfvo()
            nsd = mongoUtils.find("nsd", {"nsd-id": new_ns["nsd-id"]})
            if not nsd and ns.get("optional", False):
                # The NS is optional - continue to next
                pop_list.append(ns)
                continue
            else:
                # Error handling: The ns is not optional and the nsd is not
                # on the NFVO - stop and return
                error_message = f"NSD {new_ns['nsd-id']} not found on any NFVO registered to SM"
                logger.error(error_message)
                return error_message, []
        new_ns["nfvo-id"] = nsd["nfvo_id"]
        new_ns["nsd-info"] = nsd
        # B) ****** Replace placement value with location info ******
        if type(new_ns["placement"]) is str:
            new_ns["placement_loc"] = {"location": new_ns["placement"]}
        else:
            new_ns["placement_loc"] = (lambda x: {
                "location": "Core"
            } if not x else {
                "location": edge_loc
            })(new_ns["placement"])

        # C) ****** Get the VIM info ******
        new_ns["vims"] = []
        loc = new_ns["placement_loc"]["location"]
        get_vim = list(mongoUtils.find_all("vim", {"location": loc}))
        if not get_vim:
            if not new_ns.get("optional", False):
                # Error handling: There is no VIM at that location
                error_message = f"VIM not found in location {loc}"
                logger.error(error_message)
                return error_message, []
            else:
                # The NS is optional - continue to next
                pop_list.append(ns)
                continue
        # TODO: Check the available resources and select vim
        # Temporary use the first element
        selected_vim = get_vim[0]["id"]
        if shared_function:
            selected_vim_id = selected_vim + "_s"
        else:
            selected_vim_id = selected_vim
        new_ns["vims"].append(selected_vim_id)
        try:
            vim_dict[selected_vim_id]["ns_list"].append(new_ns["ns-name"])
            if new_ns["nfvo-id"] not in vim_dict[selected_vim_id]["nfvo_list"]:
                vim_dict[selected_vim_id]["nfvo_list"].append(
                    new_ns["nfvo-id"])
        except KeyError:
            vim_dict[selected_vim_id] = {
                "ns_list": [new_ns["ns-name"]],
                "nfvo_list": [new_ns["nfvo-id"]],
                "shared": shared_function,
                "shared_slice_list_key": shared_slice_list_key,
            }
        resources = vim_dict[selected_vim_id].get("resources", {
            "memory-mb": 0,
            "vcpu-count": 0,
            "storage-gb": 0,
            "instances": 0
        })
        for key in resources:
            resources[key] += nsd["flavor"][key]
        vim_dict[selected_vim_id]["resources"] = resources
        new_ns["placement_loc"]["vim"] = selected_vim_id
        # 0) Create an uuid for the ns
        new_ns["ns-id"] = str(uuid.uuid4())
        total_ns_list.append(new_ns)
        del new_ns
    # Remove the ns that are optional and nsd was not found
    ns_list = [ns for ns in ns_list if ns not in pop_list]
    return 0, pop_list
Exemple #6
0
def add_slice(nest_req):
    """
    Creates the network slice
    """

    # Recreate the NEST with None options where missiong
    nest = {
        "_id": nest_req["_id"],
        "status": "Init",
        "created_at": time.time(),  # unix epoch
        "deployment_time": {
            "Placement_Time": None,
            "Provisioning_Time": None,
            "WAN_Deployment_Time": None,
            "NS_Deployment_Time": None,
            "Radio_Configuration_Time": None,
            "Slice_Deployment_Time": None,
        },
    }
    mongoUtils.add("slice", nest)
    for nest_key in NEST_KEYS_OBJ:
        nest[nest_key] = nest_req.get(nest_key, None)
    for nest_key in NEST_KEYS_LIST:
        nest[nest_key] = nest_req.get(nest_key, [])

    # Check if slice monitoring has been enabled
    monitoring = os.getenv("KATANA_MONITORING", None)
    wim_monitoring = {}
    mon_producer = None
    if monitoring:
        # Create the Kafka producer
        mon_producer = create_producer()
        nest["slice_monitoring"] = {}

    # **** STEP-1: Placement ****
    nest["status"] = "Placement"
    if monitoring:
        mon_producer.send(
            "nfv_mon",
            value={
                "action": "katana_mon",
                "slice_info": {
                    "slice_id": nest["_id"],
                    "status": "placement"
                },
            },
        )

    nest["conf_comp"] = {"nf": [], "ems": []}
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Placement")
    placement_start_time = time.time()

    # Initiate the lists
    vim_dict = {}
    total_ns_list = []
    ems_messages = {}

    # Get Details for the Network Services
    # i) The NS part of the core slice
    inst_functions = {}
    for connection in nest["connections"]:
        for key in connection:
            # Check if the function has been instantiated from another connection
            if connection[key]["_id"] in inst_functions:
                connection[key] = inst_functions[connection[key]["_id"]]
                continue
            # Check if the function is shared with another slice
            # shared_check values: 0: No shared, 1: First shared, 2: Shared
            shared_check = 0
            shared_slice_list_key = None
            try:
                shared_slice_list_key = nest["shared"][key][connection[key]
                                                            ["_id"]]
                shared_slice_list = connection[key]["shared"]["sharing_list"][
                    shared_slice_list_key]
                if len(shared_slice_list) > 1:
                    shared_check = 2
                else:
                    shared_check = 1
            except KeyError:
                pass
            try:
                err, pop_list = ns_details(
                    connection[key]["ns_list"],
                    connection[key]["location"],
                    vim_dict,
                    total_ns_list,
                    shared_check,
                    shared_slice_list_key,
                )
                if pop_list:
                    connection[key]["ns_list"] = [
                        x for x in connection[key]["ns_list"]
                        if x not in pop_list
                    ]
                if err:
                    nest["status"] = f"Failed - {err}"
                    nest["ns_inst_info"] = {}
                    nest["total_ns_list"] = []
                    mongoUtils.update("slice", nest["_id"], nest)
                    return
                inst_functions[connection[key]["_id"]] = connection[key]
            except KeyError:
                continue

    # ii) The extra NS of the slice
    for location in nest["coverage"]:
        err, _ = ns_details(nest["ns_list"], location, vim_dict, total_ns_list)
        if err:
            nest["status"] = f"Failed - {err}"
            nest["ns_inst_info"] = {}
            nest["total_ns_list"] = []
            mongoUtils.update("slice", nest["_id"], nest)
            return

    nest["vim_list"] = vim_dict
    nest["total_ns_list"] = total_ns_list
    nest["deployment_time"]["Placement_Time"] = format(
        time.time() - placement_start_time, ".4f")

    # **** STEP-2: Resource Provisioning ****
    nest["status"] = "Provisioning"
    if monitoring:
        mon_producer.send(
            "nfv_mon",
            value={
                "action": "katana_mon",
                "slice_info": {
                    "slice_id": nest["_id"],
                    "status": "provisioning"
                },
            },
        )
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Provisioning")
    prov_start_time = time.time()

    # *** STEP-2a: Cloud ***
    # *** STEP-2a-i: Create the new tenant/project on the VIM ***
    for num, (vim, vim_info) in enumerate(vim_dict.items()):
        if vim_info["shared"]:
            vim_id = vim[:-2]
        else:
            vim_id = vim
        target_vim = mongoUtils.find("vim", {"id": vim_id})
        target_vim_obj = pickle.loads(
            mongoUtils.find("vim_obj", {"id": vim_id})["obj"])

        # Define project parameters
        if vim_info["shared"] == 1:
            name = "vim_{0}_katana_{1}_shared".format(
                num, vim_info["shared_slice_list_key"])
            tenant_name = vim_info["shared_slice_list_key"]
        elif vim_info["shared"] == 0:
            name = "vim_{0}_katana_{1}".format(num, nest["_id"])
            tenant_name = nest["_id"]
        else:
            # Find the shared list
            sharing_lists = mongoUtils.get("sharing_lists",
                                           vim_info["shared_slice_list_key"])
            vim_dict[vim] = sharing_lists["vims"][target_vim["id"]]
            mongoUtils.update("slice", nest["_id"], nest)
            continue
        tenant_project_name = name
        tenant_project_description = name
        tenant_project_user = name
        tenant_project_password = "******"
        # If the vim is Openstack type, set quotas
        quotas = (vim_info["resources"] if target_vim["type"] == "openstack"
                  or target_vim["type"] == "Openstack" else None)
        ids = target_vim_obj.create_slice_prerequisites(
            tenant_project_name,
            tenant_project_description,
            tenant_project_user,
            tenant_project_password,
            nest["_id"],
            quotas=quotas,
        )
        # Register the tenant to the mongo db
        target_vim["tenants"][tenant_name] = name
        mongoUtils.update("vim", target_vim["_id"], target_vim)

        # STEP-2a-ii: Αdd the new VIM tenant to NFVO
        if target_vim["type"] == "openstack":
            # Update the config parameter for the tenant
            config_param = dict(security_groups=ids["secGroupName"])
        elif target_vim["type"] == "opennebula":
            config_param = target_vim["config"]
        else:
            config_param = {}

        for nfvo_id in vim_info["nfvo_list"]:
            target_nfvo = mongoUtils.find("nfvo", {"id": nfvo_id})
            target_nfvo_obj = pickle.loads(
                mongoUtils.find("nfvo_obj", {"id": nfvo_id})["obj"])
            vim_id = target_nfvo_obj.addVim(
                tenant_project_name,
                target_vim["password"],
                target_vim["type"],
                target_vim["auth_url"],
                target_vim["username"],
                config_param,
            )
            vim_info["nfvo_vim_account"] = vim_info.get("nfvo_vim_account", {})
            vim_info["nfvo_vim_account"][nfvo_id] = vim_id
            # Register the tenant to the mongo db
            target_nfvo["tenants"][tenant_name] = target_nfvo["tenants"].get(
                nest["_id"], [])
            target_nfvo["tenants"][tenant_name].append(vim_id)
            mongoUtils.update("nfvo", target_nfvo["_id"], target_nfvo)

        if vim_info["shared"] == 1:
            sharing_lists = mongoUtils.get("sharing_lists",
                                           vim_info["shared_slice_list_key"])
            sharing_lists["vims"] = sharing_lists.get("vims", {})
            sharing_lists["vims"][target_vim["id"]] = vim_info
            mongoUtils.update("sharing_lists",
                              vim_info["shared_slice_list_key"], sharing_lists)

    mongoUtils.update("slice", nest["_id"], nest)
    # *** STEP-2b: WAN ***
    if mongoUtils.count("wim") <= 0:
        logger.warning("There is no registered WIM")
    else:
        wan_start_time = time.time()
        # Crate the data for the WIM
        wim_data = {
            "_id": nest["_id"],
            "core_connections": [],
            "extra_ns": [],
            "slice_sla": {}
        }
        # i) Create the slice_sla data for the WIM
        wim_data["slice_sla"] = {
            "network_DL_throughput": nest["network_DL_throughput"],
            "network_UL_throughput": nest["network_UL_throughput"],
            "mtu": nest["mtu"],
        }
        # ii) Add the connections
        for connection in nest["connections"]:
            data = {}
            for key in connection:
                key_data = {}
                try:
                    ns_l = connection[key]["ns_list"]
                except KeyError:
                    pass
                else:
                    key_data["ns"] = []
                    for ns in ns_l:
                        if ns["placement_loc"] not in key_data["ns"]:
                            key_data["ns"].append(ns["placement_loc"])
                try:
                    pnf_l = connection[key]["pnf_list"]
                except KeyError:
                    pass
                else:
                    key_data["pnf"] = pnf_l
                if key_data:
                    data[key] = key_data
            if data:
                wim_data["core_connections"].append(data)
        # iii) Add the extra Network Services
        for ns in nest["ns_list"]:
            if ns["placement_loc"] not in wim_data["extra_ns"]:
                wim_data["extra_ns"].append(ns["placement_loc"])
        # iV) Add the probes
        wim_data["probes"] = nest["probe_list"]
        # Select WIM - Assume that there is only one registered
        wim_list = list(mongoUtils.index("wim"))
        target_wim = wim_list[0]
        target_wim_id = target_wim["id"]
        target_wim_obj = pickle.loads(
            mongoUtils.find("wim_obj", {"id": target_wim_id})["obj"])
        target_wim_obj.create_slice(wim_data)
        nest["wim_data"] = wim_data
        target_wim["slices"][nest["_id"]] = nest["_id"]
        mongoUtils.update("slice", nest["_id"], nest)
        mongoUtils.update("wim", target_wim["_id"], target_wim)
        # Add monitoring from WIM in nest
        try:
            wim_monitoring = target_wim["monitoring-url"]
            nest["slice_monitoring"]["WIM"] = wim_monitoring
        except KeyError:
            pass
        nest["deployment_time"]["WAN_Deployment_Time"] = format(
            time.time() - wan_start_time, ".4f")
    nest["deployment_time"]["Provisioning_Time"] = format(
        time.time() - prov_start_time, ".4f")

    # **** STEP-3: Slice Activation Phase****
    nest["status"] = "Activation"
    if monitoring:
        mon_producer.send(
            "nfv_mon",
            value={
                "action": "katana_mon",
                "slice_info": {
                    "slice_id": nest["_id"],
                    "status": "activation"
                },
            },
        )
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Activation")
    # *** STEP-3a: Cloud ***
    # Instantiate NS
    # Store info about instantiated NSs
    ns_inst_info = {}
    nest["deployment_time"]["NS_Deployment_Time"] = {}
    for ns in total_ns_list:
        ns["start_time"] = time.time()
        if ns["shared_function"] == 2:
            # The ns is already instantiated and there is no need to instantiate again
            # Find the sharing list
            shared_list = mongoUtils.get("sharing_lists",
                                         ns["shared_slice_key"])
            ns_inst_info[ns["ns-id"]] = shared_list["ns_list"][ns["nsd-id"]]
            nest["conf_comp"]["nf"].append(ns["nsd-id"])
            continue
        ns_inst_info[ns["ns-id"]] = {}
        target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
        target_nfvo_obj = pickle.loads(
            mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"])
        selected_vim = ns["placement_loc"]["vim"]
        nfvo_vim_account = vim_dict[selected_vim]["nfvo_vim_account"][
            ns["nfvo-id"]]
        nfvo_inst_ns = target_nfvo_obj.instantiateNs(ns["ns-name"],
                                                     ns["nsd-id"],
                                                     nfvo_vim_account)
        ns_inst_info[ns["ns-id"]][ns["placement_loc"]["location"]] = {
            "nfvo_inst_ns": nfvo_inst_ns,
            "nfvo-id": ns["nfvo-id"],
            "ns-name": ns["ns-name"],
            "slice_id": nest["_id"],
            "vim": selected_vim,
        }
        # Check if this the first slice of a sharing list
        if ns["shared_function"] == 1:
            shared_list = mongoUtils.get("sharing_lists",
                                         ns["shared_slice_key"])
            ns_inst_info[ns["ns-id"]][ns["placement_loc"]
                                      ["location"]]["shared"] = True
            ns_inst_info[ns["ns-id"]][ns["placement_loc"]["location"]][
                "sharing_list"] = ns["shared_slice_key"]
            shared_list["ns_list"][ns["nsd-id"]] = ns_inst_info[ns["ns-id"]]
            mongoUtils.update("sharing_lists", ns["shared_slice_key"],
                              shared_list)
        nest["conf_comp"]["nf"].append(ns["nsd-id"])
        time.sleep(4)
        time.sleep(2)

    # Get the nsr for each service and wait for the activation
    for ns in total_ns_list:
        target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
        target_nfvo_obj = pickle.loads(
            mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"])
        site = ns["placement_loc"]
        nfvo_inst_ns_id = ns_inst_info[ns["ns-id"]][
            site["location"]]["nfvo_inst_ns"]
        insr = target_nfvo_obj.getNsr(nfvo_inst_ns_id)
        while insr["operational-status"] != "running" or insr[
                "config-status"] != "configured":
            if insr["operational-status"] == "failed":
                error_message = (
                    f"Network Service {ns['nsd-id']} failed to start on NFVO {ns['nfvo-id']}."
                )
                logger.error(error_message)
                nest["ns_inst_info"] = ns_inst_info
                nest["status"] = f"Failed - {error_message}"
                mongoUtils.update("slice", nest["_id"], nest)
                return
            time.sleep(10)
            insr = target_nfvo_obj.getNsr(nfvo_inst_ns_id)
        nest["deployment_time"]["NS_Deployment_Time"][ns["ns-name"]] = format(
            time.time() - ns["start_time"], ".4f")
        # Get the IPs of the instantiated NS
        vnf_list = []
        vnfr_id_list = target_nfvo_obj.getVnfrId(insr)
        for ivnfr_id in vnfr_id_list:
            vnfr = target_nfvo_obj.getVnfr(ivnfr_id)
            vnf_list.append(target_nfvo_obj.getIPs(vnfr))
        ns_inst_info[ns["ns-id"]][site["location"]]["vnfr"] = vnf_list

    nest["ns_inst_info"] = ns_inst_info
    mongoUtils.update("slice", nest["_id"], nest)

    # If monitoring parameter is set, send the ns_list to nfv_mon module
    if monitoring and mon_producer:
        mon_producer.send(topic="nfv_mon",
                          value={
                              "action": "create",
                              "ns_list": ns_inst_info
                          })
        nest["slice_monitoring"]["nfv_ns_status_monitoring"] = True

    # *** STEP-3b: Radio Slice Configuration ***
    if mongoUtils.count("ems") <= 0:
        logger.warning("There is no registered EMS")
    else:
        # Add the management IPs for the NS sent ems in ems_messages:
        ems_radio_data = {
            "ue_DL_throughput": nest["ue_DL_throughput"],
            "ue_UL_throughput": nest["ue_UL_throughput"],
            "group_communication_support": nest["group_communication_support"],
            "number_of_terminals": nest["number_of_terminals"],
            "positional_support": nest["positional_support"],
            "radio_spectrum": nest["radio_spectrum"],
            "device_velocity": nest["device_velocity"],
            "terminal_density": nest["terminal_density"],
        }
        radio_start_time = time.time()
        iii = 0
        for connection in nest["connections"]:
            iii += 1
            data = {}
            ems_id_list = []
            for key in connection:
                # Check if the connection is shared
                try:
                    shared_slice_list_key = nest["shared"][key][connection[key]
                                                                ["_id"]]
                    shared_slice_list = connection[key]["shared"][
                        "sharing_list"][shared_slice_list_key]
                    shared = True
                    if len(shared_slice_list) > 1:
                        shared_check = 2
                    else:
                        shared_check = 1
                except KeyError:
                    shared_slice_list_key = None
                    shared = False
                key_data = {}
                try:
                    ems_id = connection[key]["ems-id"]
                except KeyError:
                    continue
                else:
                    if ems_id not in ems_id_list:
                        ems_id_list.append(ems_id)
                    try:
                        ns_l = connection[key]["ns_list"]
                    except KeyError:
                        pass
                    else:
                        key_data["ns"] = []
                        for ns in ns_l:
                            try:
                                ns_info = ns_inst_info[ns["ns-id"]][
                                    connection[key]["location"]]
                            except KeyError:
                                ns_info = ns_inst_info[ns["ns-id"]]["Core"]
                            ns_data = {
                                "name": ns["ns-name"],
                                "location": ns["placement_loc"]["location"],
                                "vnf_list": ns_info["vnfr"],
                            }
                            # Add the shared information for the ns, if any
                            if shared:
                                ns_data["shared"] = ns_inst_info[ns["ns-id"]][
                                    connection[key]["location"]]["shared"]
                                ns_data["sharing_list"] = ns_inst_info[
                                    ns["ns-id"]][connection[key]
                                                 ["location"]]["sharing_list"]
                            else:
                                ns_data["shared"] = False
                            key_data["ns"].append(ns_data)
                try:
                    key_data["pnf"] = connection[key]["pnf_list"]
                except KeyError:
                    pass
                else:
                    if shared:
                        for ipnf in connection[key]["pnf_list"]:
                            ipnf["shared"] = True
                            ipnf["sharing_list"] = shared_slice_list_key
                if key_data:
                    data[key] = key_data
            if data:
                data["slice_sla"] = ems_radio_data
                data["slice_id"] = nest["_id"]
                for ems_id in ems_id_list:
                    messages = ems_messages.get(ems_id, [])
                    messages.append(data)
                    ems_messages[ems_id] = messages

        for ems_id, ems_message in ems_messages.items():
            # Find the EMS
            target_ems = mongoUtils.find("ems", {"id": ems_id})
            if not target_ems:
                # Error handling: There is no such EMS
                logger.error(
                    "EMS {} not found - No configuration".format(ems_id))
                continue
            target_ems_obj = pickle.loads(
                mongoUtils.find("ems_obj", {"id": ems_id})["obj"])
            # Send the message
            for imessage in ems_message:
                target_ems_obj.conf_radio(imessage)
            nest["conf_comp"]["ems"].append(ems_id)
        nest["ems_data"] = ems_messages
        nest["deployment_time"]["Radio_Configuration_Time"] = format(
            time.time() - radio_start_time, ".4f")

    # *** STEP-4: Finalize ***
    # Create Grafana Dashboard for monitoring
    # Create the NS status panel
    if monitoring:
        # Open the Grafana Dashboard template
        monitoring_slice_id = "slice_" + nest["_id"].replace("-", "_")
        with open("/katana-grafana/templates/new_dashboard.json",
                  mode="r") as dashboard_file:
            new_dashboard = json.load(dashboard_file)
            new_dashboard["dashboard"]["title"] = monitoring_slice_id
            new_dashboard["dashboard"]["uid"] = nest["_id"]
        # Add the dashboard panels
        # Add the NS Status panels
        expr = "ns_status" + '{slice_id="' + nest["_id"] + '"}'
        targets = [{
            "expr": expr,
            "legendFormat": "",
            "interval": "",
            "format": "table",
            "instant": True
        }]
        infra_targets = {}
        for ns in ns_inst_info.values():
            for key, value in ns.items():
                # Check if the VIM supports infrastructure monitoring
                search_vim_id = value["vim"]
                if value.get("shared", False):
                    search_vim_id = search_vim_id[:-2]
                selected_vim = mongoUtils.find("vim", {"id": search_vim_id})
                try:
                    vim_monitoring = selected_vim["type"]
                    vim_monitoring_list = infra_targets.get(vim_monitoring, [])
                    for ivnf in value["vnfr"]:
                        vim_monitoring_list += ivnf["vm_list"]
                    infra_targets[vim_monitoring] = vim_monitoring_list
                except KeyError:
                    pass
        # Create the VM Monitoring panels
        PANELS = [
            "vm_state",
            "vm_cpu_cpu_time",
            "vm_cpu_overall_cpu_usage",
            "vm_memory_actual",
            "vm_memory_available",
            "vm_memory_usage",
            "vm_disk_read_bytes",
            "vm_disk_write_bytes",
            "vm_disk_errors",
        ]
        with open("/katana-grafana/templates/new_vm_monitoring_panel.json",
                  mode="r") as panel_file:
            vm_panel_template = json.load(panel_file)
            for i, panel in enumerate(PANELS):
                vm_panel = copy.deepcopy(vm_panel_template)
                vm_panel["title"] = panel
                vm_panel["gridPos"] = {"h": 8, "w": 12, "x": 13, "y": i * 9}
                vm_panel["id"] = 10 + i
                vm_targets = []
                for vim_type, vm_list in infra_targets.items():
                    for vm in vm_list:
                        expr = (vim_type + "_" + panel + '{project=~".*' +
                                nest["_id"] + '",vm_name="' + vm + '"}')
                        vm_targets.append({
                            "expr": expr,
                            "interval": "",
                            "legendFormat": ""
                        })
                vm_panel["targets"] = vm_targets
                new_dashboard["dashboard"]["panels"].append(vm_panel)
        # Read and fill the NS Status panel template
        with open("/katana-grafana/templates/new_ns_status_panel.json",
                  mode="r") as panel_file:
            ns_panel = json.load(panel_file)
            ns_panel["targets"] = targets
            new_dashboard["dashboard"]["panels"].append(ns_panel)
        # Add the WIM Monitoring panel
        if wim_monitoring:
            # Read and fill the panel template
            with open("/katana-grafana/templates/new_wim_panel.json",
                      mode="r") as panel_file:
                wim_panel = json.load(panel_file)
                wim_panel["targets"].append({
                    "expr": f"rate({monitoring_slice_id}_flows[1m])",
                    "interval": "",
                    "legendFormat": "",
                    "refId": "A",
                })
                new_dashboard["dashboard"]["panels"].append(wim_panel)
        mon_producer.send(
            "nfv_mon",
            value={
                "action": "katana_mon",
                "slice_info": {
                    "slice_id": nest["_id"],
                    "status": "running"
                },
            },
        )

        # Use the Grafana API in order to create the new dashboard for the new slice
        grafana_url = "http://katana-grafana:3000/api/dashboards/db"
        headers = {
            "accept": "application/json",
            "content-type": "application/json"
        }
        grafana_user = os.getenv("GF_SECURITY_ADMIN_USER", "admin")
        grafana_passwd = os.getenv("GF_SECURITY_ADMIN_PASSWORD", "admin")
        r = requests.post(
            url=grafana_url,
            headers=headers,
            auth=(grafana_user, grafana_passwd),
            data=json.dumps(new_dashboard),
        )
        logger.info(f"Created new Grafana dashboard for slice {nest['_id']}")
    logger.info(f"{nest['_id']} Status: Running")
    nest["status"] = "Running"
    nest["deployment_time"]["Slice_Deployment_Time"] = format(
        time.time() - nest["created_at"], ".4f")
    mongoUtils.update("slice", nest["_id"], nest)
Exemple #7
0
def delete_slice(slice_id, force=False):
    """
    Deletes the given network slice
    """

    # Update the slice status in mongo db
    slice_json = mongoUtils.get("slice", slice_id)
    slice_json["status"] = "Terminating"
    mongoUtils.update("slice", slice_json["_id"], slice_json)
    logger.info(f"{slice_json['_id']} Status: Terminating")

    # *** Step-1: Radio Slice Configuration ***
    if slice_json["conf_comp"]["ems"]:
        ems_messages = slice_json.get("ems_data", None)
        if ems_messages:
            for ems_id, ems_message in ems_messages.items():
                # Find the EMS
                target_ems = mongoUtils.find("ems", {"id": ems_id})
                if not target_ems or ems_id not in slice_json["conf_comp"]["ems"]:
                    # Error handling: There is no such EMS
                    logger.error("EMS {} not found - No configuration".format(ems_id))
                    continue
                target_ems_obj = pickle.loads(mongoUtils.find("ems_obj", {"id": ems_id})["obj"])
                target_ems_obj.del_slice(ems_message)
    else:
        logger.info("There was not EMS configuration")

    # *** Step-2: WAN Slice ***
    wim_data = slice_json.get("wim_data", None)
    if wim_data:
        # Select WIM - Assume that there is only one registered
        wim_list = list(mongoUtils.index("wim"))
        if wim_list:
            target_wim = wim_list[0]
            target_wim_id = target_wim["id"]
            target_wim_obj = pickle.loads(mongoUtils.find("wim_obj", {"id": target_wim_id})["obj"])
            target_wim_obj.del_slice(slice_id)
            try:
                del target_wim["slices"][slice_json["_id"]]
            except KeyError:
                logger.warning(f"Slice {slice_id} not in WIM {target_wim_id}")
            else:
                mongoUtils.update("wim", target_wim["_id"], target_wim)
        else:
            err = "Cannot find WIM - WAN Slice will not be deleted"
            logger.warning(err)
            slice_json["status"] = "Error"
            slice_json["error"] = slice_json.get("error", "") + err
            mongoUtils.update("slice", slice_json["_id"], slice_json)
    else:
        logger.info("There was no WIM configuration")

    # *** Step-3: Cloud ***
    vim_error_list = []
    try:
        total_ns_list = slice_json["total_ns_list"]
        ns_inst_info = slice_json["ns_inst_info"]
        for ns in total_ns_list:
            if ns["nsd-id"] not in slice_json["conf_comp"]["nf"]:
                logger.error(f"{ns['nsd-id']} was not instantiated successfully")
                continue
            # Get the NFVO
            nfvo_id = ns["nfvo-id"]
            target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
            if not target_nfvo:
                logger.warning(
                    "NFVO with id {} was not found - NSs won't terminate".format(nfvo_id)
                )
                vim_error_list += ns["vims"]
                continue
            target_nfvo_obj = pickle.loads(
                mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"]
            )
            # Stop the NS
            nfvo_inst_ns = ns_inst_info[ns["ns-id"]][ns["placement_loc"]["location"]][
                "nfvo_inst_ns"
            ]
            target_nfvo_obj.deleteNs(nfvo_inst_ns)
            while True:
                if target_nfvo_obj.checkNsLife(nfvo_inst_ns):
                    break
                time.sleep(5)
    except KeyError as e:
        err = f"Error, not all NSs started or terminated correctly {e}"
        logger.warning(err)
        slice_json["status"] = "Error"
        slice_json["error"] = slice_json.get("error", "") + err
        mongoUtils.update("slice", slice_json["_id"], slice_json)

    vim_dict = slice_json["vim_list"]
    for vim, vim_info in vim_dict.items():
        try:
            # Delete the new tenants from the NFVO
            for nfvo, vim_account in vim_info["nfvo_vim_account"].items():
                # Get the NFVO
                target_nfvo = mongoUtils.find("nfvo", {"id": nfvo})
                target_nfvo_obj = pickle.loads(mongoUtils.find("nfvo_obj", {"id": nfvo})["obj"])
                # Delete the VIM and update nfvo db
                target_nfvo_obj.deleteVim(vim_account)
                target_nfvo["tenants"][slice_json["_id"]].remove(vim_account)
                if len(target_nfvo["tenants"][slice_json["_id"]]) == 0:
                    try:
                        del target_nfvo["tenants"][slice_json["_id"]]
                    except KeyError:
                        logger.warning(f"Slice {slice_id} not in NFO {nfvo}")
                    else:
                        mongoUtils.update("nfvo", target_nfvo["_id"], target_nfvo)
            # Delete the tenants from every vim
            if vim not in vim_error_list:
                # Get the VIM
                target_vim = mongoUtils.find("vim", {"id": vim})
                if not target_vim:
                    logger.warning("VIM id {} was not found - Tenant won't be deleted".format(vim))
                    continue
                target_vim_obj = pickle.loads(mongoUtils.find("vim_obj", {"id": vim})["obj"])
                target_vim_obj.delete_proj_user(target_vim["tenants"][slice_json["_id"]])
                try:
                    del target_vim["tenants"][slice_json["_id"]]
                except KeyError:
                    logger.warning(f"Slice {slice_id} not in VIM {vim}")
                else:
                    mongoUtils.update("vim", target_vim["_id"], target_vim)
        except KeyError as e:
            err = f"Error, not all tenants created or removed correctly {e}"
            logger.warning(err)
            slice_json["status"] = "Error"
            slice_json["error"] = slice_json.get("error", "") + err
            mongoUtils.update("slice", slice_json["_id"], slice_json)

    if "error" not in slice_json:
        mongoUtils.delete("slice", slice_json["_id"])
    elif "error" in slice_json and force:
        mongoUtils.delete("slice", slice_json["_id"])

    # Remove Slice from the tenants list on functions
    for func_id in slice_json["functions"]:
        ifunc = mongoUtils.get("func", func_id)
        try:
            ifunc["tenants"].remove(slice_json["_id"])
        except (KeyError, ValueError):
            logger.warning(f"Slice {slice_id} not in function {func_id}")
        else:
            mongoUtils.update("func", func_id, ifunc)
Exemple #8
0
def add_slice(nest_req):
    """
    Creates the network slice
    """

    # Recreate the NEST with None options where missiong
    nest = {
        "_id": nest_req["_id"],
        "status": "Init",
        "created_at": time.time(),  # unix epoch
        "deployment_time": {
            "Placement_Time": None,
            "Provisioning_Time": None,
            "WAN_Deployment_Time": None,
            "NS_Deployment_Time": None,
            "Radio_Configuration_Time": None,
            "Slice_Deployment_Time": None,
        },
    }
    mongoUtils.add("slice", nest)
    for nest_key in NEST_KEYS_OBJ:
        nest[nest_key] = nest_req.get(nest_key, None)
    for nest_key in NEST_KEYS_LIST:
        nest[nest_key] = nest_req.get(nest_key, [])

    # **** STEP-1: Placement ****
    nest["status"] = "Placement"
    nest["conf_comp"] = {"nf": [], "ems": []}
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Placement")
    placement_start_time = time.time()

    # Initiate the lists
    vim_dict = {}
    total_ns_list = []
    ems_messages = {}

    # Get Details for the Network Services
    # i) The extra NS of the slice
    for location in nest["coverage"]:
        err, _ = ns_details(nest["ns_list"], location, vim_dict, total_ns_list)
        if err:
            delete_slice(nest)
            return
    del nest["ns_list"]
    nest["ns_list"] = copy.deepcopy(total_ns_list)
    # ii) The NS part of the core slice
    inst_functions = {}
    for connection in nest["connections"]:
        for key in connection:
            if connection[key]["_id"] in inst_functions:
                connection[key] = inst_functions[connection[key]["_id"]]
                continue
            try:
                err, pop_list = ns_details(
                    connection[key]["ns_list"], connection[key]["location"], vim_dict, total_ns_list
                )
                if pop_list:
                    connection[key]["ns_list"] = [
                        x for x in connection[key]["ns_list"] if x not in pop_list
                    ]
                if err:
                    delete_slice(nest)
                    return
                inst_functions[connection[key]["_id"]] = connection[key]
            except KeyError:
                continue

    nest["vim_list"] = vim_dict
    nest["total_ns_list"] = total_ns_list
    nest["deployment_time"]["Placement_Time"] = format(time.time() - placement_start_time, ".4f")

    # **** STEP-2: Resource Provisioning ****
    nest["status"] = "Provisioning"
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Provisioning")
    prov_start_time = time.time()

    # *** STEP-2a: Cloud ***
    # *** STEP-2a-i: Create the new tenant/project on the VIM ***
    for num, (vim, vim_info) in enumerate(vim_dict.items()):
        target_vim = mongoUtils.find("vim", {"id": vim})
        target_vim_obj = pickle.loads(mongoUtils.find("vim_obj", {"id": vim})["obj"])
        # Define project parameters
        tenant_project_name = "vim_{0}_katana_{1}".format(num, nest["_id"])
        tenant_project_description = "vim_{0}_katana_{1}".format(num, nest["_id"])
        tenant_project_user = "******".format(num, nest["_id"])
        tenant_project_password = "******"
        # If the vim is Openstack type, set quotas
        quotas = (
            vim_info["resources"]
            if target_vim["type"] == "openstack" or target_vim["type"] == "Openstack"
            else None
        )
        ids = target_vim_obj.create_slice_prerequisites(
            tenant_project_name,
            tenant_project_description,
            tenant_project_user,
            tenant_project_password,
            nest["_id"],
            quotas=quotas,
        )
        # Register the tenant to the mongo db
        target_vim["tenants"][nest["_id"]] = tenant_project_name
        mongoUtils.update("vim", target_vim["_id"], target_vim)

        # STEP-2a-ii: Αdd the new VIM tenant to NFVO
        if target_vim["type"] == "openstack":
            # Update the config parameter for the tenant
            config_param = dict(security_groups=ids["secGroupName"])
        elif target_vim["type"] == "opennebula":
            config_param = target_vim["config"]
        else:
            config_param = {}

        for nfvo_id in vim_info["nfvo_list"]:
            target_nfvo = mongoUtils.find("nfvo", {"id": nfvo_id})
            target_nfvo_obj = pickle.loads(mongoUtils.find("nfvo_obj", {"id": nfvo_id})["obj"])
            vim_id = target_nfvo_obj.addVim(
                tenant_project_name,
                target_vim["password"],
                target_vim["type"],
                target_vim["auth_url"],
                target_vim["username"],
                config_param,
            )
            vim_info["nfvo_vim_account"] = vim_info.get("nfvo_vim_account", {})
            vim_info["nfvo_vim_account"][nfvo_id] = vim_id
            # Register the tenant to the mongo db
            target_nfvo["tenants"][nest["_id"]] = target_nfvo["tenants"].get(nest["_id"], [])
            target_nfvo["tenants"][nest["_id"]].append(vim_id)
            mongoUtils.update("nfvo", target_nfvo["_id"], target_nfvo)

    mongoUtils.update("slice", nest["_id"], nest)
    # *** STEP-2b: WAN ***
    if mongoUtils.count("wim") <= 0:
        logger.warning("There is no registered WIM")
    else:
        wan_start_time = time.time()
        # Crate the data for the WIM
        wim_data = {"_id": nest["_id"], "core_connections": [], "extra_ns": [], "slice_sla": {}}
        # i) Create the slice_sla data for the WIM
        wim_data["slice_sla"] = {
            "network_DL_throughput": nest["network_DL_throughput"],
            "network_UL_throughput": nest["network_UL_throughput"],
            "mtu": nest["mtu"],
        }
        # ii) Add the connections
        for connection in nest["connections"]:
            data = {}
            for key in connection:
                key_data = {}
                try:
                    ns_l = connection[key]["ns_list"]
                except KeyError:
                    pass
                else:
                    key_data["ns"] = []
                    for ns in ns_l:
                        if ns["placement_loc"] not in key_data["ns"]:
                            key_data["ns"].append(ns["placement_loc"])
                try:
                    pnf_l = connection[key]["pnf_list"]
                except KeyError:
                    pass
                else:
                    key_data["pnf"] = pnf_l
                if key_data:
                    data[key] = key_data
            if data:
                wim_data["core_connections"].append(data)
        # iii) Add the extra Network Services
        for ns in nest["ns_list"]:
            if ns["placement_loc"] not in wim_data["extra_ns"]:
                wim_data["extra_ns"].append(ns["placement_loc"])
        # iV) Add the probes
        wim_data["probes"] = nest["probe_list"]
        # Select WIM - Assume that there is only one registered
        wim_list = list(mongoUtils.index("wim"))
        target_wim = wim_list[0]
        target_wim_id = target_wim["id"]
        target_wim_obj = pickle.loads(mongoUtils.find("wim_obj", {"id": target_wim_id})["obj"])
        target_wim_obj.create_slice(wim_data)
        nest["wim_data"] = wim_data
        target_wim["slices"][nest["_id"]] = nest["_id"]
        mongoUtils.update("wim", target_wim["_id"], target_wim)
        nest["deployment_time"]["WAN_Deployment_Time"] = format(time.time() - wan_start_time, ".4f")
    nest["deployment_time"]["Provisioning_Time"] = format(time.time() - prov_start_time, ".4f")

    # **** STEP-3: Slice Activation Phase****
    nest["status"] = "Activation"
    mongoUtils.update("slice", nest["_id"], nest)
    logger.info(f"{nest['_id']} Status: Activation")
    # *** STEP-3a: Cloud ***
    # Instantiate NS
    # Store info about instantiated NSs
    ns_inst_info = {}
    nest["deployment_time"]["NS_Deployment_Time"] = {}
    for ns in total_ns_list:
        ns["start_time"] = time.time()
        ns_inst_info[ns["ns-id"]] = {}
        target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
        target_nfvo_obj = pickle.loads(mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"])
        selected_vim = ns["placement_loc"]["vim"]
        nfvo_vim_account = vim_dict[selected_vim]["nfvo_vim_account"][ns["nfvo-id"]]
        nfvo_inst_ns = target_nfvo_obj.instantiateNs(ns["ns-name"], ns["nsd-id"], nfvo_vim_account)
        ns_inst_info[ns["ns-id"]][ns["placement_loc"]["location"]] = {"nfvo_inst_ns": nfvo_inst_ns}
        nest["conf_comp"]["nf"].append(ns["nsd-id"])
        time.sleep(4)
        time.sleep(2)

    # Get the nsr for each service and wait for the activation
    for ns in total_ns_list:
        target_nfvo = mongoUtils.find("nfvo", {"id": ns["nfvo-id"]})
        target_nfvo_obj = pickle.loads(mongoUtils.find("nfvo_obj", {"id": ns["nfvo-id"]})["obj"])
        site = ns["placement_loc"]
        nfvo_inst_ns_id = ns_inst_info[ns["ns-id"]][site["location"]]["nfvo_inst_ns"]
        insr = target_nfvo_obj.getNsr(nfvo_inst_ns_id)
        while insr["operational-status"] != "running" or insr["config-status"] != "configured":
            time.sleep(10)
            insr = target_nfvo_obj.getNsr(nfvo_inst_ns_id)
        nest["deployment_time"]["NS_Deployment_Time"][ns["ns-name"]] = format(
            time.time() - ns["start_time"], ".4f"
        )
        # Get the IPs of the instantiated NS
        vnf_list = []
        vnfr_id_list = target_nfvo_obj.getVnfrId(insr)
        for ivnfr_id in vnfr_id_list:
            vnfr = target_nfvo_obj.getVnfr(ivnfr_id)
            vnf_list.append(target_nfvo_obj.getIPs(vnfr))
        ns_inst_info[ns["ns-id"]][site["location"]]["vnfr"] = vnf_list

    nest["ns_inst_info"] = ns_inst_info
    mongoUtils.update("slice", nest["_id"], nest)

    # *** STEP-3b: Radio Slice Configuration ***
    if mongoUtils.count("ems") <= 0:
        logger.warning("There is no registered EMS")
    else:
        # Add the management IPs for the NS sent ems in ems_messages:
        ems_radio_data = {
            "ue_DL_throughput": nest["ue_DL_throughput"],
            "ue_UL_throughput": nest["ue_UL_throughput"],
            "group_communication_support": nest["group_communication_support"],
            "number_of_terminals": nest["number_of_terminals"],
            "positional_support": nest["positional_support"],
            "radio_spectrum": nest["radio_spectrum"],
            "device_velocity": nest["device_velocity"],
            "terminal_density": nest["terminal_density"],
        }
        radio_start_time = time.time()
        for connection in nest["connections"]:
            data = {}
            ems_id_list = []
            for key in connection:
                key_data = {}
                try:
                    ems_id = connection[key]["ems-id"]
                except KeyError:
                    continue
                else:
                    if ems_id not in ems_id_list:
                        ems_id_list.append(ems_id)
                    try:
                        ns_l = connection[key]["ns_list"]
                    except KeyError:
                        pass
                    else:
                        key_data["ns"] = []
                        for ns in ns_l:
                            try:
                                ns_info = ns_inst_info[ns["ns-id"]][connection[key]["location"]]
                            except KeyError:
                                ns_info = ns_inst_info[ns["ns-id"]]["Core"]
                            ns_data = {
                                "name": ns["ns-name"],
                                "location": ns["placement_loc"]["location"],
                                "vnf_list": ns_info["vnfr"],
                            }
                            key_data["ns"].append(ns_data)
                try:
                    key_data["pnf"] = connection[key]["pnf_list"]
                except KeyError:
                    pass
                if key_data:
                    data[key] = key_data
            if data:
                data["slice_sla"] = ems_radio_data
                data["slice_id"] = nest["_id"]
                for ems_id in ems_id_list:
                    messages = ems_messages.get(ems_id, [])
                    messages.append(data)
                    ems_messages[ems_id] = messages

        for ems_id, ems_message in ems_messages.items():
            # Find the EMS
            target_ems = mongoUtils.find("ems", {"id": ems_id})
            if not target_ems:
                # Error handling: There is no such EMS
                logger.error("EMS {} not found - No configuration".format(ems_id))
                continue
            target_ems_obj = pickle.loads(mongoUtils.find("ems_obj", {"id": ems_id})["obj"])
            # Send the message
            for imessage in ems_message:
                target_ems_obj.conf_radio(imessage)
            nest["conf_comp"]["ems"].append(ems_id)
        nest["ems_data"] = ems_messages
        nest["deployment_time"]["Radio_Configuration_Time"] = format(
            time.time() - radio_start_time, ".4f"
        )

    # *** STEP-4: Finalize ***
    logger.info(f"{nest['_id']} Status: Running")
    nest["status"] = "Running"
    nest["deployment_time"]["Slice_Deployment_Time"] = format(
        time.time() - nest["created_at"], ".4f"
    )
    mongoUtils.update("slice", nest["_id"], nest)
Exemple #9
0
def nest_mapping(req):
    """
    Function that maps nest to the underlying network functions
    """

    # Store the gst in DB
    mongoUtils.add("gst", req)

    nest = {"_id": req["_id"]}

    # Recreate the nest req

    # Check if the base_slice_des_ref or the required fields are set
    base_slice_des_ref = req["base_slice_descriptor"].get("base_slice_des_ref", None)
    if not base_slice_des_ref:
        for req_key in REQ_FIELDS:
            if req_key not in req["base_slice_descriptor"]:
                logger.error("Required field base_slice_descriptor.{} is missing".format(req_key))
                return (
                    "Error: Required field base_slice_descriptor.{} is missing".format(req_key),
                    400,
                )

    for field in NEST_FIELDS:
        req[field] = req.get(field, None)

    # ****** STEP 1: Slice Descriptor ******
    if not req["base_slice_descriptor"]:
        logger.error("No Base Slice Descriptor given - Exit")
        return "NEST Error: No Base Slice Descriptor given", 400
    req_slice_des = req["base_slice_descriptor"]
    # *** Recreate the NEST ***
    for req_key in SLICE_DES_OBJ:
        req_slice_des[req_key] = req_slice_des.get(req_key, None)
    for req_key in SLICE_DES_LIST:
        req_slice_des[req_key] = req_slice_des.get(req_key, [])

    # *** Check if there are references for slice ***
    if req_slice_des["base_slice_des_ref"]:
        ref_slice = mongoUtils.find(
            "base_slice_des_ref", {"base_slice_des_id": req_slice_des["base_slice_des_ref"]}
        )
        if ref_slice:
            for key, value in req_slice_des.items():
                try:
                    if value is None or value == []:
                        req_slice_des[key] = ref_slice[key]
                except KeyError:
                    continue
        else:
            logger.error(
                "slice_descriptor {} not found".format(req_slice_des["base_slice_des_ref"])
            )
            return "Error: referenced slice_descriptor not found", 400

    # Create the shared value
    nest["shared"] = {
        "isolation": req_slice_des["isolation"],
        "simultaneous_nsi": req_slice_des["simultaneous_nsi"],
    }

    # Check that the location in coverage field is registered
    not_supp_loc = []
    for location_id in req_slice_des["coverage"]:
        if not mongoUtils.find("location", {"id": location_id.lower()}):
            not_supp_loc.append(location_id)
            logger.warning(f"Location {location_id} is not registered")

    for location_id in not_supp_loc:
        req_slice_des["coverage"].remove(location_id)

    # *************************** Start the mapping ***************************
    # Currently supports:
    # 1) If delay_tolerance --> EMBB else --> URLLC
    #    If EMBB --> EPC Placement=@Core. If URLLC --> EPC Placement=@Edge
    # 2) If network throughput > 100 Mbps --> Type=5G
    # *************************************************************************
    functions_list = []

    if req_slice_des["network_DL_throughput"]["guaranteed"] > 100000:
        gen = 5
    else:
        gen = 4

    # *** Calculate the type of the slice (sst) ***
    if req_slice_des["delay_tolerance"]:
        # EMBB
        nest["sst"] = 1
        # Find the registered function for Core Function
        epc = mongoUtils.find("func", calc_find_data(gen, "core", 0))
        if not epc:
            return "Error: Not available Core Network Functions", 400
        # Check if the nest allows shareable functions, if the function is shareable
        if (
            req_slice_des["isolation"] != 1
            and req_slice_des["isolation"] != 3
            and epc["shared"]["availability"]
        ):
            found_list_key = None
            max_len = epc["shared"].get("max_shared", 0)
            for grouped_nest_key, grouped_nest_list in epc["shared"]["sharing_list"].items():
                if len(grouped_nest_list) < max_len or not max_len:
                    found_list_key = grouped_nest_key
                    grouped_nest_list.append(nest["_id"])
                    sharing_list = mongoUtils.get("sharing_lists", found_list_key)
                    sharing_list["nest_list"].append(nest["_id"])
                    mongoUtils.update("sharing_lists", found_list_key, sharing_list)
                    break
            if not found_list_key:
                found_list_key = str(uuid.uuid4())
                epc["shared"]["sharing_list"][found_list_key] = [nest["_id"]]
                data = {
                    "_id": found_list_key,
                    "nest_list": [nest["_id"]],
                    "ns_list": {},
                }
                mongoUtils.add("sharing_lists", data)
            nest["shared"]["core"] = {epc["_id"]: found_list_key}
        connections = []
        not_supp_loc = []
        for location in req_slice_des["coverage"]:
            enb = mongoUtils.find("func", calc_find_data(gen, location.lower(), 1))
            if not enb:
                not_supp_loc.append(location)
            else:
                # Check if the nest allows shareable functions, if the function is shareable
                if req_slice_des["isolation"] < 2 and enb["shared"]["availability"]:
                    found_list_key = None
                    max_len = enb["shared"].get("max_shared", 0)
                    for grouped_nest_key, grouped_nest_list in enb["shared"][
                        "sharing_list"
                    ].items():
                        if len(grouped_nest_list) < max_len or not max_len:
                            found_list_key = grouped_nest_key
                            grouped_nest_list.append(nest["_id"])
                            sharing_list = mongoUtils.get("sharing_lists", found_list_key)
                            sharing_list["nest_list"].append(nest["_id"])
                            mongoUtils.update("sharing_lists", found_list_key, sharing_list)
                            break
                    if not found_list_key:
                        found_list_key = str(uuid.uuid4())
                        enb["shared"]["sharing_list"][found_list_key] = [nest["_id"]]
                        data = {
                            "_id": found_list_key,
                            "nest_list": [nest["_id"]],
                            "ns_list": {},
                        }
                        mongoUtils.add("sharing_lists", data)
                    nest["shared"]["radio"] = nest["shared"].get("radio", {})
                    nest["shared"]["radio"][enb["_id"]] = found_list_key
                connections.append({"core": epc, "radio": enb})
                enb["tenants"].append(nest["_id"])
                mongoUtils.update("func", enb["_id"], enb)
                functions_list.append(enb["_id"])
        if not epc or not connections:
            return "Error: Not available Network Functions", 400
        epc["tenants"].append(nest["_id"])
        mongoUtils.update("func", epc["_id"], epc)
        functions_list.append(epc["_id"])
        for location in not_supp_loc:
            logger.warning(f"Location {location} not supported")
            req_slice_des["coverage"].remove(location)
    else:
        # URLLC
        nest["sst"] = 2
        connections = []
        not_supp_loc = []
        for location in req_slice_des["coverage"]:
            epc = mongoUtils.find("func", calc_find_data(gen, location.lower(), 0))
            enb = mongoUtils.find("func", calc_find_data(gen, location.lower(), 1))
            if not epc or not enb:
                not_supp_loc.append(location)
            else:
                # Check if the nest allows shareable functions, if the function is shareable
                # For the Core function
                if (
                    req_slice_des["isolation"] != 1
                    and req_slice_des["isolation"] != 3
                    and epc["shared"]["availability"]
                ):
                    found_list_key = None
                    max_len = epc["shared"].get("max_shared", 0)
                    for grouped_nest_key, grouped_nest_list in epc["shared"][
                        "sharing_list"
                    ].items():
                        if len(grouped_nest_list) < max_len or not max_len:
                            found_list_key = grouped_nest_key
                            grouped_nest_list.append(nest["_id"])
                            sharing_list = mongoUtils.get("sharing_lists", found_list_key)
                            sharing_list["nest_list"].append(nest["_id"])
                            mongoUtils.update("sharing_lists", found_list_key, sharing_list)
                            break
                    if not found_list_key:
                        found_list_key = str(uuid.uuid4())
                        epc["shared"]["sharing_list"][found_list_key] = [nest["_id"]]
                        data = {
                            "_id": found_list_key,
                            "nest_list": [nest["_id"]],
                            "ns_list": {},
                        }
                        mongoUtils.add("sharing_lists", data)
                    nest["shared"]["core"] = nest["shared"].get("core", {})
                    nest["shared"]["core"][epc["_id"]] = found_list_key
                # For the RAN function
                if req_slice_des["isolation"] < 2 and enb["shared"]["availability"]:
                    found_list_key = None
                    max_len = enb["shared"].get("max_shared", 0)
                    for grouped_nest_key, grouped_nest_list in enb["shared"][
                        "sharing_list"
                    ].items():
                        if len(grouped_nest_list) < max_len or not max_len:
                            found_list_key = grouped_nest_key
                            grouped_nest_list.append(nest["_id"])
                            sharing_list = mongoUtils.get("sharing_lists", found_list_key)
                            sharing_list["nest_list"].append(nest["_id"])
                            mongoUtils.update("sharing_lists", found_list_key, sharing_list)
                            break
                    if not found_list_key:
                        found_list_key = str(uuid.uuid4())
                        enb["shared"]["sharing_list"][found_list_key] = [nest["_id"]]
                        data = {
                            "_id": found_list_key,
                            "nest_list": [nest["_id"]],
                            "ns_list": {},
                        }
                        mongoUtils.add("sharing_lists", data)
                    nest["shared"]["radio"] = nest["shared"].get("radio", {})
                    nest["shared"]["radio"][enb["_id"]] = found_list_key
                connections.append({"core": epc, "radio": enb})
                epc["tenants"].append(nest["_id"])
                enb["tenants"].append(nest["_id"])
                mongoUtils.update("func", enb["_id"], enb)
                mongoUtils.update("func", epc["_id"], epc)
                functions_list.extend([epc["_id"], enb["_id"]])
        if not connections:
            return "Error: Not available Network Functions", 400
        for location in not_supp_loc:
            logger.warning(f"Location {location} not supported")
            req_slice_des["coverage"].remove(location)

    nest["connections"] = connections
    nest["functions"] = functions_list

    # Values to be copied to NEST
    KEYS_TO_BE_COPIED = (
        "network_DL_throughput",
        "ue_DL_throughput",
        "network_UL_throughput",
        "ue_UL_throughput",
        "group_communication_support",
        "mtu",
        "number_of_terminals",
        "positional_support",
        "radio_spectrum",
        "device_velocity",
        "terminal_density",
        "coverage",
    )
    for key in KEYS_TO_BE_COPIED:
        nest[key] = req_slice_des[key]

    # Add slice_name to NEST based on the base_slice_des id
    nest["slice_name"] = req_slice_des["base_slice_des_id"]

    # ****** STEP 2: Service Descriptor ******
    if req["service_descriptor"]:
        req_service_des = req["service_descriptor"]
        # *** Recreate the NEST ***
        for req_key in SERVICE_DES_OBJ:
            req_service_des[req_key] = req_service_des.get(req_key, None)
        for req_key in SERVICE_DES_LIST:
            req_service_des[req_key] = req_service_des.get(req_key, [])
        # Create the NS field on Nest
        nest["ns_list"] = req_service_des["ns_list"]

        # # Replace Placement with location in each NS
        # for ns in nest["ns_list"]:
        #     ns["placement"] = (
        #         lambda x: {"location": ["Core"]} if not x else
        #         {"location": req_slice_des["coverage"]})(ns["placement"])

    # ****** STEP 3: Test Descriptor ******
    if req["test_descriptor"]:
        req_test_des = req["test_descriptor"]
        # *** Recreate the NEST ***
        for req_key in TEST_DES_OBJ:
            req_test_des[req_key] = req_test_des.get(req_key, None)
        for req_key in TEST_DES_LIST:
            req_test_des[req_key] = req_test_des.get(req_key, [])
        # Create the Probe field on Nest
        nest["probe_list"] = req_test_des["probe_list"]

    if (
        not mongoUtils.find(
            "base_slice_des_ref",
            {"base_slice_des_id": req["base_slice_descriptor"]["base_slice_des_id"]},
        )
        and req["base_slice_descriptor"]["base_slice_des_id"]
    ):
        new_uuid = str(uuid.uuid4())
        req["base_slice_descriptor"]["_id"] = new_uuid
        mongoUtils.add("base_slice_des_ref", req["base_slice_descriptor"])
    return nest, 0
def ns_details(ns_list, edge_loc, vim_dict, total_ns_list):
    """
    Get details for the NS that are part of the slice
    A) Find the nsd details for each NS
    B) Replace placement value with location
    C) Get the VIM for each NS
    """
    pop_list = []
    for ns in ns_list:
        try:
            if ns["placement_loc"] and not ns["placement"]:
                # Placement to Core already done - go to next NS
                continue
            else:
                # Placement to Edge - Is already done - Need to make another ns
                new_ns = copy.deepcopy(ns)
        except KeyError:
            # No placement at all
            new_ns = ns
        # A) ****** Get the NSD ******
        # Search the nsd collection in Mongo for the nsd
        nsd = mongoUtils.find("nsd", {
            "nsd-id": new_ns["nsd-id"],
            "nfvo_id": new_ns["nfvo-id"]
        })
        if not nsd:
            # Bootstrap the NFVO to check for NSDs that are not in mongo
            # If again is not found, check if NS is optional.
            # If it is just remove it, else error
            nfvo_obj_json = mongoUtils.find("nfvo_obj",
                                            {"id": new_ns["nfvo-id"]})
            if not nfvo_obj_json:
                # Error handling: There is no OSM for that ns -
                # Stop and return
                logger.error("There is no NFVO with id {}".format(
                    new_ns["nfvo-id"]))
                return 1, []
            nfvo = pickle.loads(nfvo_obj_json["obj"])
            nfvo.bootstrapNfvo()
            nsd = mongoUtils.find("nsd", {
                "nsd-id": new_ns["nsd-id"],
                "nfvo_id": new_ns["nfvo-id"]
            })
            if not nsd and ns.get("optional", False):
                pop_list.append(ns)
                continue
            else:
                # Error handling: The ns is not optional and the nsd is not
                # on the NFVO - stop and return
                logger.error(
                    f"NSD {new_ns['nsd-id']} not found on OSM {new_ns['nfvo-id']}"
                )
                return 1, []
        new_ns["nsd-info"] = nsd
        # B) ****** Replace placement value with location info ******
        new_ns["placement_loc"] = (lambda x: {
            "location": "Core"
        } if not x else {
            "location": edge_loc
        })(new_ns["placement"])

        # C) ****** Get the VIM info ******
        new_ns["vims"] = []
        loc = new_ns["placement_loc"]["location"]
        get_vim = list(mongoUtils.find_all("vim", {"location": loc}))
        if not get_vim:
            # Error handling: There is no VIM at that location
            logger.error(f"VIM not found in location {loc}")
            return 1, []
        # TODO: Check the available resources and select vim
        # Temporary use the first element
        selected_vim = get_vim[0]["id"]
        new_ns["vims"].append(selected_vim)
        try:
            vim_dict[selected_vim]["ns_list"].append(new_ns["ns-name"])
            if new_ns["nfvo-id"] not in vim_dict[selected_vim]["nfvo_list"]:
                vim_dict[selected_vim]["nfvo_list"].append(new_ns["nfvo-id"])
        except KeyError:
            vim_dict[selected_vim] = {
                "ns_list": [new_ns["ns-name"]],
                "nfvo_list": [new_ns["nfvo-id"]],
            }
        resources = vim_dict[selected_vim].get("resources", {
            "memory-mb": 0,
            "vcpu-count": 0,
            "storage-gb": 0,
            "instances": 0
        })
        for key in resources:
            resources[key] += nsd["flavor"][key]
        vim_dict[selected_vim]["resources"] = resources
        new_ns["placement_loc"]["vim"] = selected_vim
        # 0) Create an uuid for the ns
        new_ns["ns-id"] = str(uuid.uuid4())
        total_ns_list.append(new_ns)
        del new_ns
    # Remove the ns that are optional and nsd was not found
    ns_list = [ns for ns in ns_list if ns not in pop_list]
    return 0, pop_list
def nest_mapping(req):
    """
    Function that maps nest to the underlying network functions
    """
    # Store the gst in DB
    mongoUtils.add("gst", req)

    nest = {"_id": req["_id"]}

    # Recreate the nest req
    for field in NEST_FIELDS:
        req[field] = req.get(field, None)

    # ****** STEP 1: Slice Descriptor ******
    if not req["base_slice_descriptor"]:
        logger.error("No Base Slice Descriptor given - Exit")
        return "NEST Error: No Base Slice Descriptor given", 400
    req_slice_des = req["base_slice_descriptor"]
    # *** Recreate the NEST ***
    for req_key in SLICE_DES_OBJ:
        req_slice_des[req_key] = req_slice_des.get(req_key, None)
    for req_key in SLICE_DES_LIST:
        req_slice_des[req_key] = req_slice_des.get(req_key, [])

    # *** Check if there are references for slice ***
    if req_slice_des["base_slice_des_ref"]:
        ref_slice = mongoUtils.find(
            "base_slice_des_ref", {"base_slice_des_id": req_slice_des["base_slice_des_ref"]}
        )
        if ref_slice:
            for key, value in req_slice_des.items():
                try:
                    if value is None:
                        req_slice_des[key] = ref_slice[key]
                except KeyError:
                    continue
        else:
            logger.error(
                "slice_descriptor {} not found".format(req_slice_des["base_slice_des_ref"])
            )
            return "Error: referenced slice_descriptor not found", 400

    # *************************** Start the mapping ***************************
    # Currently supports:
    # 1) If delay_tolerance --> EMBB else --> URLLC
    #    If EMBB --> EPC Placement=@Core. If URLLC --> EPC Placement=@Edge
    # 2) If network throughput > 100 Mbps --> Type=5G
    # *************************************************************************
    functions_list = []

    if req_slice_des["network_DL_throughput"]["guaranteed"] > 100000:
        gen = 5
    else:
        gen = 4

    # *** Calculate the type of the slice (sst) ***
    if req_slice_des["delay_tolerance"]:
        # EMBB
        nest["sst"] = 1
        epc = mongoUtils.find("func", calc_find_data(gen, "Core", 0))
        if not epc:
            return "Error: Not available Core Network Functions", 400
        connections = []
        not_supp_loc = []
        for location in req_slice_des["coverage"]:
            enb = mongoUtils.find("func", calc_find_data(gen, location, 1))
            if not enb:
                not_supp_loc.append(location)
            else:
                connections.append({"core": epc, "radio": enb})
                enb["tenants"].append(nest["_id"])
                mongoUtils.update("func", enb["_id"], enb)
                functions_list.append(enb["_id"])
        if not epc or not connections:
            return "Error: Not available Network Functions", 400
        epc["tenants"].append(nest["_id"])
        mongoUtils.update("func", epc["_id"], epc)
        functions_list.append(epc["_id"])
        for location in not_supp_loc:
            logger.warning(f"Location {location} not supported")
            req_slice_des["coverage"].remove(location)
    else:
        # URLLC
        nest["sst"] = 2
        connections = []
        not_supp_loc = []
        for location in req_slice_des["coverage"]:
            epc = mongoUtils.find("func", calc_find_data(gen, location, 0))
            enb = mongoUtils.find("func", calc_find_data(gen, location, 1))
            if not epc or not enb:
                not_supp_loc.append(location)
            else:
                connections.append({"core": epc, "radio": enb})
                epc["tenants"].append(nest["_id"])
                enb["tenants"].append(nest["_id"])
                mongoUtils.update("func", enb["_id"], enb)
                mongoUtils.update("func", epc["_id"], epc)
                functions_list.extend([epc["_id"], enb["_id"]])
        if not connections:
            return "Error: Not available Network Functions", 400
        for location in not_supp_loc:
            logger.warning(f"Location {location} not supported")
            req_slice_des["coverage"].remove(location)

    nest["connections"] = connections
    nest["functions"] = functions_list

    # Values to be copied to NEST
    KEYS_TO_BE_COPIED = (
        "network_DL_throughput",
        "ue_DL_throughput",
        "network_UL_throughput",
        "ue_UL_throughput",
        "group_communication_support",
        "mtu",
        "number_of_terminals",
        "positional_support",
        "radio_spectrum",
        "device_velocity",
        "terminal_density",
        "coverage",
    )
    for key in KEYS_TO_BE_COPIED:
        nest[key] = req_slice_des[key]

    # Create the shared value
    nest["shared"] = {
        "isolation": req_slice_des["isolation_level"],
        "simultaneous_nsi": req_slice_des["simultaneous_nsi"],
    }

    # ****** STEP 2: Service Descriptor ******
    if req["service_descriptor"]:
        req_service_des = req["service_descriptor"]
        # *** Recreate the NEST ***
        for req_key in SERVICE_DES_OBJ:
            req_service_des[req_key] = req_service_des.get(req_key, None)
        for req_key in SERVICE_DES_LIST:
            req_service_des[req_key] = req_service_des.get(req_key, [])
        # Create the NS field on Nest
        nest["ns_list"] = req_service_des["ns_list"]

        # # Replace Placement with location in each NS
        # for ns in nest["ns_list"]:
        #     ns["placement"] = (
        #         lambda x: {"location": ["Core"]} if not x else
        #         {"location": req_slice_des["coverage"]})(ns["placement"])

    # ****** STEP 3: Service Descriptor ******
    if req["test_descriptor"]:
        req_test_des = req["test_descriptor"]
        # *** Recreate the NEST ***
        for req_key in TEST_DES_OBJ:
            req_test_des[req_key] = req_test_des.get(req_key, None)
        for req_key in TEST_DES_LIST:
            req_test_des[req_key] = req_test_des.get(req_key, [])
        # Create the Probe field on Nest
        nest["probe_list"] = req_test_des["probe_list"]

    if not mongoUtils.find(
        "base_slice_des_ref",
        {"base_slice_des_id": req["base_slice_descriptor"]["base_slice_des_id"]},
    ):
        new_uuid = str(uuid.uuid4())
        req["base_slice_descriptor"]["_id"] = new_uuid
        mongoUtils.add("base_slice_des_ref", req["base_slice_descriptor"])
    return nest, 0
Exemple #12
0
    def post(self):
        """
        Add a new vim. The request must provide the vim details.
        used by: `katana vim add -f [file]`
        """
        new_uuid = str(uuid.uuid4())
        request.json["_id"] = new_uuid
        request.json["created_at"] = time.time()  # unix epoch
        request.json["tenants"] = {}

        # Check the required fields
        try:
            username = request.json["username"]
            password = request.json["password"]
            auth_url = request.json["auth_url"]
            project_name = request.json["admin_project_name"]
            location_id = request.json["location"].lower()
            request.json["location"] = location_id
            vim_id = request.json["id"]
        except KeyError:
            return f"Error: Required fields: {self.req_fields}", 400

        # Check that the VIM location is registered
        location = mongoUtils.find("location", {"id": location_id})
        if not location:
            return f"Location {location_id} is not registered. Please add the location first", 400
        location["vims"].append(vim_id)
        # Type of OpenStack
        if request.json["type"] == "openstack":
            try:
                new_vim = openstackUtils.Openstack(
                    uuid=new_uuid,
                    auth_url=auth_url,
                    project_name=project_name,
                    username=username,
                    password=password,
                )
                if new_vim.auth_error:
                    raise (AttributeError)
            except AttributeError:
                return "Error: VIM Error", 400
            else:
                request.json["resources"] = new_vim.get_resources()
                thebytes = pickle.dumps(new_vim)
                obj_json = {"_id": new_uuid, "id": request.json["id"], "obj": Binary(thebytes)}
                try:
                    vim_monitoring = request.json["infrastructure_monitoring"]
                except KeyError:
                    pass
                else:
                    with open("/targets/vim_targets.json", mode="r") as prom_file:
                        prom = json.load(prom_file)
                        prom.append({"targets": [vim_monitoring], "labels": {}})
                    with open("/targets/vim_targets.json", mode="w") as prom_file:
                        json.dump(prom, prom_file)
        # Type of OpenNebula
        elif request.json["type"] == "opennebula":
            try:
                new_vim = opennebulaUtils.Opennebula(
                    uuid=new_uuid,
                    auth_url=auth_url,
                    project_name=project_name,
                    username=username,
                    password=password,
                )
            except AttributeError:
                return "Error: VIM Error", 400
            else:
                request.json["resources"] = {"N/A": "N/A"}
                thebytes = pickle.dumps(new_vim)
                obj_json = {"_id": new_uuid, "id": request.json["id"], "obj": Binary(thebytes)}
        else:
            response = dumps({"error": "This type VIM is not supported"})
            return response, 400
        try:
            new_uuid = mongoUtils.add("vim", request.json)
        except pymongo.errors.DuplicateKeyError:
            return f"VIM with id {vim_id} already exists", 400
        mongoUtils.add("vim_obj", obj_json)
        if location:
            mongoUtils.update("location", location["_id"], location)
        return new_uuid, 201