From 52d701a56e6a74f854876aca615ed9dd29ccbf9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Fran=C3=A7oise?= Date: Tue, 31 Jan 2017 12:15:45 +0100 Subject: [PATCH] Updated graph model to use attr_dict This patchset makes use of a more idiomatic structure of networkx Change-Id: Iccc4e9f0cc14cccadb2959ff8d68cd68367c4da3 --- watcher/common/exception.py | 8 ++- watcher/decision_engine/model/element/base.py | 3 +- watcher/decision_engine/model/model_root.py | 58 ++++++++++--------- watcher/decision_engine/scope/default.py | 2 +- watcher/objects/base.py | 4 ++ .../decision_engine/scope/test_default.py | 13 ++--- 6 files changed, 48 insertions(+), 40 deletions(-) diff --git a/watcher/common/exception.py b/watcher/common/exception.py index bf2679eee..a7b86a4c4 100644 --- a/watcher/common/exception.py +++ b/watcher/common/exception.py @@ -402,11 +402,15 @@ class WildcardCharacterIsUsed(WatcherException): # Model -class InstanceNotFound(WatcherException): +class ComputeResourceNotFound(WatcherException): + msg_fmt = _("The compute resource '%(name)s' could not be found") + + +class InstanceNotFound(ComputeResourceNotFound): msg_fmt = _("The instance '%(name)s' could not be found") -class ComputeNodeNotFound(WatcherException): +class ComputeNodeNotFound(ComputeResourceNotFound): msg_fmt = _("The compute node %(name)s could not be found") diff --git a/watcher/decision_engine/model/element/base.py b/watcher/decision_engine/model/element/base.py index 21cd5b2fa..6ff04da25 100644 --- a/watcher/decision_engine/model/element/base.py +++ b/watcher/decision_engine/model/element/base.py @@ -30,7 +30,8 @@ LOG = log.getLogger(__name__) @six.add_metaclass(abc.ABCMeta) -class Element(base.WatcherObject, base.WatcherObjectDictCompat): +class Element(base.WatcherObject, base.WatcherObjectDictCompat, + base.WatcherComparableObject): # Initial version VERSION = '1.0' diff --git a/watcher/decision_engine/model/model_root.py b/watcher/decision_engine/model/model_root.py index f82d0360a..c1843bb90 100644 --- a/watcher/decision_engine/model/model_root.py +++ b/watcher/decision_engine/model/model_root.py @@ -57,13 +57,13 @@ class ModelRoot(nx.DiGraph, base.Model): @lockutils.synchronized("model_root") def add_node(self, node): self.assert_node(node) - super(ModelRoot, self).add_node(node) + super(ModelRoot, self).add_node(node.uuid, node) @lockutils.synchronized("model_root") def remove_node(self, node): self.assert_node(node) try: - super(ModelRoot, self).remove_node(node) + super(ModelRoot, self).remove_node(node.uuid) except nx.NetworkXError as exc: LOG.exception(exc) raise exception.ComputeNodeNotFound(name=node.uuid) @@ -72,7 +72,7 @@ class ModelRoot(nx.DiGraph, base.Model): def add_instance(self, instance): self.assert_instance(instance) try: - super(ModelRoot, self).add_node(instance) + super(ModelRoot, self).add_node(instance.uuid, instance) except nx.NetworkXError as exc: LOG.exception(exc) raise exception.InstanceNotFound(name=instance.uuid) @@ -80,7 +80,7 @@ class ModelRoot(nx.DiGraph, base.Model): @lockutils.synchronized("model_root") def remove_instance(self, instance): self.assert_instance(instance) - super(ModelRoot, self).remove_node(instance) + super(ModelRoot, self).remove_node(instance.uuid) @lockutils.synchronized("model_root") def map_instance(self, instance, node): @@ -98,7 +98,7 @@ class ModelRoot(nx.DiGraph, base.Model): self.assert_node(node) self.assert_instance(instance) - self.add_edge(instance, node) + self.add_edge(instance.uuid, node.uuid) @lockutils.synchronized("model_root") def unmap_instance(self, instance, node): @@ -107,7 +107,7 @@ class ModelRoot(nx.DiGraph, base.Model): if isinstance(node, six.string_types): node = self.get_node_by_uuid(node) - self.remove_edge(instance, node) + self.remove_edge(instance.uuid, node.uuid) def delete_instance(self, instance, node=None): self.assert_instance(instance) @@ -130,55 +130,59 @@ class ModelRoot(nx.DiGraph, base.Model): return False # unmap - self.remove_edge(instance, source_node) + self.remove_edge(instance.uuid, source_node.uuid) # map - self.add_edge(instance, destination_node) + self.add_edge(instance.uuid, destination_node.uuid) return True @lockutils.synchronized("model_root") def get_all_compute_nodes(self): - return {cn.uuid: cn for cn in self.nodes() + return {uuid: cn for uuid, cn in self.nodes(data=True) if isinstance(cn, element.ComputeNode)} @lockutils.synchronized("model_root") def get_node_by_uuid(self, uuid): - for graph_node in self.nodes(): - if (isinstance(graph_node, element.ComputeNode) and - graph_node.uuid == uuid): - return graph_node - raise exception.ComputeNodeNotFound(name=uuid) + try: + return self._get_by_uuid(uuid) + except exception.ComputeResourceNotFound: + raise exception.ComputeNodeNotFound(name=uuid) @lockutils.synchronized("model_root") def get_instance_by_uuid(self, uuid): - return self._get_instance_by_uuid(uuid) + try: + return self._get_by_uuid(uuid) + except exception.ComputeResourceNotFound: + raise exception.InstanceNotFound(name=uuid) - def _get_instance_by_uuid(self, uuid): - for graph_node in self.nodes(): - if (isinstance(graph_node, element.Instance) and - graph_node.uuid == str(uuid)): - return graph_node - raise exception.InstanceNotFound(name=uuid) + def _get_by_uuid(self, uuid): + try: + return self.node[uuid] + except Exception as exc: + LOG.exception(exc) + raise exception.ComputeResourceNotFound(name=uuid) @lockutils.synchronized("model_root") def get_node_by_instance_uuid(self, instance_uuid): - instance = self._get_instance_by_uuid(instance_uuid) - for node in self.neighbors(instance): + instance = self._get_by_uuid(instance_uuid) + for node_uuid in self.neighbors(instance.uuid): + node = self._get_by_uuid(node_uuid) if isinstance(node, element.ComputeNode): return node raise exception.ComputeNodeNotFound(name=instance_uuid) @lockutils.synchronized("model_root") def get_all_instances(self): - return {inst.uuid: inst for inst in self.nodes() + return {uuid: inst for uuid, inst in self.nodes(data=True) if isinstance(inst, element.Instance)} @lockutils.synchronized("model_root") def get_node_instances(self, node): self.assert_node(node) node_instances = [] - for neighbor in self.predecessors(node): - if isinstance(neighbor, element.Instance): - node_instances.append(neighbor) + for instance_uuid in self.predecessors(node.uuid): + instance = self._get_by_uuid(instance_uuid) + if isinstance(instance, element.Instance): + node_instances.append(instance) return node_instances diff --git a/watcher/decision_engine/scope/default.py b/watcher/decision_engine/scope/default.py index bb7e24a73..f6de55b67 100644 --- a/watcher/decision_engine/scope/default.py +++ b/watcher/decision_engine/scope/default.py @@ -169,7 +169,7 @@ class DefaultScope(base.BaseScope): try: node_name = cluster_model.get_node_by_instance_uuid( instance_uuid).uuid - except exception.InstanceNotFound: + except exception.ComputeResourceNotFound: LOG.warning(_LW("The following instance %s cannot be found. " "It might be deleted from CDM along with node" " instance was hosted on."), diff --git a/watcher/objects/base.py b/watcher/objects/base.py index fac68edc4..8b9341814 100644 --- a/watcher/objects/base.py +++ b/watcher/objects/base.py @@ -92,6 +92,10 @@ class WatcherObjectDictCompat(ovo_base.VersionedObjectDictCompat): pass +class WatcherComparableObject(ovo_base.ComparableVersionedObject): + pass + + class WatcherPersistentObject(object): """Mixin class for Persistent objects. diff --git a/watcher/tests/decision_engine/scope/test_default.py b/watcher/tests/decision_engine/scope/test_default.py index f66e84ea4..ab512ad82 100644 --- a/watcher/tests/decision_engine/scope/test_default.py +++ b/watcher/tests/decision_engine/scope/test_default.py @@ -42,10 +42,8 @@ class TestDefaultScope(base.TestCase): for i in range(2)] model = default.DefaultScope(audit_scope, osc=mock.Mock()).get_scoped_model(cluster) - expected_edges = [('INSTANCE_2', 'Node_1')] - edges = [(src.uuid, dst.uuid) for src, dst in model.edges()] - self.assertEqual(sorted(expected_edges), sorted(edges)) + self.assertEqual(sorted(expected_edges), sorted(model.edges())) @mock.patch.object(nova_helper.NovaHelper, 'get_availability_zone_list') def test_get_scoped_model_without_scope(self, mock_zone_list): @@ -67,8 +65,7 @@ class TestDefaultScope(base.TestCase): ('INSTANCE_6', 'Node_3'), ('INSTANCE_7', 'Node_4'), ] - edges = [(src.uuid, dst.uuid) for src, dst in model.edges()] - self.assertEqual(sorted(expected_edges), sorted(edges)) + self.assertEqual(sorted(expected_edges), sorted(model.edges())) @mock.patch.object(nova_helper.NovaHelper, 'get_aggregate_detail') @mock.patch.object(nova_helper.NovaHelper, 'get_aggregate_list') @@ -199,8 +196,7 @@ class TestDefaultScope(base.TestCase): ('INSTANCE_1', 'Node_0'), ('INSTANCE_6', 'Node_3'), ('INSTANCE_7', 'Node_4')] - edges = [(src.uuid, dst.uuid) for src, dst in model.edges()] - self.assertEqual(sorted(expected_edges), sorted(edges)) + self.assertEqual(sorted(expected_edges), sorted(model.edges())) def test_remove_instances_from_model(self): model = self.fake_cluster.generate_scenario_1() @@ -213,5 +209,4 @@ class TestDefaultScope(base.TestCase): ('INSTANCE_5', 'Node_2'), ('INSTANCE_6', 'Node_3'), ('INSTANCE_7', 'Node_4')] - edges = [(src.uuid, dst.uuid) for src, dst in model.edges()] - self.assertEqual(sorted(expected_edges), sorted(edges)) + self.assertEqual(sorted(expected_edges), sorted(model.edges()))