Browse Source

Merge "Implement watcher datamodel list in watcher-decision-engine"

changes/22/681222/1
Zuul 1 week ago
parent
commit
0732485467

+ 4
- 3
watcher/decision_engine/manager.py View File

@@ -38,13 +38,13 @@ See :doc:`../architecture` for more details on this component.
38 38
 """
39 39
 
40 40
 from watcher.common import service_manager
41
+from watcher import conf
41 42
 from watcher.decision_engine.messaging import audit_endpoint
43
+from watcher.decision_engine.messaging import data_model_endpoint
42 44
 from watcher.decision_engine.model.collector import manager
43 45
 from watcher.decision_engine.strategy.strategies import base \
44 46
     as strategy_endpoint
45 47
 
46
-from watcher import conf
47
-
48 48
 CONF = conf.CONF
49 49
 
50 50
 
@@ -73,7 +73,8 @@ class DecisionEngineManager(service_manager.ServiceManager):
73 73
     @property
74 74
     def conductor_endpoints(self):
75 75
         return [audit_endpoint.AuditEndpoint,
76
-                strategy_endpoint.StrategyEndpoint]
76
+                strategy_endpoint.StrategyEndpoint,
77
+                data_model_endpoint.DataModelEndpoint]
77 78
 
78 79
     @property
79 80
     def notification_endpoints(self):

+ 60
- 0
watcher/decision_engine/messaging/data_model_endpoint.py View File

@@ -0,0 +1,60 @@
1
+# -*- encoding: utf-8 -*-
2
+# Copyright 2019 ZTE corporation.
3
+# All Rights Reserved.
4
+#
5
+# Licensed under the Apache License, Version 2.0 (the "License");
6
+# you may not use this file except in compliance with the License.
7
+# You may obtain a copy of the License at
8
+#
9
+# http://www.apache.org/licenses/LICENSE-2.0
10
+#
11
+# Unless required by applicable law or agreed to in writing, software
12
+# distributed under the License is distributed on an "AS IS" BASIS,
13
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14
+# implied.
15
+# See the License for the specific language governing permissions and
16
+# limitations under the License.
17
+#
18
+from watcher.common import exception
19
+from watcher.common import utils
20
+from watcher.decision_engine.model.collector import manager
21
+from watcher import objects
22
+
23
+
24
+class DataModelEndpoint(object):
25
+    def __init__(self, messaging):
26
+        self._messaging = messaging
27
+
28
+    def get_audit_scope(self, context, audit=None):
29
+        scope = None
30
+        try:
31
+            if utils.is_uuid_like(audit) or utils.is_int_like(audit):
32
+                audit = objects.Audit.get(
33
+                    context, audit)
34
+            else:
35
+                audit = objects.Audit.get_by_name(
36
+                    context, audit)
37
+        except exception.AuditNotFound:
38
+            raise exception.InvalidIdentity(identity=audit)
39
+        if audit:
40
+            scope = audit.scope
41
+        else:
42
+            scope = []
43
+        return scope
44
+
45
+    def get_data_model_info(self, context, data_model_type='compute',
46
+                            audit=None):
47
+        if audit is not None:
48
+            scope = self.get_audit_scope(context, audit)
49
+        else:
50
+            scope = []
51
+        collector_manager = manager.CollectorManager()
52
+        collector = collector_manager.get_cluster_model_collector(
53
+            data_model_type)
54
+        audit_scope_handler = collector.get_audit_scope_handler(
55
+            audit_scope=scope)
56
+        available_data_model = audit_scope_handler.get_scoped_model(
57
+            collector.get_latest_cluster_data_model())
58
+        if not available_data_model:
59
+            return {"context": []}
60
+        return {"context": available_data_model.to_list()}

+ 18
- 0
watcher/decision_engine/model/model_root.py View File

@@ -248,6 +248,24 @@ class ModelRoot(nx.DiGraph, base.Model):
248 248
 
249 249
         return etree.tostring(root, pretty_print=True).decode('utf-8')
250 250
 
251
+    def to_list(self):
252
+        ret_list = []
253
+        for cn in sorted(self.get_all_compute_nodes().values(),
254
+                         key=lambda cn: cn.uuid):
255
+            in_dict = {}
256
+            for field in cn.fields:
257
+                new_name = "node_"+str(field)
258
+                in_dict[new_name] = cn[field]
259
+            node_instances = self.get_node_instances(cn)
260
+            for instance in sorted(node_instances, key=lambda x: x.uuid):
261
+                for field in instance.fields:
262
+                    new_name = "server_"+str(field)
263
+                    in_dict[new_name] = instance[field]
264
+                if in_dict != {}:
265
+                    deep_in_dict = in_dict.copy()
266
+                    ret_list.append(deep_in_dict)
267
+        return ret_list
268
+
251 269
     @classmethod
252 270
     def from_xml(cls, data):
253 271
         model = cls()

+ 5
- 0
watcher/decision_engine/rpcapi.py View File

@@ -44,6 +44,11 @@ class DecisionEngineAPI(service.Service):
44 44
         return self.conductor_client.call(
45 45
             context, 'get_strategy_info', strategy_name=strategy_name)
46 46
 
47
+    def get_data_model_info(self, context, data_model_type, audit):
48
+        return self.conductor_client.call(
49
+            context, 'get_data_model_info',
50
+            data_model_type=data_model_type, audit=audit)
51
+
47 52
 
48 53
 class DecisionEngineAPIManager(service_manager.ServiceManager):
49 54
 

+ 54
- 0
watcher/tests/decision_engine/messaging/test_data_model_endpoint.py View File

@@ -0,0 +1,54 @@
1
+# -*- encoding: utf-8 -*-
2
+# Copyright 2019 ZTE Corporation.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License");
5
+# you may not use this file except in compliance with the License.
6
+# You may obtain a copy of the License at
7
+#
8
+# http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS,
12
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13
+# implied.
14
+# See the License for the specific language governing permissions and
15
+# limitations under the License.
16
+import mock
17
+import unittest
18
+
19
+from watcher.common import exception
20
+from watcher.common import utils
21
+from watcher.decision_engine.messaging import data_model_endpoint
22
+from watcher.decision_engine.model.collector import manager
23
+from watcher.objects import audit
24
+
25
+
26
+class TestDataModelEndpoint(unittest.TestCase):
27
+    def setUp(self):
28
+        self.endpoint_instance = data_model_endpoint.DataModelEndpoint('fake')
29
+
30
+    @mock.patch.object(audit.Audit, 'get')
31
+    def test_get_audit_scope(self, mock_get):
32
+        mock_get.return_value = mock.Mock(scope='fake_scope')
33
+        audit_uuid = utils.generate_uuid()
34
+
35
+        result = self.endpoint_instance.get_audit_scope(
36
+            context=None,
37
+            audit=audit_uuid)
38
+        self.assertEqual('fake_scope', result)
39
+
40
+    @mock.patch.object(audit.Audit, 'get_by_name')
41
+    def test_get_audit_scope_with_error_name(self, mock_get_by_name):
42
+        mock_get_by_name.side_effect = exception.AuditNotFound()
43
+        audit_name = 'error_audit_name'
44
+
45
+        self.assertRaises(
46
+            exception.InvalidIdentity,
47
+            self.endpoint_instance.get_audit_scope,
48
+            context=None,
49
+            audit=audit_name)
50
+
51
+    @mock.patch.object(manager, 'CollectorManager', mock.Mock())
52
+    def test_get_data_model_info(self):
53
+        result = self.endpoint_instance.get_data_model_info(context='fake')
54
+        self.assertIn('context', result)

+ 22
- 0
watcher/tests/decision_engine/model/test_model.py View File

@@ -16,6 +16,7 @@
16 16
 # See the License for the specific language governing permissions and
17 17
 # limitations under the License.
18 18
 
19
+import mock
19 20
 import os
20 21
 
21 22
 from oslo_utils import uuidutils
@@ -63,6 +64,27 @@ class TestModel(base.TestCase):
63 64
         model = model_root.ModelRoot.from_xml(struct_str)
64 65
         self.assertEqual(expected_model.to_string(), model.to_string())
65 66
 
67
+    @mock.patch.object(model_root.ModelRoot, 'get_all_compute_nodes')
68
+    @mock.patch.object(model_root.ModelRoot, 'get_node_instances')
69
+    def test_get_model_to_list(self, mock_instances, mock_nodes):
70
+        fake_compute_node = mock.MagicMock(
71
+            uuid='fake_node_uuid',
72
+            fields=['uuid'])
73
+        fake_instance = mock.MagicMock(
74
+            uuid='fake_instance_uuid',
75
+            fields=['uuid'])
76
+
77
+        mock_nodes.return_value = {'fake_node_uuid': fake_compute_node}
78
+        mock_instances.return_value = [fake_instance]
79
+
80
+        expected_keys = ['server_uuid', 'node_uuid']
81
+
82
+        result = model_root.ModelRoot().to_list()
83
+        self.assertEqual(1, len(result))
84
+
85
+        result_keys = result[0].keys()
86
+        self.assertEqual(sorted(expected_keys), sorted(result_keys))
87
+
66 88
     def test_get_node_by_instance_uuid(self):
67 89
         model = model_root.ModelRoot()
68 90
         uuid_ = "{0}".format(uuidutils.generate_uuid())

+ 11
- 0
watcher/tests/decision_engine/test_rpcapi.py View File

@@ -52,3 +52,14 @@ class TestDecisionEngineAPI(base.TestCase):
52 52
             self.api.get_strategy_info(self.context, "dummy")
53 53
             mock_call.assert_called_once_with(
54 54
                 self.context, 'get_strategy_info', strategy_name="dummy")
55
+
56
+    def test_get_data_model_info(self):
57
+        with mock.patch.object(om.RPCClient, 'call') as mock_call:
58
+            self.api.get_data_model_info(
59
+                self.context,
60
+                data_model_type='compute',
61
+                audit=None)
62
+            mock_call.assert_called_once_with(
63
+                self.context, 'get_data_model_info',
64
+                data_model_type='compute',
65
+                audit=None)

Loading…
Cancel
Save