Minor fix regarding "related" in WIM
[osm/RO.git] / osm_ro / wim / engine.py
index fcb3477..3fdd032 100644 (file)
@@ -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"""
@@ -271,16 +322,16 @@ class WimEngine(object):
             tenant (str): UUID of the OSM tenant
 
         Returns:
-            str: UUID of the WIM account that is able to connect all the
+            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)['uuid']
+        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"""
         if sce_net_id in wim_usage:
             account_id = wim_usage[sce_net_id]
@@ -296,7 +347,8 @@ class WimEngine(object):
             '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, wim_usage, networks, tenant=None):
@@ -305,7 +357,7 @@ class WimEngine(object):
         (NSR).
 
         Arguments:
-            wim_usage(dict): Mapping between sce_net_id and wim_id
+            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.
@@ -315,6 +367,9 @@ class WimEngine(object):
             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)
@@ -323,11 +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(wim_usage,
-                                 key[0], key[1], grouped_networks[key], tenant)
-            for key in wan_groups
+                                 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):
@@ -460,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')