From: gifrerenom Date: Mon, 7 Mar 2022 18:05:29 +0000 (+0000) Subject: Fix bug 1900 to populate WIM parameters and auto-select WIM when needed. X-Git-Tag: v10.1.1-rc1~3 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F33%2F11733%2F3;p=osm%2FLCM.git Fix bug 1900 to populate WIM parameters and auto-select WIM when needed. Change-Id: I784efbdd5051ae3e11d8d28638c464ef36ddee25 Signed-off-by: gifrerenom --- diff --git a/osm_lcm/data_utils/database/wim_account.py b/osm_lcm/data_utils/database/wim_account.py new file mode 100644 index 0000000..8b0b5f6 --- /dev/null +++ b/osm_lcm/data_utils/database/wim_account.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# This file is part of OSM Life-Cycle Management module +# +# Copyright 2022 ETSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## + +from osm_lcm.data_utils.database.database import Database + +__author__ = ( + "Lluis Gifre , Ricard Vilalta " +) + + +class WimAccountDB: + db = None + db_wims = {} + + def initialize_db(): + WimAccountDB.db = Database().instance.db + + def get_wim_account_with_id(wim_account_id): + if not WimAccountDB.db: + WimAccountDB.initialize_db() + if wim_account_id in WimAccountDB.db_wims: + return WimAccountDB.db_wims[wim_account_id] + db_wim = WimAccountDB.db.get_one("wim_accounts", {"_id": wim_account_id}) or {} + WimAccountDB.db_wims[wim_account_id] = db_wim + return db_wim + + def get_all_wim_accounts(): + if not WimAccountDB.db: + WimAccountDB.initialize_db() + db_wims_list = WimAccountDB.db.get_list("wim_accounts") + WimAccountDB.db_wims.update({db_wim["_id"]: db_wim for db_wim in db_wims_list}) + return WimAccountDB.db_wims diff --git a/osm_lcm/data_utils/vim.py b/osm_lcm/data_utils/vim.py index 0e69572..15ca93f 100644 --- a/osm_lcm/data_utils/vim.py +++ b/osm_lcm/data_utils/vim.py @@ -21,3 +21,29 @@ # For those usages not covered by the Apache License, Version 2.0 please # contact: fbravo@whitestack.com ## + +from osm_lcm.data_utils.database.vim_account import VimAccountDB + +__author__ = ( + "Lluis Gifre , Ricard Vilalta " +) + + +def get_vims_to_connect(db_nsr, db_vnfrs, target_vld): + vims_to_connect = set() + vld = next( + (vld for vld in db_nsr["vld"] if vld["id"] == target_vld["id"]), + None, + ) + if vld is None: + return vims_to_connect # VLD not in NS, means it is an internal VLD within a single VIM + # iterate over VNFs and retrieve name of VIMs they are planned to be deployed to + for vld_member_vnf_index_ref in vld["vnfd-connection-point-ref"]: + vld_member_vnf_index_ref = vld_member_vnf_index_ref["member-vnf-index-ref"] + db_vim = VimAccountDB.get_vim_account_with_id( + db_vnfrs[vld_member_vnf_index_ref]["vim-account-id"] + ) + if db_vim is None: + continue + vims_to_connect.add(db_vim["name"]) + return vims_to_connect diff --git a/osm_lcm/data_utils/wim.py b/osm_lcm/data_utils/wim.py new file mode 100644 index 0000000..9a875b3 --- /dev/null +++ b/osm_lcm/data_utils/wim.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- + +# This file is part of OSM Life-Cycle Management module +# +# Copyright 2022 ETSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +## + + +from osm_lcm.data_utils.database.vim_account import VimAccountDB +from osm_lcm.data_utils.database.wim_account import WimAccountDB +from osm_lcm.data_utils.vim import get_vims_to_connect +from osm_lcm.lcm_utils import LcmException + +__author__ = ( + "Lluis Gifre , Ricard Vilalta " +) + + +def get_candidate_wims(vims_to_connect): + all_wim_accounts = WimAccountDB.get_all_wim_accounts() + candidate_wims = {} + for wim_id, db_wim in all_wim_accounts.items(): + wim_port_mapping = db_wim.get("config", {}).get("wim_port_mapping", []) + wim_dc_ids = { + m.get("datacenter_id") for m in wim_port_mapping if m.get("datacenter_id") + } + not_reachable_vims = vims_to_connect.difference(wim_dc_ids) + if len(not_reachable_vims) > 0: + continue + # TODO: consider adding other filtering fields such as supported layer(s) [L2, L3, ...] + candidate_wims[wim_id] = db_wim + return candidate_wims + + +def select_feasible_wim_account(db_nsr, db_vnfrs, target_vld, vld_params, logger): + logger.info("Checking if WIM is needed for VLD({:s})...".format(str(target_vld))) + if target_vld.get("mgmt-network", False): + logger.info( + "WIM not needed, VLD({:s}) is a management network".format(str(target_vld)) + ) + return None, None # assume mgmt networks do not use a WIM + # find VIMs to be connected by VLD + vims_to_connect = get_vims_to_connect(db_nsr, db_vnfrs, target_vld) + # check if we need a WIM to interconnect the VNFs in different VIMs + if len(vims_to_connect) < 2: + logger.info( + "WIM not needed, VLD({:s}) does not involve multiple VIMs".format( + str(target_vld) + ) + ) + return None, None + # if more than one VIM needs to be connected... + logger.info( + "WIM is needed, multiple VIMs to interconnect: {:s}".format( + str(vims_to_connect) + ) + ) + # find a WIM having these VIMs on its wim_port_mapping setting + candidate_wims = get_candidate_wims(vims_to_connect) + logger.info("Candidate WIMs: {:s}".format(str(candidate_wims))) + + # check if a desired wim_account_id is specified in vld_params + wim_account_id = vld_params.get("wimAccountId") + if wim_account_id: + # check if the desired WIM account is feasible + # implicitly checks if it exists in the DB + db_wim = candidate_wims.get(wim_account_id) + if db_wim: + return wim_account_id, db_wim + msg = ( + "WimAccountId specified in VldParams({:s}) cannot be used " + "to connect the required VIMs({:s}). Candidate WIMs are: {:s}" + ) + raise LcmException( + msg.format(str(vld_params), str(vims_to_connect), str(candidate_wims)) + ) + + # TODO: sort feasible WIMs based on some meaningful criteria, and select best one + wim_account_id = next(iter(candidate_wims.keys()), None) + db_wim = candidate_wims.get(wim_account_id) + return wim_account_id, db_wim + + +def get_target_wim_attrs(nsr_id, target_vld, vld_params): + target_vims = [ + "vim:{:s}".format(vim_id) for vim_id in vld_params["vim-network-name"] + ] + wim_vld = "nsrs:{}:vld.{}".format(nsr_id, target_vld["id"]) + vld_type = target_vld.get("type") + if vld_type is None: + vld_type = "ELAN" if len(target_vims) > 2 else "ELINE" + target_wim_attrs = { + "sdn": True, + "target_vims": target_vims, + "vlds": [wim_vld], + "type": vld_type, + } + return target_wim_attrs + + +def get_sdn_ports(vld_params, db_wim): + if vld_params.get("provider-network"): + # if SDN ports are specified in VLD params, use them + return vld_params["provider-network"].get("sdn-ports") + + # otherwise, compose SDN ports required + wim_port_mapping = db_wim.get("config", {}).get("wim_port_mapping", []) + sdn_ports = [] + for vim_id in vld_params["vim-network-name"]: + db_vim = VimAccountDB.get_vim_account_with_id(vim_id) + vim_name = db_vim["name"] + mapping = next( + (m for m in wim_port_mapping if m["datacenter_id"] == vim_name), + None, + ) + if mapping is None: + msg = "WIM({:s},{:s}) does not specify a mapping for VIM({:s},{:s})" + raise LcmException( + msg.format( + db_wim["name"], + db_wim["_id"], + db_vim["name"], + db_vim["_id"], + ) + ) + sdn_port = { + "device_id": vim_name, + "switch_id": mapping.get("device_id"), + "switch_port": mapping.get("device_interface_id"), + "service_endpoint_id": mapping.get("service_endpoint_id"), + } + service_mapping_info = mapping.get("service_mapping_info", {}) + encapsulation = service_mapping_info.get("encapsulation", {}) + if encapsulation.get("type"): + sdn_port["service_endpoint_encapsulation_type"] = encapsulation["type"] + if encapsulation.get("vlan"): + sdn_port["vlan"] = encapsulation["vlan"] + sdn_ports.append(sdn_port) + return sdn_ports diff --git a/osm_lcm/ns.py b/osm_lcm/ns.py index 3eca8aa..8b9a99a 100644 --- a/osm_lcm/ns.py +++ b/osm_lcm/ns.py @@ -70,6 +70,11 @@ from osm_common.fsbase import FsException from osm_lcm.data_utils.database.database import Database from osm_lcm.data_utils.filesystem.filesystem import Filesystem +from osm_lcm.data_utils.wim import ( + get_sdn_ports, + get_target_wim_attrs, + select_feasible_wim_account, +) from n2vc.n2vc_juju_conn import N2VCJujuConnector from n2vc.exceptions import N2VCException, N2VCNotFound, K8sException @@ -806,9 +811,30 @@ class NsLcm(LcmBase): target_vld["vim_info"][target_sdn]["sdn-ports"] = vld_params[ "provider-network" ]["sdn-ports"] - if vld_params.get("wimAccountId"): - target_wim = "wim:{}".format(vld_params["wimAccountId"]) - target_vld["vim_info"][target_wim] = {} + + # check if WIM is needed; if needed, choose a feasible WIM able to connect VIMs + # if wim_account_id is specified in vld_params, validate if it is feasible. + wim_account_id, db_wim = select_feasible_wim_account( + db_nsr, db_vnfrs, target_vld, vld_params, self.logger + ) + + if wim_account_id: + # WIM is needed and a feasible one was found, populate WIM target and SDN ports + self.logger.info("WIM selected: {:s}".format(str(wim_account_id))) + # update vld_params with correct WIM account Id + vld_params["wimAccountId"] = wim_account_id + + target_wim = "wim:{}".format(wim_account_id) + target_wim_attrs = get_target_wim_attrs(nsr_id, target_vld, vld_params) + sdn_ports = get_sdn_ports(vld_params, db_wim) + if len(sdn_ports) > 0: + target_vld["vim_info"][target_wim] = target_wim_attrs + target_vld["vim_info"][target_wim]["sdn-ports"] = sdn_ports + + self.logger.debug( + "Target VLD with WIM data: {:s}".format(str(target_vld)) + ) + for param in ("vim-network-name", "vim-network-id"): if vld_params.get(param): if isinstance(vld_params[param], dict):