CORD-1891: Fix dependency issue breaking slice deletion

This fix adds back some code that was lost in a rebase of a previous
patch: https://gerrit.opencord.org/#/c/5486/3..4/xos/synchronizers/new_base/event_loop.py

Change-Id: I2cd1d7ba5f8a0e575f1f2cb0723029a4164c8d52
(cherry picked from commit b497bfbe28683111bd6561af8fcad292f1c8d3f6)
diff --git a/xos/synchronizers/new_base/event_loop.py b/xos/synchronizers/new_base/event_loop.py
index 4e392bb..7841d5d 100644
--- a/xos/synchronizers/new_base/event_loop.py
+++ b/xos/synchronizers/new_base/event_loop.py
@@ -444,37 +444,35 @@
             paths = all_shortest_paths(G, m1, m2)
         except NetworkXNoPath:
             # Easy. The two models are unrelated.
-            return False
+            return False, None
 
         for p in paths:
-            path_verdict = True
             src_object = o1
-            da = None
 
             for i in range(len(p) - 1):
                 src = p[i]
                 dst = p[i + 1]
                 edge_label = G[src][dst]
                 sa = edge_label['src_accessor']
-                da = edge_label['dst_accessor']
                 try:
                     dst_object = getattr(src_object, sa)
-                    if dst_object and dst_object.leaf_model_name != dst and i != len(
-                            p) - 2:
-                        raise AttributeError
                 except AttributeError as e:
                     self.log.debug(
                         'Could not check object dependencies, making conservative choice', src_object=src_object, sa=sa, o1=o1, o2=o2)
-                    return True
+                    return True, DIRECT_EDGE
+
                 src_object = dst_object
 
-            if src_object and ((not da and src_object == o2) or (
-                    da and src_object == getattr(o2, da))):
-                return True
+                if not src_object:
+                    break
+
+            verdict, edge_type = self.same_object(src_object, o2)
+            if verdict:
+                return verdict, edge_type
 
             # Otherwise try other paths
 
-        return False
+        return False, None
 
     """
 
@@ -501,8 +499,6 @@
         r = range(n)
         indexed_objects = zip(r, objects)
 
-        mG = self.model_dependency_graph[deletion]
-
         oG = DiGraph()
 
         for i in r:
@@ -512,11 +508,21 @@
             for i0 in range(n):
                 for i1 in range(n):
                     if i0 != i1:
-                        if not deletion and self.concrete_path_exists(
-                                objects[i0], objects[i1]):
-                            oG.add_edge(i0, i1)
-                        elif deletion and self.concrete_path_exists(objects[i1], objects[i0]):
-                            oG.add_edge(i0, i1)
+                        if deletion:
+                            path_args = (objects[i1], objects[i0])
+                        else:
+                            path_args = (objects[i0], objects[i1])
+
+                        is_connected, edge_type = self.concrete_path_exists(
+                            *path_args)
+                        if is_connected:
+                            try:
+                                edge_type = oG[i1][i0]['type']
+                                if edge_type == PROXY_EDGE:
+                                    oG.remove_edge(i1, i0)
+                                    oG.add_edge(i0, i1, {'type': edge_type})
+                            except KeyError:
+                                oG.add_edge(i0, i1, {'type': edge_type})
         except KeyError:
             pass
 
diff --git a/xos/synchronizers/new_base/tests/model-deps b/xos/synchronizers/new_base/tests/model-deps
new file mode 100644
index 0000000..75a60e0
--- /dev/null
+++ b/xos/synchronizers/new_base/tests/model-deps
@@ -0,0 +1,665 @@
+{ 
+     
+    
+    "User": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerUser", "controllerusers", "user"],
+        
+        
+        ["Site", "site", "users"],
+        ["DashboardView", "dashboards", "user"]
+        
+    ], 
+    
+    "Privilege": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerPrivilege", "controllerprivileges", "privilege"]
+        
+        
+        
+    ], 
+    
+    "AddressPool": [
+        
+        
+        
+        
+        
+        ["Service", "service", "addresspools"]
+        
+    ], 
+     
+    
+    "ControllerDashboardView": [
+        
+        
+        
+        
+        
+        ["Controller", "controller", "controllerdashboardviews"],
+        ["DashboardView", "dashboardView", "controllerdashboardviews"]
+        
+    ], 
+    
+    "ControllerImages": [
+        
+        
+        
+        
+        
+        ["Image", "image", "controllerimages"],
+        ["Controller", "controller", "controllerimages"]
+        
+    ], 
+    
+    "ControllerNetwork": [
+        
+        
+        
+        
+        
+        ["Network", "network", "controllernetworks"],
+        ["Controller", "controller", "controllernetworks"]
+        
+    ], 
+    
+    "ControllerRole": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "ControllerSite": [
+        
+        
+        
+        
+        
+        ["Site", "site", "controllersite"],
+        ["Controller", "controller", "controllersite"]
+        
+    ], 
+    
+    "ControllerPrivilege": [
+        
+        
+        
+        
+        
+        ["Controller", "controller", "controllerprivileges"],
+        ["Privilege", "privilege", "controllerprivileges"]
+        
+    ], 
+    
+    "ControllerSitePrivilege": [
+        
+        
+        
+        
+        
+        ["Controller", "controller", "controllersiteprivileges"],
+        ["SitePrivilege", "site_privilege", "controllersiteprivileges"]
+        
+    ], 
+    
+    "ControllerSlice": [
+        
+        
+        
+        
+        
+        ["Controller", "controller", "controllerslices"],
+        ["Slice", "slice", "controllerslices"]
+        
+    ], 
+    
+    "ControllerSlicePrivilege": [
+        
+        
+        
+        
+        
+        ["Controller", "controller", "controllersliceprivileges"],
+        ["SlicePrivilege", "slice_privilege", "controllersliceprivileges"]
+        
+    ], 
+    
+    "ControllerUser": [
+        
+        
+        
+        
+        
+        ["User", "user", "controllerusers"],
+        ["Controller", "controller", "controllersusers"]
+        
+    ], 
+    
+    "DashboardView": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerDashboardView", "controllerdashboardviews", "dashboardView"],
+        
+        
+        ["Controller", "controllers", "dashboardviews"],
+        ["Deployment", "deployments", "dashboardviews"]
+        
+    ], 
+    
+    "Deployment": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "DeploymentPrivilege": [
+        
+        
+        
+        
+        
+        ["User", "user", "deploymentprivileges"],
+        ["Deployment", "deployment", "deploymentprivileges"],
+        ["DeploymentRole", "role", "deploymentprivileges"]
+        
+    ], 
+    
+    "DeploymentRole": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Diag": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Flavor": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Image": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerImages", "controllerimages", "image"]
+        
+        
+        
+    ], 
+    
+    "ImageDeployments": [
+        
+        
+        
+        
+        
+        ["Image", "image", "imagedeployments"],
+        ["Deployment", "deployment", "imagedeployments"]
+        
+    ], 
+    
+    "Instance": [
+        
+        
+        
+        
+        
+        ["Image", "image", "instances"],
+        ["User", "creator", "instances"],
+        ["Slice", "slice", "instances"],
+        ["Deployment", "deployment", "instance_deployment"],
+        ["Node", "node", "instances"],
+        ["Flavor", "flavor", "instance"],
+        ["Instance", "parent", "instance"]
+        
+    ], 
+    
+    "Network": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerNetwork", "controllernetworks", "network"],
+        
+        
+        ["NetworkTemplate", "template", "network"],
+        ["Slice", "owner", "ownedNetworks"],
+        ["Slice", "permitted_slices", "availableNetworks"]
+    ], 
+    
+    "NetworkParameter": [
+        
+        
+        
+        
+        
+        ["NetworkParameterType", "parameter", "networkparameters"]
+        
+    ], 
+    
+    "NetworkParameterType": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "NetworkSlice": [
+        
+        
+        
+        
+        
+        ["Network", "network", "networkslices"],
+        ["Slice", "slice", "networkslices"]
+        
+    ], 
+    
+    "NetworkTemplate": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Node": [
+        
+        
+        
+        
+        
+        ["SiteDeployment", "site_deployment", "nodes"]
+        
+    ], 
+    
+    "NodeLabel": [
+        
+        
+        
+        
+        
+        ["Node", "node", "nodelabels"]
+        
+    ], 
+    
+    "Port": [
+        
+        
+        
+        
+        
+        ["Network", "network", "links"],
+        ["Instance", "instance", "ports"]
+        
+    ], 
+    
+    "Role": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+    ], 
+    
+    "Service": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "ServiceAttribute": [
+        
+        
+        
+        
+        
+        ["Service", "service", "serviceattributes"]
+        
+    ], 
+    
+    "ServiceDependency": [
+        
+        
+        
+        
+        
+        ["Service", "provider_service", "provided_dependencies"],
+        ["Service", "subscriber_service", "subscribed_dependencies"]
+        
+    ], 
+    
+    "ServiceMonitoringAgentInfo": [
+        
+        
+        
+        
+        
+        ["Service", "service", "servicemonitoringagents"]
+        
+    ], 
+    
+    "ServicePrivilege": [
+        
+        
+        
+        
+        
+        ["User", "user", "serviceprivileges"],
+        ["Service", "service", "serviceprivileges"],
+        ["ServiceRole", "role", "serviceprivileges"]
+        
+    ], 
+    
+    "ServiceRole": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Site": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerSite", "controllersite", "site"],
+        
+        
+        ["Deployment", "deployments", "sites"]
+        
+    ], 
+    
+    "SiteDeployment": [
+        
+        
+        
+        
+        
+        ["Site", "site", "sitedeployments"],
+        ["Deployment", "deployment", "sitedeployments"],
+        ["Controller", "controller", "sitedeployments"]
+        
+    ], 
+    
+    "SitePrivilege": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerSitePrivilege", "controllersiteprivileges", "site_privilege"],
+        
+        
+        ["User", "user", "siteprivileges"],
+        ["Site", "site", "siteprivileges"],
+        ["SiteRole", "role", "siteprivileges"]
+        
+    ], 
+    
+    "SiteRole": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Slice": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerSlice", "controllerslices", "slice"],
+        
+        
+        ["Site", "site", "slices"],
+        ["Service", "service", "slices"],
+        ["User", "creator", "slices"],
+        ["Flavor", "default_flavor", "slices"],
+        ["Image", "default_image", "slices"],
+        ["Node", "default_node", "slices"]
+        
+    ], 
+    
+    "SlicePrivilege": [
+        
+        
+        
+        
+        
+         
+        
+        
+        
+        
+        
+        
+        
+        ["ControllerSlicePrivilege", "controllersliceprivileges", "slice_privilege"],
+        
+        
+        ["User", "user", "sliceprivileges"],
+        ["Slice", "slice", "sliceprivileges"],
+        ["SliceRole", "role", "sliceprivileges"]
+        
+    ], 
+    
+    "SliceRole": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "Tag": [
+        
+        
+        
+        
+        
+        ["Service", "service", "tags"]
+        
+    ], 
+    
+    "InterfaceType": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "ServiceInterface": [
+        
+        
+        
+        
+        
+        ["Service", "service", "service_interfaces"],
+        ["InterfaceType", "interface_type", "service_interfaces"]
+        
+    ], 
+    
+    "ServiceInstance": [
+        
+        
+        
+        
+        
+        ["Service", "owner", "service_instances"]
+        
+    ], 
+    
+    "ServiceInstanceLink": [
+        
+        
+        
+        
+        
+        ["ServiceInstance", "provider_service_instance", "provided_links"],
+        ["ServiceInterface", "provider_service_interface", "provided_links"],
+        ["ServiceInstance", "subscriber_service_instance", "subscribed_links"],
+        ["Service", "subscriber_service", "subscribed_links"],
+        ["Network", "subscriber_network", "subscribed_links"]
+        
+    ], 
+    
+    "ServiceInstanceAttribute": [
+        
+        
+        
+        
+        
+        ["ServiceInstance", "service_instance", "service_instance_attributes"]
+        
+    ], 
+    
+    "TenantWithContainer": [
+        
+        
+        
+        
+        
+        ["Service", "owner", "service_instances"],
+        ["Instance", "instance", "+"],
+        ["User", "creator", "+"]
+        
+    ], 
+    
+    "XOS": [
+        
+        
+        
+        
+        
+        
+    ], 
+    
+    "XOSGuiExtension": [
+        
+        
+        
+        
+        
+        
+    ]
+}
diff --git a/xos/synchronizers/new_base/tests/model-deps-onos b/xos/synchronizers/new_base/tests/model-deps-onos
new file mode 100644
index 0000000..03d9c9f
--- /dev/null
+++ b/xos/synchronizers/new_base/tests/model-deps-onos
@@ -0,0 +1,5 @@
+{
+    "ONOSApp": [
+        ["ONOSService", "", ""]
+    ]
+}
diff --git a/xos/synchronizers/new_base/tests/test_controller_dependencies.py b/xos/synchronizers/new_base/tests/test_controller_dependencies.py
new file mode 100644
index 0000000..0ac1f7c
--- /dev/null
+++ b/xos/synchronizers/new_base/tests/test_controller_dependencies.py
@@ -0,0 +1,171 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+import unittest
+from mock import patch
+import mock
+import pdb
+import networkx as nx
+
+import os, sys
+
+sys.path.append("../..")
+sys.path.append("../../new_base")
+config =  os.path.abspath(os.path.dirname(os.path.realpath(__file__)) + "/test_config.yaml")
+from xosconfig import Config
+Config.init(config, 'synchronizer-config-schema.yaml')
+
+import synchronizers.new_base.modelaccessor
+from steps.mock_modelaccessor import *
+import event_loop
+import backend
+
+class TestControllerDependencies(unittest.TestCase):
+    def setUp(self):
+        b = backend.Backend()
+        steps_dir = Config.get("steps_dir")
+        self.steps = b.load_sync_step_modules(steps_dir)
+        self.synchronizer = event_loop.XOSObserver(self.steps)
+
+    def test_multi_controller_path(self):
+        csl = ControllerSlice()
+        csi = ControllerSite()
+        site = Site()
+        slice = Slice()
+        slice.site = site
+        csl.slice = slice
+        csi.site = site
+        slice.controllerslices = mock_enumerator([csl])
+        site.controllersite = mock_enumerator([csi])
+
+        verdict, edge_type = self.synchronizer.concrete_path_exists(csl, csi)
+        self.assertTrue(verdict)
+        self.assertEqual(edge_type, event_loop.PROXY_EDGE)
+
+    def test_controller_path_simple(self):
+        p = Instance()
+        s = Slice()
+        t = Site()
+        ct = ControllerSite()
+        p.slice = s
+        s.site = t
+        ct.site = t
+        t.controllersite = mock_enumerator([ct])
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,ct], False)
+        self.assertEqual([ct, p], cohorts[0])
+        cohorts = self.synchronizer.compute_dependent_cohorts([ct,p], False)
+        self.assertEqual([ct, p], cohorts[0])
+
+    def test_controller_deletion_path(self):
+        p = Instance()
+        s = Slice()
+        t = Site()
+        ct = ControllerSite()
+        ct.site = t
+        p.slice = s
+        s.site = t
+
+        t.controllersite = mock_enumerator([ct])
+        
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,s,t,ct], False)
+        self.assertEqual([t, ct, s, p], cohorts[0])
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,s,t,ct], True)
+        self.assertEqual([p, s, ct, t], cohorts[0])
+
+    def test_multi_controller_schedule(self):
+        csl = ControllerSlice()
+        csi = ControllerSite()
+        site = Site()
+        slice = Slice()
+        slice.site = site
+        csl.slice = slice
+        csi.site = site
+        slice.controllerslices = mock_enumerator([csl])
+        site.controllersite = mock_enumerator([csi])
+        i = Instance()
+        i.slice = slice
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i, slice, site, csl, csi], False)
+        self.assertEqual([site, csi, slice, csl, i], cohorts[0])
+
+    def test_multi_controller_path_negative(self):
+        csl = ControllerSlice()
+        csi = ControllerSite()
+        site = Site()
+        slice = Slice()
+        slice.site = site
+        csl.slice = slice
+        csi.site = site
+        slice.controllerslices = mock_enumerator([])
+        site.controllersite = mock_enumerator([])
+
+        verdict, edge_type = self.synchronizer.concrete_path_exists(csl, csi)
+        self.assertFalse(verdict)
+        self.assertEqual(edge_type, None)
+
+    def test_controller_path_simple(self):
+        p = Instance()
+        s = Slice()
+        t = Site()
+        ct = ControllerSite()
+        p.slice = s
+        s.site = t
+        ct.site = t
+        t.controllersite = mock_enumerator([])
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,ct], False)
+        self.assertIn([ct], cohorts)
+        self.assertIn([p], cohorts)
+
+    def test_controller_deletion_path(self):
+        p = Instance()
+        s = Slice()
+        t = Site()
+        ct = ControllerSite()
+        s.site = t
+
+        t.controllersite = mock_enumerator([])
+        
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,s,t,ct], False)
+        self.assertIn([t,s], cohorts)
+        self.assertIn([p], cohorts)
+        self.assertIn([ct], cohorts)
+        cohorts = self.synchronizer.compute_dependent_cohorts([p,s,t,ct], True)
+        self.assertIn([s,t], cohorts)
+        self.assertIn([p], cohorts)
+        self.assertIn([ct], cohorts)
+
+
+    def test_multi_controller_schedule(self):
+        csl = ControllerSlice()
+        csi = ControllerSite()
+        site = Site()
+        slice = Slice()
+        slice.site = site
+        slice.controllerslices = mock_enumerator([])
+        site.controllersite = mock_enumerator([])
+        i = Instance()
+        i.slice = slice
+
+        cohorts = self.synchronizer.compute_dependent_cohorts([i, slice, site, csl, csi], False)
+        self.assertIn([site, slice, i], cohorts)
+        self.assertIn([csl], cohorts)
+        self.assertIn([csi], cohorts)
+
+
+if __name__ == '__main__':
+    unittest.main()
+
+# ControllerNetworks, ControllerSlice, ControllerSite, instance, slice, site
diff --git a/xos/synchronizers/new_base/tests/test_payload.py b/xos/synchronizers/new_base/tests/test_payload.py
index 15c5874..7440c99 100644
--- a/xos/synchronizers/new_base/tests/test_payload.py
+++ b/xos/synchronizers/new_base/tests/test_payload.py
@@ -123,7 +123,7 @@
         cs.synchronizer_step = steps.sync_controller_slices.SyncControllerSlices()
 
         self.synchronizer.sync_cohort(cohort, False)
-        o.save.assert_called_with(update_fields=['backend_status', 'backend_register','updated'])
+        o.save.assert_called_with(always_update_timestamp=True, update_fields=['backend_status', 'backend_register'])
         self.assertEqual(cs.backend_code, 1)
 
         self.assertIn('Force', cs.backend_status)
@@ -146,7 +146,7 @@
         cs.synchronizer_step = steps.sync_controller_slices.SyncControllerSlices()
 
         self.synchronizer.sync_cohort(cohort, False)
-        o.save.assert_called_with(update_fields=['backend_status', 'backend_register','updated'])
+        o.save.assert_called_with(always_update_timestamp=True, update_fields=['backend_status', 'backend_register'])
         self.assertIn('Force', cs.backend_status)
         self.assertIn('Failed due to', o.backend_status)