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)