1# Copyright 2015 Cloudbase Solutions Srl
2# All Rights Reserved.
3#
4#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5#    not use this file except in compliance with the License. You may obtain
6#    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, WITHOUT
12#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13#    License for the specific language governing permissions and limitations
14#    under the License.
15
16from unittest import mock
17
18from os_win import exceptions
19from os_win.tests.unit import test_base
20from os_win.utils import _wqlutils
21from os_win.utils.compute import vmutils
22from os_win.utils.metrics import metricsutils
23from os_win import utilsfactory
24
25
26class MetricsUtilsTestCase(test_base.OsWinBaseTestCase):
27    """Unit tests for the Hyper-V MetricsUtils class."""
28
29    _FAKE_RET_VAL = 0
30    _FAKE_PORT = "fake's port name"
31
32    _autospec_classes = [
33        vmutils.VMUtils,
34    ]
35
36    def setUp(self):
37        super(MetricsUtilsTestCase, self).setUp()
38
39        mock.patch.object(utilsfactory, 'get_vmutils',
40                          mock.Mock(return_value=vmutils.VMUtils)).start()
41
42        self.utils = metricsutils.MetricsUtils()
43        self.utils._conn_attr = mock.MagicMock()
44
45    def test_cache_metrics_defs(self):
46        mock_metric_def = mock.Mock(ElementName=mock.sentinel.elementname)
47        self.utils._conn.CIM_BaseMetricDefinition.return_value = [
48            mock_metric_def]
49        self.utils._cache_metrics_defs()
50        expected_cache_metrics = {mock.sentinel.elementname: mock_metric_def}
51        self.assertEqual(expected_cache_metrics, self.utils._metrics_defs_obj)
52
53    @mock.patch.object(metricsutils.MetricsUtils, '_enable_metrics')
54    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
55    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
56    def test_enable_vm_metrics_collection(
57            self, mock_get_vm, mock_get_vm_resources, mock_enable_metrics):
58        mock_vm = mock_get_vm.return_value
59        mock_disk = mock.MagicMock()
60        mock_dvd = mock.MagicMock(
61            ResourceSubType=self.utils._DVD_DISK_RES_SUB_TYPE)
62        mock_get_vm_resources.return_value = [mock_disk, mock_dvd]
63
64        self.utils.enable_vm_metrics_collection(mock.sentinel.vm_name)
65
66        metrics_names = [self.utils._CPU_METRICS,
67                         self.utils._MEMORY_METRICS]
68        mock_enable_metrics.assert_has_calls(
69            [mock.call(mock_disk), mock.call(mock_vm, metrics_names)])
70
71    @mock.patch.object(metricsutils.MetricsUtils, '_enable_metrics')
72    def test_enable_disk_metrics_collection(self, mock_enable_metrics):
73        mock_get_disk = (
74            self.utils._vmutils._get_mounted_disk_resource_from_path)
75
76        self.utils.enable_disk_metrics_collection(
77            mock.sentinel.disk_path,
78            mock.sentinel.is_physical,
79            mock.sentinel.serial)
80
81        mock_get_disk.assert_called_once_with(
82            mock.sentinel.disk_path,
83            is_physical=mock.sentinel.is_physical,
84            serial=mock.sentinel.serial)
85        mock_enable_metrics.assert_called_once_with(mock_get_disk.return_value)
86
87    @mock.patch.object(metricsutils.MetricsUtils, '_enable_metrics')
88    @mock.patch.object(metricsutils.MetricsUtils, '_get_switch_port')
89    def test_enable_switch_port_metrics_collection(self, mock_get_port,
90                                                   mock_enable_metrics):
91        self.utils.enable_port_metrics_collection(mock.sentinel.port_name)
92
93        mock_get_port.assert_called_once_with(mock.sentinel.port_name)
94        metrics = [self.utils._NET_IN_METRICS,
95                   self.utils._NET_OUT_METRICS]
96        mock_enable_metrics.assert_called_once_with(
97            mock_get_port.return_value, metrics)
98
99    def _check_enable_metrics(self, metrics=None, definition=None):
100        mock_element = mock.MagicMock()
101        self.utils._metrics_svc.ControlMetrics.return_value = [0]
102
103        self.utils._enable_metrics(mock_element, metrics)
104
105        self.utils._metrics_svc.ControlMetrics.assert_called_once_with(
106            Subject=mock_element.path_.return_value,
107            Definition=definition,
108            MetricCollectionEnabled=self.utils._METRICS_ENABLED)
109
110    def test_enable_metrics_no_metrics(self):
111        self._check_enable_metrics()
112
113    def test_enable_metrics(self):
114        metrics_name = self.utils._CPU_METRICS
115        metrics_def = mock.MagicMock()
116        self.utils._metrics_defs_obj = {metrics_name: metrics_def}
117        self._check_enable_metrics([metrics_name, mock.sentinel.metrics_name],
118                                   metrics_def.path_.return_value)
119
120    def test_enable_metrics_exception(self):
121        metric_name = self.utils._CPU_METRICS
122        metric_def = mock.MagicMock()
123        self.utils._metrics_defs_obj = {metric_name: metric_def}
124
125        self.utils._metrics_svc.ControlMetrics.return_value = [1]
126        self.assertRaises(exceptions.OSWinException,
127                          self.utils._enable_metrics,
128                          mock.MagicMock(),
129                          [metric_name])
130
131    @mock.patch.object(metricsutils.MetricsUtils, '_get_metrics')
132    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
133    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
134    def test_get_cpu_metrics(self, mock_get_vm, mock_get_vm_resources,
135                             mock_get_metrics):
136        fake_cpu_count = 2
137        fake_uptime = 1000
138        fake_cpu_metrics_val = 2000
139
140        self.utils._metrics_defs_obj = {
141            self.utils._CPU_METRICS: mock.sentinel.metrics}
142
143        mock_vm = mock_get_vm.return_value
144        mock_vm.OnTimeInMilliseconds = fake_uptime
145        mock_cpu = mock.MagicMock(VirtualQuantity=fake_cpu_count)
146        mock_get_vm_resources.return_value = [mock_cpu]
147
148        mock_metric = mock.MagicMock(MetricValue=fake_cpu_metrics_val)
149        mock_get_metrics.return_value = [mock_metric]
150
151        cpu_metrics = self.utils.get_cpu_metrics(mock.sentinel.vm_name)
152
153        self.assertEqual(3, len(cpu_metrics))
154        self.assertEqual(fake_cpu_metrics_val, cpu_metrics[0])
155        self.assertEqual(fake_cpu_count, cpu_metrics[1])
156        self.assertEqual(fake_uptime, cpu_metrics[2])
157
158        mock_get_vm.assert_called_once_with(mock.sentinel.vm_name)
159        mock_get_vm_resources.assert_called_once_with(
160            mock.sentinel.vm_name, self.utils._PROCESSOR_SETTING_DATA_CLASS)
161        mock_get_metrics.assert_called_once_with(mock_vm,
162                                                 mock.sentinel.metrics)
163
164    @mock.patch.object(metricsutils.MetricsUtils, '_get_metrics')
165    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
166    def test_get_memory_metrics(self, mock_get_vm, mock_get_metrics):
167        mock_vm = mock_get_vm.return_value
168        self.utils._metrics_defs_obj = {
169            self.utils._MEMORY_METRICS: mock.sentinel.metrics}
170
171        metrics_memory = mock.MagicMock()
172        metrics_memory.MetricValue = 3
173        mock_get_metrics.return_value = [metrics_memory]
174
175        response = self.utils.get_memory_metrics(mock.sentinel.vm_name)
176
177        self.assertEqual(3, response)
178        mock_get_vm.assert_called_once_with(mock.sentinel.vm_name)
179        mock_get_metrics.assert_called_once_with(mock_vm,
180                                                 mock.sentinel.metrics)
181
182    @mock.patch.object(_wqlutils, 'get_element_associated_class')
183    @mock.patch.object(metricsutils.MetricsUtils,
184                       '_sum_metrics_values_by_defs')
185    @mock.patch.object(metricsutils.MetricsUtils,
186                       '_get_metrics_value_instances')
187    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
188    def test_get_vnic_metrics(self, mock_get_vm_resources,
189                              mock_get_value_instances, mock_sum_by_defs,
190                              mock_get_element_associated_class):
191        fake_rx_mb = 1000
192        fake_tx_mb = 2000
193
194        self.utils._metrics_defs_obj = {
195            self.utils._NET_IN_METRICS: mock.sentinel.net_in_metrics,
196            self.utils._NET_OUT_METRICS: mock.sentinel.net_out_metrics}
197
198        mock_port = mock.MagicMock(Parent=mock.sentinel.vnic_path)
199        mock_vnic = mock.MagicMock(ElementName=mock.sentinel.element_name,
200                                   Address=mock.sentinel.address)
201        mock_vnic.path_.return_value = mock.sentinel.vnic_path
202        mock_get_vm_resources.side_effect = [[mock_port], [mock_vnic]]
203        mock_sum_by_defs.return_value = [fake_rx_mb, fake_tx_mb]
204
205        vnic_metrics = list(
206            self.utils.get_vnic_metrics(mock.sentinel.vm_name))
207
208        self.assertEqual(1, len(vnic_metrics))
209        self.assertEqual(fake_rx_mb, vnic_metrics[0]['rx_mb'])
210        self.assertEqual(fake_tx_mb, vnic_metrics[0]['tx_mb'])
211        self.assertEqual(mock.sentinel.element_name,
212                         vnic_metrics[0]['element_name'])
213        self.assertEqual(mock.sentinel.address, vnic_metrics[0]['address'])
214
215        mock_get_vm_resources.assert_has_calls([
216            mock.call(mock.sentinel.vm_name, self.utils._PORT_ALLOC_SET_DATA),
217            mock.call(mock.sentinel.vm_name,
218                      self.utils._SYNTH_ETH_PORT_SET_DATA)])
219        mock_get_value_instances.assert_called_once_with(
220            mock_get_element_associated_class.return_value,
221            self.utils._BASE_METRICS_VALUE)
222        mock_sum_by_defs.assert_called_once_with(
223            mock_get_value_instances.return_value,
224            [mock.sentinel.net_in_metrics, mock.sentinel.net_out_metrics])
225
226    @mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
227    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
228    def test_get_disk_metrics(self, mock_get_vm_resources,
229                              mock_get_metrics_values):
230        fake_read_mb = 1000
231        fake_write_mb = 2000
232
233        self.utils._metrics_defs_obj = {
234            self.utils._DISK_RD_METRICS: mock.sentinel.disk_rd_metrics,
235            self.utils._DISK_WR_METRICS: mock.sentinel.disk_wr_metrics}
236
237        mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
238                                   InstanceID=mock.sentinel.instance_id)
239        mock_get_vm_resources.return_value = [mock_disk]
240        mock_get_metrics_values.return_value = [fake_read_mb, fake_write_mb]
241
242        disk_metrics = list(
243            self.utils.get_disk_metrics(mock.sentinel.vm_name))
244
245        self.assertEqual(1, len(disk_metrics))
246        self.assertEqual(fake_read_mb, disk_metrics[0]['read_mb'])
247        self.assertEqual(fake_write_mb, disk_metrics[0]['write_mb'])
248        self.assertEqual(mock.sentinel.instance_id,
249                         disk_metrics[0]['instance_id'])
250        self.assertEqual(mock.sentinel.host_resource,
251                         disk_metrics[0]['host_resource'])
252
253        mock_get_vm_resources.assert_called_once_with(
254            mock.sentinel.vm_name,
255            self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
256        metrics = [mock.sentinel.disk_rd_metrics,
257                   mock.sentinel.disk_wr_metrics]
258        mock_get_metrics_values.assert_called_once_with(mock_disk, metrics)
259
260    @mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
261    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
262    def test_get_disk_latency_metrics(self, mock_get_vm_resources,
263                                      mock_get_metrics_values):
264        self.utils._metrics_defs_obj = {
265            self.utils._DISK_LATENCY_METRICS: mock.sentinel.metrics}
266
267        mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
268                                   InstanceID=mock.sentinel.instance_id)
269        mock_get_vm_resources.return_value = [mock_disk]
270        mock_get_metrics_values.return_value = [mock.sentinel.latency]
271
272        disk_metrics = list(
273            self.utils.get_disk_latency_metrics(mock.sentinel.vm_name))
274
275        self.assertEqual(1, len(disk_metrics))
276        self.assertEqual(mock.sentinel.latency,
277                         disk_metrics[0]['disk_latency'])
278        self.assertEqual(mock.sentinel.instance_id,
279                         disk_metrics[0]['instance_id'])
280        mock_get_vm_resources.assert_called_once_with(
281            mock.sentinel.vm_name,
282            self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
283        mock_get_metrics_values.assert_called_once_with(
284            mock_disk, [mock.sentinel.metrics])
285
286    @mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
287    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
288    def test_get_disk_iops_metrics(self, mock_get_vm_resources,
289                                   mock_get_metrics_values):
290        self.utils._metrics_defs_obj = {
291            self.utils._DISK_IOPS_METRICS: mock.sentinel.metrics}
292        mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
293                                   InstanceID=mock.sentinel.instance_id)
294        mock_get_vm_resources.return_value = [mock_disk]
295        mock_get_metrics_values.return_value = [mock.sentinel.iops]
296
297        disk_metrics = list(
298            self.utils.get_disk_iops_count(mock.sentinel.vm_name))
299
300        self.assertEqual(1, len(disk_metrics))
301        self.assertEqual(mock.sentinel.iops,
302                         disk_metrics[0]['iops_count'])
303        self.assertEqual(mock.sentinel.instance_id,
304                         disk_metrics[0]['instance_id'])
305        mock_get_vm_resources.assert_called_once_with(
306            mock.sentinel.vm_name,
307            self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
308        mock_get_metrics_values.assert_called_once_with(
309            mock_disk, [mock.sentinel.metrics])
310
311    def test_sum_metrics_values(self):
312        mock_metric = mock.MagicMock(MetricValue='100')
313        result = self.utils._sum_metrics_values([mock_metric] * 2)
314        self.assertEqual(200, result)
315
316    def test_sum_metrics_values_by_defs(self):
317        mock_metric = mock.MagicMock(MetricDefinitionId=mock.sentinel.def_id,
318                                     MetricValue='100')
319        mock_metric_useless = mock.MagicMock(MetricValue='200')
320        mock_metric_def = mock.MagicMock(Id=mock.sentinel.def_id)
321
322        result = self.utils._sum_metrics_values_by_defs(
323            [mock_metric, mock_metric_useless], [None, mock_metric_def])
324
325        self.assertEqual([0, 100], result)
326
327    def test_get_metrics_value_instances(self):
328        FAKE_CLASS_NAME = "FAKE_CLASS"
329        mock_el_metric = mock.MagicMock()
330        mock_el_metric_2 = mock.MagicMock()
331        mock_el_metric_2.path.return_value = mock.Mock(Class=FAKE_CLASS_NAME)
332
333        self.utils._conn.Msvm_MetricForME.side_effect = [
334            [], [mock.Mock(Dependent=mock_el_metric_2)]]
335
336        returned = self.utils._get_metrics_value_instances(
337            [mock_el_metric, mock_el_metric_2], FAKE_CLASS_NAME)
338
339        expected_return = [mock_el_metric_2]
340        self.assertEqual(expected_return, returned)
341
342    @mock.patch.object(metricsutils.MetricsUtils,
343                       '_sum_metrics_values_by_defs')
344    def test_get_metrics_values(self, mock_sum_by_defs):
345        mock_element = mock.MagicMock()
346        self.utils._conn.Msvm_MetricForME.return_value = [
347            mock.Mock(Dependent=mock.sentinel.metric),
348            mock.Mock(Dependent=mock.sentinel.another_metric)]
349
350        resulted_metrics_sum = self.utils._get_metrics_values(
351            mock_element, mock.sentinel.metrics_defs)
352
353        self.utils._conn.Msvm_MetricForME.assert_called_once_with(
354            Antecedent=mock_element.path_.return_value)
355        mock_sum_by_defs.assert_called_once_with(
356            [mock.sentinel.metric, mock.sentinel.another_metric],
357            mock.sentinel.metrics_defs)
358        expected_metrics_sum = mock_sum_by_defs.return_value
359        self.assertEqual(expected_metrics_sum, resulted_metrics_sum)
360
361    @mock.patch.object(metricsutils.MetricsUtils, '_filter_metrics')
362    def test_get_metrics(self, mock_filter_metrics):
363        mock_metric = mock.MagicMock()
364        mock_element = mock.MagicMock()
365        self.utils._conn.Msvm_MetricForME.return_value = [mock_metric]
366
367        result = self.utils._get_metrics(mock_element,
368                                         mock.sentinel.metrics_def)
369
370        self.assertEqual(mock_filter_metrics.return_value, result)
371        self.utils._conn.Msvm_MetricForME.assert_called_once_with(
372            Antecedent=mock_element.path_.return_value)
373        mock_filter_metrics.assert_called_once_with(
374            [mock_metric.Dependent],
375            mock.sentinel.metrics_def)
376
377    def test_filter_metrics(self):
378        mock_metric = mock.MagicMock(MetricDefinitionId=mock.sentinel.def_id)
379        mock_bad_metric = mock.MagicMock()
380        mock_metric_def = mock.MagicMock(Id=mock.sentinel.def_id)
381
382        result = self.utils._filter_metrics([mock_bad_metric, mock_metric],
383                                            mock_metric_def)
384
385        self.assertEqual([mock_metric], result)
386
387    @mock.patch.object(_wqlutils, 'get_element_associated_class')
388    @mock.patch.object(metricsutils.MetricsUtils, '_get_vm_setting_data')
389    def test_get_vm_resources(self, mock_get_vm_setting_data,
390                              mock_get_element_associated_class):
391        result = self.utils._get_vm_resources(mock.sentinel.vm_name,
392                                              mock.sentinel.resource_class)
393
394        mock_get_vm_setting_data.assert_called_once_with(mock.sentinel.vm_name)
395        vm_setting_data = mock_get_vm_setting_data.return_value
396        mock_get_element_associated_class.assert_called_once_with(
397            self.utils._conn, mock.sentinel.resource_class,
398            element_instance_id=vm_setting_data.InstanceID)
399        self.assertEqual(mock_get_element_associated_class.return_value,
400                         result)
401
402    @mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
403    def test_get_vm(self, mock_unique_result):
404        result = self.utils._get_vm(mock.sentinel.vm_name)
405
406        self.assertEqual(mock_unique_result.return_value, result)
407        conn_class = self.utils._conn.Msvm_ComputerSystem
408        conn_class.assert_called_once_with(ElementName=mock.sentinel.vm_name)
409        mock_unique_result.assert_called_once_with(conn_class.return_value,
410                                                   mock.sentinel.vm_name)
411
412    @mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
413    def test_get_switch_port(self, mock_unique_result):
414        result = self.utils._get_switch_port(mock.sentinel.port_name)
415
416        self.assertEqual(mock_unique_result.return_value, result)
417        conn_class = self.utils._conn.Msvm_EthernetPortAllocationSettingData
418        conn_class.assert_called_once_with(ElementName=mock.sentinel.port_name)
419        mock_unique_result.assert_called_once_with(conn_class.return_value,
420                                                   mock.sentinel.port_name)
421
422    @mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
423    def test_get_vm_setting_data(self, mock_unique_result):
424        result = self.utils._get_vm_setting_data(mock.sentinel.vm_name)
425
426        self.assertEqual(mock_unique_result.return_value, result)
427        conn_class = self.utils._conn.Msvm_VirtualSystemSettingData
428        conn_class.assert_called_once_with(ElementName=mock.sentinel.vm_name)
429        mock_unique_result.assert_called_once_with(conn_class.return_value,
430                                                   mock.sentinel.vm_name)
431
432    def test_unique_result_not_found(self):
433        self.assertRaises(exceptions.NotFound,
434                          self.utils._unique_result,
435                          [], mock.sentinel.resource_name)
436
437    def test_unique_result_duplicate(self):
438        self.assertRaises(exceptions.OSWinException,
439                          self.utils._unique_result,
440                          [mock.ANY, mock.ANY], mock.sentinel.resource_name)
441
442    def test_unique_result(self):
443        result = self.utils._unique_result([mock.sentinel.obj],
444                                           mock.sentinel.resource_name)
445        self.assertEqual(mock.sentinel.obj, result)
446