X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=osm_ro%2Fwim%2Fengine.py;h=3fdd03244f125abdde809788deb62cf134d1a4a0;hb=refs%2Fchanges%2F99%2F7599%2F2;hp=dde5dee4a6d66a2ab2f388218773fd7d61cbca19;hpb=c5293def02d95ed4ee086dd8842437b76ec05c4e;p=osm%2FRO.git diff --git a/osm_ro/wim/engine.py b/osm_ro/wim/engine.py index dde5dee4..3fdd0324 100644 --- a/osm_ro/wim/engine.py +++ b/osm_ro/wim/engine.py @@ -48,12 +48,17 @@ import logging from contextlib import contextmanager from itertools import groupby from operator import itemgetter +from sys import exc_info from uuid import uuid4 +from six import reraise + from ..utils import remove_none_items from .actions import Action from .errors import ( + DbBaseException, NoWimConnectedToDatacenters, + UnexpectedDatabaseError, WimAccountNotActive ) from .wim_thread import WimThread @@ -76,10 +81,29 @@ class WimEngine(object): Please check the wim schema to have more information about ``properties``. + The ``config`` property might contain a ``wim_port_mapping`` dict, + In this case, the method ``create_wim_port_mappings`` will be + automatically invoked. + Returns: str: uuid of the newly created WIM record """ - return self.persist.create_wim(properties) + port_mapping = ((properties.get('config', {}) or {}) + .pop('wim_port_mapping', {})) + uuid = self.persist.create_wim(properties) + + if port_mapping: + try: + self.create_wim_port_mappings(uuid, port_mapping) + except DbBaseException: + # Rollback + self.delete_wim(uuid) + ex = UnexpectedDatabaseError('Failed to create port mappings' + 'Rolling back wim creation') + self.logger.exception(str(ex)) + reraise(ex.__class__, ex, exc_info()[2]) + + return uuid def get_wim(self, uuid_or_name, tenant_id=None): """Retrieve existing WIM record by name or id. @@ -95,8 +119,35 @@ class WimEngine(object): ``properties`` is a dictionary with the properties being changed, if a property is not present, the old value will be preserved + + Similarly to create_wim, the ``config`` property might contain a + ``wim_port_mapping`` dict, In this case, port mappings will be + automatically updated. """ - return self.persist.update_wim(uuid_or_name, properties) + port_mapping = ((properties.get('config', {}) or {}) + .pop('wim_port_mapping', {})) + orig_props = self.persist.get_by_name_or_uuid('wims', uuid_or_name) + uuid = orig_props['uuid'] + + response = self.persist.update_wim(uuid, properties) + + if port_mapping: + try: + # It is very complex to diff and update individually all the + # port mappings. Therefore a practical approach is just delete + # and create it again. + self.persist.delete_wim_port_mappings(uuid) + # ^ Calling from persistence avoid reloading twice the thread + self.create_wim_port_mappings(uuid, port_mapping) + except DbBaseException: + # Rollback + self.update_wim(uuid_or_name, orig_props) + ex = UnexpectedDatabaseError('Failed to update port mappings' + 'Rolling back wim updates\n') + self.logger.exception(str(ex)) + reraise(ex.__class__, ex, exc_info()[2]) + + return response def delete_wim(self, uuid_or_name): """Kill the corresponding wim threads and erase the WIM record""" @@ -247,7 +298,7 @@ class WimEngine(object): """Find a single WIM that is able to connect all the datacenters listed - Raises + Raises: NoWimConnectedToDatacenters: if no WIM connected to all datacenters at once is found """ @@ -261,37 +312,64 @@ class WimEngine(object): # used here) return suitable_wim_ids[0] + def find_suitable_wim_account(self, datacenter_ids, tenant): + """Find a WIM account that is able to connect all the datacenters + listed + + Arguments: + datacenter_ids (list): List of UUIDs of all the datacenters (vims), + that need to be connected. + tenant (str): UUID of the OSM tenant + + Returns: + object with the WIM account that is able to connect all the + datacenters. + """ + wim_id = self.find_common_wim(datacenter_ids, tenant) + return self.persist.get_wim_account_by(wim_id, tenant) + def derive_wan_link(self, + wim_usage, instance_scenario_id, sce_net_id, - networks, tenant): + networks, tenant, related=None): """Create a instance_wim_nets record for the given information""" - datacenters = [n['datacenter_id'] for n in networks] - wim_id = self.find_common_wim(datacenters, tenant) - - account = self.persist.get_wim_account_by(wim_id, tenant) + if sce_net_id in wim_usage: + account_id = wim_usage[sce_net_id] + account = self.persist.get_wim_account_by(uuid=account_id) + wim_id = account['wim_id'] + else: + datacenters = [n['datacenter_id'] for n in networks] + wim_id = self.find_common_wim(datacenters, tenant) + account = self.persist.get_wim_account_by(wim_id, tenant) return { 'uuid': str(uuid4()), 'instance_scenario_id': instance_scenario_id, 'sce_net_id': sce_net_id, 'wim_id': wim_id, - 'wim_account_id': account['uuid'] + 'wim_account_id': account['uuid'], + 'related': related } - def derive_wan_links(self, networks, tenant=None): + def derive_wan_links(self, wim_usage, networks, tenant=None): """Discover and return what are the wan_links that have to be created considering a set of networks (VLDs) required for a scenario instance (NSR). Arguments: + wim_usage(dict): Mapping between sce_net_id and wim_id. If wim_id is False, means not create wam_links networks(list): Dicts containing the information about the networks that will be instantiated to materialize a Network Service (scenario) instance. + Corresponding to the ``instance_net`` record. Returns: list: list of WAN links to be written to the database """ # Group networks by key=(instance_scenario_id, sce_net_id) + related = None + if networks: + related = networks[0].get("related") filtered = _filter_multi_vim(networks) grouped_networks = _group_networks(filtered) datacenters_per_group = _count_datacenters(grouped_networks) @@ -300,10 +378,11 @@ class WimEngine(object): wan_groups = [key for key, counter in datacenters_per_group if counter > 1] - + # Keys are tuples(instance_scenario_id, sce_net_id) return [ - self.derive_wan_link(key[0], key[1], grouped_networks[key], tenant) - for key in wan_groups + self.derive_wan_link(wim_usage, + key[0], key[1], grouped_networks[key], tenant, related) + for key in wan_groups if wim_usage.get(key[1]) is not False ] def create_action(self, wan_link): @@ -436,7 +515,7 @@ def _group_networks(networks): (scenario) instance. Returns: dict: Keys are tuples (instance_scenario_id, sce_net_id) and values - are lits of networks. + are list of networks. """ criteria = itemgetter('instance_scenario_id', 'sce_net_id')