Refactors metrics related utils
Adds metricsutils and test_metricsutils. It contains methods from networkutils, vmutils and ceilometer, all related to enabling, retrieving and processing virtual machine related metrics. Note: vmutils' enable_vm_metrics_collection will have to be removed once all its usages have updated to metricsutils. Partially-Implements: blueprint add-os-win-modules Change-Id: Ia937399c999d7293799a864a322323e3d2efc123
This commit is contained in:
parent
be7cc91201
commit
518ef3d06d
@ -34,7 +34,7 @@ class OSWinException(Exception):
|
||||
|
||||
|
||||
class NotFound(OSWinException):
|
||||
msg_fmt = _("Resource could not be found.")
|
||||
msg_fmt = _("Resource could not be found: %(resource)s")
|
||||
|
||||
|
||||
class HyperVException(OSWinException):
|
||||
|
@ -69,6 +69,7 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
self._vmutils = vmutils.VMUtils()
|
||||
self._vmutils._conn = mock.MagicMock()
|
||||
self._vmutils._jobutils = mock.MagicMock()
|
||||
self._vmutils._metricsutils = mock.MagicMock()
|
||||
self._vmutils._pathutils = mock.MagicMock()
|
||||
|
||||
def test_vs_man_svc(self):
|
||||
@ -579,40 +580,10 @@ class VMUtilsTestCase(test_base.OsWinBaseTestCase):
|
||||
getattr(mock_svc, self._DESTROY_SNAPSHOT).assert_called_with(
|
||||
self._FAKE_SNAPSHOT_PATH)
|
||||
|
||||
@mock.patch.object(vmutils.VMUtils, '_get_vm_disks')
|
||||
def test_enable_vm_metrics_collection(self, mock_get_vm_disks):
|
||||
self._lookup_vm()
|
||||
mock_svc = self._vmutils._conn.Msvm_MetricService()[0]
|
||||
|
||||
metric_def = mock.MagicMock()
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_disk.path_.return_value = self._FAKE_RES_PATH
|
||||
mock_get_vm_disks.return_value = ([mock_disk], [mock_disk])
|
||||
|
||||
fake_metric_def_paths = ['fake_0', 'fake_0', None]
|
||||
fake_metric_resource_paths = [self._FAKE_VM_PATH,
|
||||
self._FAKE_VM_PATH,
|
||||
self._FAKE_RES_PATH]
|
||||
|
||||
metric_def.path_.side_effect = fake_metric_def_paths
|
||||
self._vmutils._conn.CIM_BaseMetricDefinition.return_value = [
|
||||
metric_def]
|
||||
|
||||
self._vmutils.enable_vm_metrics_collection(self._FAKE_VM_NAME)
|
||||
|
||||
calls = [mock.call(Name=def_name)
|
||||
for def_name in [self._vmutils._METRIC_AGGR_CPU_AVG,
|
||||
self._vmutils._METRIC_AGGR_MEMORY_AVG]]
|
||||
self._vmutils._conn.CIM_BaseMetricDefinition.assert_has_calls(calls)
|
||||
|
||||
calls = []
|
||||
for i in range(len(fake_metric_def_paths)):
|
||||
calls.append(mock.call(
|
||||
Subject=fake_metric_resource_paths[i],
|
||||
Definition=fake_metric_def_paths[i],
|
||||
MetricCollectionEnabled=self._vmutils._METRIC_ENABLED))
|
||||
|
||||
mock_svc.ControlMetrics.assert_has_calls(calls, any_order=True)
|
||||
def test_enable_vm_metrics_collection(self):
|
||||
self._vmutils.enable_vm_metrics_collection(mock.sentinel.vm_name)
|
||||
method = self._vmutils._metricsutils.enable_vm_metrics_collection
|
||||
method.assert_called_once_with(mock.sentinel.vm_name)
|
||||
|
||||
def test_get_vm_dvd_disk_paths(self):
|
||||
mock_vm = self._lookup_vm()
|
||||
|
0
os_win/tests/utils/metrics/__init__.py
Normal file
0
os_win/tests/utils/metrics/__init__.py
Normal file
370
os_win/tests/utils/metrics/test_metricsutils.py
Normal file
370
os_win/tests/utils/metrics/test_metricsutils.py
Normal file
@ -0,0 +1,370 @@
|
||||
# Copyright 2015 Cloudbase Solutions Srl
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 mock
|
||||
from oslotest import base
|
||||
|
||||
from os_win import exceptions
|
||||
from os_win.utils.metrics import metricsutils
|
||||
|
||||
|
||||
class MetricsUtilsTestCase(base.BaseTestCase):
|
||||
"""Unit tests for the Hyper-V MetricsUtils class."""
|
||||
|
||||
_FAKE_RET_VAL = 0
|
||||
_FAKE_PORT = "fake's port name"
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_cache_metrics_defs')
|
||||
def setUp(self, mock_cache_metrics_defs):
|
||||
super(MetricsUtilsTestCase, self).setUp()
|
||||
self.utils = metricsutils.MetricsUtils()
|
||||
self.utils._conn_obj = mock.MagicMock()
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_enable_metrics')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
|
||||
def test_enable_vm_metrics_collection(
|
||||
self, mock_get_vm, mock_get_vm_resources, mock_enable_metrics):
|
||||
mock_vm = mock_get_vm.return_value
|
||||
mock_disk = mock.MagicMock()
|
||||
mock_dvd = mock.MagicMock(
|
||||
ResourceSubType=self.utils._DVD_DISK_RES_SUB_TYPE)
|
||||
mock_get_vm_resources.return_value = [mock_disk, mock_dvd]
|
||||
|
||||
self.utils.enable_vm_metrics_collection(mock.sentinel.vm_name)
|
||||
|
||||
metrics_names = [self.utils._CPU_METRICS,
|
||||
self.utils._MEMORY_METRICS]
|
||||
mock_enable_metrics.assert_has_calls(
|
||||
[mock.call(mock_disk), mock.call(mock_vm, metrics_names)])
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_enable_metrics')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_switch_port')
|
||||
def test_enable_switch_port_metrics_collection(self, mock_get_port,
|
||||
mock_enable_metrics):
|
||||
self.utils.enable_port_metrics_collection(mock.sentinel.port_name)
|
||||
|
||||
mock_get_port.assert_called_once_with(mock.sentinel.port_name)
|
||||
metrics = [self.utils._NET_IN_METRICS,
|
||||
self.utils._NET_OUT_METRICS]
|
||||
mock_enable_metrics.assert_called_once_with(
|
||||
mock_get_port.return_value, metrics)
|
||||
|
||||
def _check_enable_metrics(self, metrics=None, definition=None):
|
||||
mock_element = mock.MagicMock()
|
||||
|
||||
self.utils._enable_metrics(mock_element, metrics)
|
||||
|
||||
self.utils._metrics_svc.ControlMetrics.assert_called_once_with(
|
||||
Subject=mock_element.path_.return_value,
|
||||
Definition=definition,
|
||||
MetricCollectionEnabled=self.utils._METRICS_ENABLED)
|
||||
|
||||
def test_enable_metrics_no_metrics(self):
|
||||
self._check_enable_metrics()
|
||||
|
||||
def test_enable_metrics(self):
|
||||
metrics_name = self.utils._CPU_METRICS
|
||||
metrics_def = mock.MagicMock()
|
||||
self.utils._metrics_defs = {metrics_name: metrics_def}
|
||||
self._check_enable_metrics([metrics_name, mock.sentinel.metrics_name],
|
||||
metrics_def.path_.return_value)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_metrics')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
|
||||
def test_get_cpu_metrics(self, mock_get_vm, mock_get_vm_resources,
|
||||
mock_get_metrics):
|
||||
fake_cpu_count = 2
|
||||
fake_uptime = 1000
|
||||
fake_cpu_metrics_val = 2000
|
||||
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._CPU_METRICS: mock.sentinel.metrics}
|
||||
|
||||
mock_vm = mock_get_vm.return_value
|
||||
mock_vm.OnTimeInMilliseconds = fake_uptime
|
||||
mock_cpu = mock.MagicMock(VirtualQuantity=fake_cpu_count)
|
||||
mock_get_vm_resources.return_value = [mock_cpu]
|
||||
|
||||
mock_metric = mock.MagicMock(MetricValue=fake_cpu_metrics_val)
|
||||
mock_get_metrics.return_value = [mock_metric]
|
||||
|
||||
cpu_metrics = self.utils.get_cpu_metrics(mock.sentinel.vm_name)
|
||||
|
||||
self.assertEqual(3, len(cpu_metrics))
|
||||
self.assertEqual(fake_cpu_metrics_val, cpu_metrics[0])
|
||||
self.assertEqual(fake_cpu_count, cpu_metrics[1])
|
||||
self.assertEqual(fake_uptime, cpu_metrics[2])
|
||||
|
||||
mock_get_vm.assert_called_once_with(mock.sentinel.vm_name)
|
||||
mock_get_vm_resources.assert_called_once_with(
|
||||
mock.sentinel.vm_name, self.utils._PROCESSOR_SETTING_DATA_CLASS)
|
||||
mock_get_metrics.assert_called_once_with(mock_vm,
|
||||
mock.sentinel.metrics)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_metrics')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm')
|
||||
def test_get_memory_metrics(self, mock_get_vm, mock_get_metrics):
|
||||
mock_vm = mock_get_vm.return_value
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._MEMORY_METRICS: mock.sentinel.metrics}
|
||||
|
||||
metrics_memory = mock.MagicMock()
|
||||
metrics_memory.MetricValue = 3
|
||||
mock_get_metrics.return_value = [metrics_memory]
|
||||
|
||||
response = self.utils.get_memory_metrics(mock.sentinel.vm_name)
|
||||
|
||||
self.assertEqual(3, response)
|
||||
mock_get_vm.assert_called_once_with(mock.sentinel.vm_name)
|
||||
mock_get_metrics.assert_called_once_with(mock_vm,
|
||||
mock.sentinel.metrics)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils,
|
||||
'_sum_metrics_values_by_defs')
|
||||
@mock.patch.object(metricsutils.MetricsUtils,
|
||||
'_get_metrics_value_instances')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
def test_get_vnic_metrics(self, mock_get_vm_resources,
|
||||
mock_get_value_instances, mock_sum_by_defs):
|
||||
fake_rx_mb = 1000
|
||||
fake_tx_mb = 2000
|
||||
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._NET_IN_METRICS: mock.sentinel.net_in_metrics,
|
||||
self.utils._NET_OUT_METRICS: mock.sentinel.net_out_metrics}
|
||||
|
||||
mock_port = mock.MagicMock(Parent=mock.sentinel.vnic_path)
|
||||
mock_vnic = mock.MagicMock(ElementName=mock.sentinel.element_name,
|
||||
Address=mock.sentinel.address)
|
||||
mock_vnic.path_.return_value = mock.sentinel.vnic_path
|
||||
mock_get_vm_resources.side_effect = [[mock_port], [mock_vnic]]
|
||||
mock_sum_by_defs.return_value = [fake_rx_mb, fake_tx_mb]
|
||||
|
||||
vnic_metrics = list(
|
||||
self.utils.get_vnic_metrics(mock.sentinel.vm_name))
|
||||
|
||||
self.assertEqual(1, len(vnic_metrics))
|
||||
self.assertEqual(fake_rx_mb, vnic_metrics[0]['rx_mb'])
|
||||
self.assertEqual(fake_tx_mb, vnic_metrics[0]['tx_mb'])
|
||||
self.assertEqual(mock.sentinel.element_name,
|
||||
vnic_metrics[0]['element_name'])
|
||||
self.assertEqual(mock.sentinel.address, vnic_metrics[0]['address'])
|
||||
|
||||
mock_get_vm_resources.assert_has_calls([
|
||||
mock.call(mock.sentinel.vm_name, self.utils._PORT_ALLOC_SET_DATA),
|
||||
mock.call(mock.sentinel.vm_name,
|
||||
self.utils._SYNTH_ETH_PORT_SET_DATA)])
|
||||
mock_get_value_instances.assert_called_once_with(
|
||||
mock_port.associators.return_value, self.utils._BASE_METRICS_VALUE)
|
||||
mock_sum_by_defs.assert_called_once_with(
|
||||
mock_get_value_instances.return_value,
|
||||
[mock.sentinel.net_in_metrics, mock.sentinel.net_out_metrics])
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
def test_get_disk_metrics(self, mock_get_vm_resources,
|
||||
mock_get_metrics_values):
|
||||
fake_read_mb = 1000
|
||||
fake_write_mb = 2000
|
||||
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._DISK_RD_METRICS: mock.sentinel.disk_rd_metrics,
|
||||
self.utils._DISK_WR_METRICS: mock.sentinel.disk_wr_metrics}
|
||||
|
||||
mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
|
||||
InstanceID=mock.sentinel.instance_id)
|
||||
mock_get_vm_resources.return_value = [mock_disk]
|
||||
mock_get_metrics_values.return_value = [fake_read_mb, fake_write_mb]
|
||||
|
||||
disk_metrics = list(
|
||||
self.utils.get_disk_metrics(mock.sentinel.vm_name))
|
||||
|
||||
self.assertEqual(1, len(disk_metrics))
|
||||
self.assertEqual(fake_read_mb, disk_metrics[0]['read_mb'])
|
||||
self.assertEqual(fake_write_mb, disk_metrics[0]['write_mb'])
|
||||
self.assertEqual(mock.sentinel.instance_id,
|
||||
disk_metrics[0]['instance_id'])
|
||||
self.assertEqual(mock.sentinel.host_resource,
|
||||
disk_metrics[0]['host_resource'])
|
||||
|
||||
mock_get_vm_resources.assert_called_once_with(
|
||||
mock.sentinel.vm_name,
|
||||
self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
metrics = [mock.sentinel.disk_rd_metrics,
|
||||
mock.sentinel.disk_wr_metrics]
|
||||
mock_get_metrics_values.assert_called_once_with(mock_disk, metrics)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
def test_get_disk_latency_metrics(self, mock_get_vm_resources,
|
||||
mock_get_metrics_values):
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._DISK_LATENCY_METRICS: mock.sentinel.metrics}
|
||||
|
||||
mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
|
||||
InstanceID=mock.sentinel.instance_id)
|
||||
mock_get_vm_resources.return_value = [mock_disk]
|
||||
mock_get_metrics_values.return_value = [mock.sentinel.latency]
|
||||
|
||||
disk_metrics = list(
|
||||
self.utils.get_disk_latency_metrics(mock.sentinel.vm_name))
|
||||
|
||||
self.assertEqual(1, len(disk_metrics))
|
||||
self.assertEqual(mock.sentinel.latency,
|
||||
disk_metrics[0]['disk_latency'])
|
||||
self.assertEqual(mock.sentinel.instance_id,
|
||||
disk_metrics[0]['instance_id'])
|
||||
mock_get_vm_resources.assert_called_once_with(
|
||||
mock.sentinel.vm_name,
|
||||
self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
mock_get_metrics_values.assert_called_once_with(
|
||||
mock_disk, [mock.sentinel.metrics])
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_metrics_values')
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_resources')
|
||||
def test_get_disk_iops_metrics(self, mock_get_vm_resources,
|
||||
mock_get_metrics_values):
|
||||
self.utils._metrics_defs = {
|
||||
self.utils._DISK_IOPS_METRICS: mock.sentinel.metrics}
|
||||
mock_disk = mock.MagicMock(HostResource=[mock.sentinel.host_resource],
|
||||
InstanceID=mock.sentinel.instance_id)
|
||||
mock_get_vm_resources.return_value = [mock_disk]
|
||||
mock_get_metrics_values.return_value = [mock.sentinel.iops]
|
||||
|
||||
disk_metrics = list(
|
||||
self.utils.get_disk_iops_count(mock.sentinel.vm_name))
|
||||
|
||||
self.assertEqual(1, len(disk_metrics))
|
||||
self.assertEqual(mock.sentinel.iops,
|
||||
disk_metrics[0]['iops_count'])
|
||||
self.assertEqual(mock.sentinel.instance_id,
|
||||
disk_metrics[0]['instance_id'])
|
||||
mock_get_vm_resources.assert_called_once_with(
|
||||
mock.sentinel.vm_name,
|
||||
self.utils._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
mock_get_metrics_values.assert_called_once_with(
|
||||
mock_disk, [mock.sentinel.metrics])
|
||||
|
||||
def test_sum_metrics_values(self):
|
||||
mock_metric = mock.MagicMock(MetricValue='100')
|
||||
result = self.utils._sum_metrics_values([mock_metric] * 2)
|
||||
self.assertEqual(200, result)
|
||||
|
||||
def test_sum_metrics_values_by_defs(self):
|
||||
mock_metric = mock.MagicMock(MetricDefinitionId=mock.sentinel.def_id,
|
||||
MetricValue='100')
|
||||
mock_metric_useless = mock.MagicMock(MetricValue='200')
|
||||
mock_metric_def = mock.MagicMock(Id=mock.sentinel.def_id)
|
||||
|
||||
result = self.utils._sum_metrics_values_by_defs(
|
||||
[mock_metric, mock_metric_useless], [None, mock_metric_def])
|
||||
|
||||
self.assertEqual([0, 100], result)
|
||||
|
||||
def test_get_metrics_value_instances(self):
|
||||
mock_element = mock.MagicMock()
|
||||
mock_associator = mock.MagicMock()
|
||||
mock_element.associators.return_value = [mock_associator]
|
||||
|
||||
mock_element2 = mock.MagicMock()
|
||||
mock_element2.associators.return_value = []
|
||||
|
||||
returned = self.utils._get_metrics_value_instances(
|
||||
[mock_element, mock_element2], mock.sentinel.result_class)
|
||||
|
||||
self.assertEqual([mock_associator], returned)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_filter_metrics')
|
||||
def test_get_metrics(self, mock_filter_metrics):
|
||||
mock_metric = mock.MagicMock()
|
||||
mock_element = mock.MagicMock()
|
||||
mock_element.associators.return_value = [mock_metric]
|
||||
|
||||
result = self.utils._get_metrics(mock_element,
|
||||
mock.sentinel.metrics_def)
|
||||
|
||||
self.assertEqual(mock_filter_metrics.return_value, result)
|
||||
mock_filter_metrics.assert_called_once_with([mock_metric],
|
||||
mock.sentinel.metrics_def)
|
||||
|
||||
def test_filter_metrics(self):
|
||||
mock_metric = mock.MagicMock(MetricDefinitionId=mock.sentinel.def_id)
|
||||
mock_bad_metric = mock.MagicMock()
|
||||
mock_metric_def = mock.MagicMock(Id=mock.sentinel.def_id)
|
||||
|
||||
result = self.utils._filter_metrics([mock_bad_metric, mock_metric],
|
||||
mock_metric_def)
|
||||
|
||||
self.assertEqual([mock_metric], result)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_get_vm_setting_data')
|
||||
def test_get_vm_resources(self, mock_get_vm_setting_data):
|
||||
result = self.utils._get_vm_resources(mock.sentinel.vm_name,
|
||||
mock.sentinel.resource_class)
|
||||
|
||||
associators = mock_get_vm_setting_data.return_value.associators
|
||||
mock_get_vm_setting_data.assert_called_once_with(mock.sentinel.vm_name)
|
||||
associators.assert_called_once_with(
|
||||
wmi_result_class=mock.sentinel.resource_class)
|
||||
self.assertEqual(associators.return_value, result)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
|
||||
def test_get_vm(self, mock_unique_result):
|
||||
result = self.utils._get_vm(mock.sentinel.vm_name)
|
||||
|
||||
self.assertEqual(mock_unique_result.return_value, result)
|
||||
conn_class = self.utils._conn.Msvm_ComputerSystem
|
||||
conn_class.assert_called_once_with(ElementName=mock.sentinel.vm_name)
|
||||
mock_unique_result.assert_called_once_with(conn_class.return_value,
|
||||
mock.sentinel.vm_name)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
|
||||
def test_get_switch_port(self, mock_unique_result):
|
||||
result = self.utils._get_switch_port(mock.sentinel.port_name)
|
||||
|
||||
self.assertEqual(mock_unique_result.return_value, result)
|
||||
conn_class = self.utils._conn.Msvm_SyntheticEthernetPortSettingData
|
||||
conn_class.assert_called_once_with(ElementName=mock.sentinel.port_name)
|
||||
mock_unique_result.assert_called_once_with(conn_class.return_value,
|
||||
mock.sentinel.port_name)
|
||||
|
||||
@mock.patch.object(metricsutils.MetricsUtils, '_unique_result')
|
||||
def test_get_vm_setting_data(self, mock_unique_result):
|
||||
result = self.utils._get_vm_setting_data(mock.sentinel.vm_name)
|
||||
|
||||
self.assertEqual(mock_unique_result.return_value, result)
|
||||
conn_class = self.utils._conn.Msvm_VirtualSystemSettingData
|
||||
conn_class.assert_called_once_with(
|
||||
ElementName=mock.sentinel.vm_name,
|
||||
VirtualSystemType=self.utils._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
mock_unique_result.assert_called_once_with(conn_class.return_value,
|
||||
mock.sentinel.vm_name)
|
||||
|
||||
def test_unique_result_not_found(self):
|
||||
self.assertRaises(exceptions.NotFound,
|
||||
self.utils._unique_result,
|
||||
[], mock.sentinel.resource_name)
|
||||
|
||||
def test_unique_result_duplicate(self):
|
||||
self.assertRaises(exceptions.OSWinException,
|
||||
self.utils._unique_result,
|
||||
[mock.ANY, mock.ANY], mock.sentinel.resource_name)
|
||||
|
||||
def test_unique_result(self):
|
||||
result = self.utils._unique_result([mock.sentinel.obj],
|
||||
mock.sentinel.resource_name)
|
||||
self.assertEqual(mock.sentinel.obj, result)
|
@ -316,39 +316,6 @@ class NetworkUtilsTestCase(base.BaseTestCase):
|
||||
self.assertEqual(4, actual_calls)
|
||||
mock_add_feature.assert_called_with(mock_acl, mock_port)
|
||||
|
||||
def test_enable_control_metrics_ok(self):
|
||||
mock_metrics_svc = self.netutils._conn.Msvm_MetricService()[0]
|
||||
mock_metrics_def_source = self.netutils._conn.CIM_BaseMetricDefinition
|
||||
mock_metric_def = mock.MagicMock()
|
||||
mock_port = self._mock_get_switch_port_alloc()
|
||||
|
||||
mock_metrics_def_source.return_value = [mock_metric_def]
|
||||
m_call = mock.call(
|
||||
Subject=mock_port.path_.return_value,
|
||||
Definition=mock_metric_def.path_.return_value,
|
||||
MetricCollectionEnabled=self.netutils._METRIC_ENABLED)
|
||||
|
||||
self.netutils.enable_control_metrics(self._FAKE_PORT_NAME)
|
||||
|
||||
mock_metrics_svc.ControlMetrics.assert_has_calls([m_call, m_call])
|
||||
|
||||
def test_enable_control_metrics_no_port(self):
|
||||
mock_metrics_svc = self.netutils._conn.Msvm_MetricService()[0]
|
||||
self._mock_get_switch_port_alloc(found=False)
|
||||
|
||||
self.netutils.enable_control_metrics(self._FAKE_PORT_NAME)
|
||||
self.assertEqual(0, mock_metrics_svc.ControlMetrics.call_count)
|
||||
|
||||
def test_enable_control_metrics_no_def(self):
|
||||
mock_metrics_svc = self.netutils._conn.Msvm_MetricService()[0]
|
||||
mock_metrics_def_source = self.netutils._conn.CIM_BaseMetricDefinition
|
||||
|
||||
self._mock_get_switch_port_alloc()
|
||||
mock_metrics_def_source.return_value = None
|
||||
|
||||
self.netutils.enable_control_metrics(self._FAKE_PORT_NAME)
|
||||
self.assertEqual(0, mock_metrics_svc.ControlMetrics.call_count)
|
||||
|
||||
@mock.patch.object(networkutils.NetworkUtils, '_is_port_vm_started')
|
||||
def test_can_enable_control_metrics_true(self, mock_is_started):
|
||||
mock_acl = mock.MagicMock()
|
||||
|
@ -26,6 +26,7 @@ class FakeCPUSpec(object):
|
||||
Architecture = mock.sentinel.cpu_arch
|
||||
Name = mock.sentinel.cpu_name
|
||||
Manufacturer = mock.sentinel.cpu_man
|
||||
MaxClockSpeed = mock.sentinel.max_clock_speed
|
||||
NumberOfCores = mock.sentinel.cpu_cores
|
||||
NumberOfLogicalProcessors = mock.sentinel.cpu_procs
|
||||
|
||||
|
@ -36,6 +36,7 @@ from os_win._i18n import _, _LW
|
||||
from os_win import constants
|
||||
from os_win import exceptions
|
||||
from os_win.utils import jobutils
|
||||
from os_win.utils.metrics import metricsutils
|
||||
from os_win.utils import pathutils
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -72,9 +73,6 @@ class VMUtils(object):
|
||||
_VIRTUAL_SYSTEM_SUBTYPE_GEN2 = 'Microsoft:Hyper-V:SubType:2'
|
||||
|
||||
_SNAPSHOT_FULL = 2
|
||||
_METRIC_ENABLED = 2
|
||||
_METRIC_AGGR_CPU_AVG = 'Aggregated Average CPU Utilization'
|
||||
_METRIC_AGGR_MEMORY_AVG = 'Aggregated Average Memory Utilization'
|
||||
|
||||
_VM_ENABLED_STATE_PROP = "EnabledState"
|
||||
|
||||
@ -92,6 +90,7 @@ class VMUtils(object):
|
||||
def __init__(self, host='.'):
|
||||
self._vs_man_svc_attr = None
|
||||
self._jobutils = jobutils.JobUtils()
|
||||
self._metricsutils = metricsutils.MetricsUtils()
|
||||
self._pathutils = pathutils.PathUtils()
|
||||
self._enabled_states_map = {v: k for k, v in
|
||||
six.iteritems(self._vm_power_states_map)}
|
||||
@ -649,31 +648,10 @@ class VMUtils(object):
|
||||
self._jobutils.check_ret_val(ret_val, job_path)
|
||||
|
||||
def enable_vm_metrics_collection(self, vm_name):
|
||||
metric_names = [self._METRIC_AGGR_CPU_AVG,
|
||||
self._METRIC_AGGR_MEMORY_AVG]
|
||||
|
||||
vm = self._lookup_vm_check(vm_name)
|
||||
metric_svc = self._conn.Msvm_MetricService()[0]
|
||||
(disks, volumes) = self._get_vm_disks(vm)
|
||||
filtered_disks = [d for d in disks if
|
||||
d.ResourceSubType is not self._DVD_DISK_RES_SUB_TYPE]
|
||||
|
||||
# enable metrics for disk.
|
||||
for disk in filtered_disks:
|
||||
self._enable_metrics(metric_svc, disk)
|
||||
|
||||
for metric_name in metric_names:
|
||||
metric_def = self._conn.CIM_BaseMetricDefinition(Name=metric_name)
|
||||
if not metric_def:
|
||||
LOG.debug("Metric not found: %s", metric_name)
|
||||
else:
|
||||
self._enable_metrics(metric_svc, vm, metric_def[0].path_())
|
||||
|
||||
def _enable_metrics(self, metric_svc, element, definition_path=None):
|
||||
metric_svc.ControlMetrics(
|
||||
Subject=element.path_(),
|
||||
Definition=definition_path,
|
||||
MetricCollectionEnabled=self._METRIC_ENABLED)
|
||||
# TODO(claudiub): nova still calls this method, causing VMs to fail to
|
||||
# spawn if metrics collection is enabled. Remove this when the nova
|
||||
# compute HyperVDriver properly calls the metricsutils method instead.
|
||||
self._metricsutils.enable_vm_metrics_collection(vm_name)
|
||||
|
||||
def get_vm_dvd_disk_paths(self, vm_name):
|
||||
vm = self._lookup_vm_check(vm_name)
|
||||
|
@ -73,6 +73,7 @@ class HostUtils(object):
|
||||
cpu_info = {'Architecture': cpu.Architecture,
|
||||
'Name': cpu.Name,
|
||||
'Manufacturer': cpu.Manufacturer,
|
||||
'MaxClockSpeed': cpu.MaxClockSpeed,
|
||||
'NumberOfCores': cpu.NumberOfCores,
|
||||
'NumberOfLogicalProcessors':
|
||||
cpu.NumberOfLogicalProcessors}
|
||||
|
0
os_win/utils/metrics/__init__.py
Normal file
0
os_win/utils/metrics/__init__.py
Normal file
282
os_win/utils/metrics/metricsutils.py
Normal file
282
os_win/utils/metrics/metricsutils.py
Normal file
@ -0,0 +1,282 @@
|
||||
# Copyright 2016 Cloudbase Solutions Srl
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
"""
|
||||
Utility class for metrics related operations.
|
||||
Based on the "root/virtualization/v2" namespace available starting with
|
||||
Hyper-V Server / Windows Server 2012.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
if sys.platform == 'win32':
|
||||
import wmi
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from os_win._i18n import _, _LW
|
||||
from os_win import exceptions
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MetricsUtils(object):
|
||||
|
||||
_VIRTUAL_SYSTEM_TYPE_REALIZED = 'Microsoft:Hyper-V:System:Realized'
|
||||
_DVD_DISK_RES_SUB_TYPE = 'Microsoft:Hyper-V:Virtual CD/DVD Disk'
|
||||
_STORAGE_ALLOC_SETTING_DATA_CLASS = 'Msvm_StorageAllocationSettingData'
|
||||
_PROCESSOR_SETTING_DATA_CLASS = 'Msvm_ProcessorSettingData'
|
||||
_SYNTH_ETH_PORT_SET_DATA = 'Msvm_SyntheticEthernetPortSettingData'
|
||||
_PORT_ALLOC_SET_DATA = 'Msvm_EthernetPortAllocationSettingData'
|
||||
_PORT_ALLOC_ACL_SET_DATA = 'Msvm_EthernetSwitchPortAclSettingData'
|
||||
_METRICS_ME = 'Msvm_MetricForME'
|
||||
_BASE_METRICS_VALUE = 'Msvm_BaseMetricValue'
|
||||
|
||||
_CPU_METRICS = 'Aggregated Average CPU Utilization'
|
||||
_MEMORY_METRICS = 'Aggregated Average Memory Utilization'
|
||||
_NET_IN_METRICS = 'Filtered Incoming Network Traffic'
|
||||
_NET_OUT_METRICS = 'Filtered Outgoing Network Traffic'
|
||||
# Disk metrics are supported from Hyper-V 2012 R2
|
||||
_DISK_RD_METRICS = 'Disk Data Read'
|
||||
_DISK_WR_METRICS = 'Disk Data Written'
|
||||
_DISK_LATENCY_METRICS = 'Average Disk Latency'
|
||||
_DISK_IOPS_METRICS = 'Average Normalized Disk Throughput'
|
||||
|
||||
_METRICS_ENABLED = 2
|
||||
|
||||
_wmi_namespace = '//./root/virtualization/v2'
|
||||
|
||||
def __init__(self, host='.'):
|
||||
self._conn_obj = None
|
||||
self._metrics_svc_obj = None
|
||||
self._cache_metrics_defs()
|
||||
|
||||
@property
|
||||
def _conn(self):
|
||||
if not self._conn_obj:
|
||||
self._conn_obj = wmi.WMI(moniker=self._wmi_namespace)
|
||||
return self._conn_obj
|
||||
|
||||
@property
|
||||
def _metrics_svc(self):
|
||||
if not self._metrics_svc_obj:
|
||||
self._metrics_svc_obj = self._conn.Msvm_MetricService()[0]
|
||||
return self._metrics_svc_obj
|
||||
|
||||
def _cache_metrics_defs(self):
|
||||
self._metrics_defs = {}
|
||||
for metrics_def in self._conn.CIM_BaseMetricDefinition():
|
||||
self._metrics_defs[metrics_def.ElementName] = metrics_def
|
||||
|
||||
def enable_vm_metrics_collection(self, vm_name):
|
||||
vm = self._get_vm(vm_name)
|
||||
disks = self._get_vm_resources(vm_name,
|
||||
self._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
filtered_disks = [d for d in disks if
|
||||
d.ResourceSubType is not self._DVD_DISK_RES_SUB_TYPE]
|
||||
|
||||
# enable metrics for disk.
|
||||
for disk in filtered_disks:
|
||||
self._enable_metrics(disk)
|
||||
|
||||
metrics_names = [self._CPU_METRICS, self._MEMORY_METRICS]
|
||||
self._enable_metrics(vm, metrics_names)
|
||||
|
||||
def enable_port_metrics_collection(self, switch_port_name):
|
||||
port = self._get_switch_port(switch_port_name)
|
||||
metrics_names = [self._NET_IN_METRICS, self._NET_OUT_METRICS]
|
||||
self._enable_metrics(port, metrics_names)
|
||||
|
||||
def _enable_metrics(self, element, metrics_names=None):
|
||||
if not metrics_names:
|
||||
definition_paths = [None]
|
||||
else:
|
||||
definition_paths = []
|
||||
for metrics_name in metrics_names:
|
||||
metrics_def = self._metrics_defs.get(metrics_name)
|
||||
if not metrics_def:
|
||||
LOG.warning(_LW("Metric not found: %s"), metrics_name)
|
||||
continue
|
||||
definition_paths.append(metrics_def.path_())
|
||||
|
||||
element_path = element.path_()
|
||||
for definition_path in definition_paths:
|
||||
self._metrics_svc.ControlMetrics(
|
||||
Subject=element_path,
|
||||
Definition=definition_path,
|
||||
MetricCollectionEnabled=self._METRICS_ENABLED)
|
||||
|
||||
def get_cpu_metrics(self, vm_name):
|
||||
vm = self._get_vm(vm_name)
|
||||
cpu_sd = self._get_vm_resources(vm_name,
|
||||
self._PROCESSOR_SETTING_DATA_CLASS)[0]
|
||||
cpu_metrics_def = self._metrics_defs[self._CPU_METRICS]
|
||||
cpu_metrics_aggr = self._get_metrics(vm, cpu_metrics_def)
|
||||
|
||||
cpu_used = 0
|
||||
if cpu_metrics_aggr:
|
||||
cpu_used = int(cpu_metrics_aggr[0].MetricValue)
|
||||
|
||||
return (cpu_used,
|
||||
int(cpu_sd.VirtualQuantity),
|
||||
int(vm.OnTimeInMilliseconds))
|
||||
|
||||
def get_memory_metrics(self, vm_name):
|
||||
vm = self._get_vm(vm_name)
|
||||
memory_def = self._metrics_defs[self._MEMORY_METRICS]
|
||||
metrics_memory = self._get_metrics(vm, memory_def)
|
||||
memory_usage = 0
|
||||
if metrics_memory:
|
||||
memory_usage = int(metrics_memory[0].MetricValue)
|
||||
return memory_usage
|
||||
|
||||
def get_vnic_metrics(self, vm_name):
|
||||
ports = self._get_vm_resources(vm_name, self._PORT_ALLOC_SET_DATA)
|
||||
vnics = self._get_vm_resources(vm_name, self._SYNTH_ETH_PORT_SET_DATA)
|
||||
|
||||
metrics_def_in = self._metrics_defs[self._NET_IN_METRICS]
|
||||
metrics_def_out = self._metrics_defs[self._NET_OUT_METRICS]
|
||||
|
||||
for port in ports:
|
||||
vnic = [v for v in vnics if port.Parent == v.path_()][0]
|
||||
port_acls = port.associators(
|
||||
wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA)
|
||||
|
||||
metrics_value_instances = self._get_metrics_value_instances(
|
||||
port_acls, self._BASE_METRICS_VALUE)
|
||||
metrics_values = self._sum_metrics_values_by_defs(
|
||||
metrics_value_instances, [metrics_def_in, metrics_def_out])
|
||||
|
||||
yield {
|
||||
'rx_mb': metrics_values[0],
|
||||
'tx_mb': metrics_values[1],
|
||||
'element_name': vnic.ElementName,
|
||||
'address': vnic.Address
|
||||
}
|
||||
|
||||
def get_disk_metrics(self, vm_name):
|
||||
metrics_def_r = self._metrics_defs[self._DISK_RD_METRICS]
|
||||
metrics_def_w = self._metrics_defs[self._DISK_WR_METRICS]
|
||||
|
||||
disks = self._get_vm_resources(vm_name,
|
||||
self._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
for disk in disks:
|
||||
metrics_values = self._get_metrics_values(
|
||||
disk, [metrics_def_r, metrics_def_w])
|
||||
|
||||
yield {
|
||||
# Values are in megabytes
|
||||
'read_mb': metrics_values[0],
|
||||
'write_mb': metrics_values[1],
|
||||
'instance_id': disk.InstanceID,
|
||||
'host_resource': disk.HostResource[0]
|
||||
}
|
||||
|
||||
def get_disk_latency_metrics(self, vm_name):
|
||||
metrics_latency_def = self._metrics_defs[self._DISK_LATENCY_METRICS]
|
||||
|
||||
disks = self._get_vm_resources(vm_name,
|
||||
self._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
for disk in disks:
|
||||
metrics_values = self._get_metrics_values(
|
||||
disk, [metrics_latency_def])
|
||||
|
||||
yield {
|
||||
'disk_latency': metrics_values[0],
|
||||
'instance_id': disk.InstanceID,
|
||||
}
|
||||
|
||||
def get_disk_iops_count(self, vm_name):
|
||||
metrics_def_iops = self._metrics_defs[self._DISK_IOPS_METRICS]
|
||||
|
||||
disks = self._get_vm_resources(vm_name,
|
||||
self._STORAGE_ALLOC_SETTING_DATA_CLASS)
|
||||
for disk in disks:
|
||||
metrics_values = self._get_metrics_values(
|
||||
disk, [metrics_def_iops])
|
||||
|
||||
yield {
|
||||
'iops_count': metrics_values[0],
|
||||
'instance_id': disk.InstanceID,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _sum_metrics_values(metrics):
|
||||
return sum([int(metric.MetricValue) for metric in metrics])
|
||||
|
||||
def _sum_metrics_values_by_defs(self, element_metrics, metrics_defs):
|
||||
metrics_values = []
|
||||
for metrics_def in metrics_defs:
|
||||
if metrics_def:
|
||||
metrics = self._filter_metrics(element_metrics, metrics_def)
|
||||
metrics_values.append(self._sum_metrics_values(metrics))
|
||||
else:
|
||||
# In case the metric is not defined on this host
|
||||
metrics_values.append(0)
|
||||
return metrics_values
|
||||
|
||||
@staticmethod
|
||||
def _get_metrics_value_instances(elements, result_class):
|
||||
instances = []
|
||||
for el in elements:
|
||||
associators = el.associators(wmi_result_class=result_class)
|
||||
if associators:
|
||||
instances.append(associators[0])
|
||||
|
||||
return instances
|
||||
|
||||
def _get_metrics_values(self, element, metrics_defs):
|
||||
element_metrics = element.associators(
|
||||
wmi_association_class=self._METRICS_ME)
|
||||
return self._sum_metrics_values_by_defs(element_metrics, metrics_defs)
|
||||
|
||||
def _get_metrics(self, element, metrics_def):
|
||||
metrics = element.associators(wmi_association_class=self._METRICS_ME)
|
||||
return self._filter_metrics(metrics, metrics_def)
|
||||
|
||||
@staticmethod
|
||||
def _filter_metrics(all_metrics, metrics_def):
|
||||
return [v for v in all_metrics if
|
||||
v.MetricDefinitionId == metrics_def.Id]
|
||||
|
||||
def _get_vm_resources(self, vm_name, resource_class):
|
||||
setting_data = self._get_vm_setting_data(vm_name)
|
||||
return setting_data.associators(wmi_result_class=resource_class)
|
||||
|
||||
def _get_vm(self, vm_name):
|
||||
vms = self._conn.Msvm_ComputerSystem(ElementName=vm_name)
|
||||
return self._unique_result(vms, vm_name)
|
||||
|
||||
def _get_switch_port(self, port_name):
|
||||
ports = self._conn.Msvm_SyntheticEthernetPortSettingData(
|
||||
ElementName=port_name)
|
||||
return self._unique_result(ports, port_name)
|
||||
|
||||
def _get_vm_setting_data(self, vm_name):
|
||||
vssds = self._conn.Msvm_VirtualSystemSettingData(
|
||||
ElementName=vm_name,
|
||||
VirtualSystemType=self._VIRTUAL_SYSTEM_TYPE_REALIZED)
|
||||
return self._unique_result(vssds, vm_name)
|
||||
|
||||
@staticmethod
|
||||
def _unique_result(objects, resource_name):
|
||||
n = len(objects)
|
||||
if n == 0:
|
||||
raise exceptions.NotFound(resource=resource_name)
|
||||
elif n > 1:
|
||||
raise exceptions.OSWinException(
|
||||
_('Duplicate resource name found: %s') % resource_name)
|
||||
else:
|
||||
return objects[0]
|
@ -57,10 +57,6 @@ class NetworkUtils(object):
|
||||
_ACL_ACTION_DENY = 2
|
||||
_ACL_ACTION_METER = 3
|
||||
|
||||
_METRIC_ENABLED = 2
|
||||
_NET_IN_METRIC_NAME = 'Filtered Incoming Network Traffic'
|
||||
_NET_OUT_METRIC_NAME = 'Filtered Outgoing Network Traffic'
|
||||
|
||||
_ACL_APPLICABILITY_LOCAL = 1
|
||||
_ACL_APPLICABILITY_REMOTE = 2
|
||||
|
||||
@ -318,22 +314,6 @@ class NetworkUtils(object):
|
||||
acl_dir, acl_type, self._ACL_ACTION_METER)
|
||||
self._jobutils.add_virt_feature(acl, port)
|
||||
|
||||
def enable_control_metrics(self, switch_port_name):
|
||||
port, found = self._get_switch_port_allocation(switch_port_name, False)
|
||||
if not found:
|
||||
return
|
||||
|
||||
metric_svc = self._conn.Msvm_MetricService()[0]
|
||||
metric_names = [self._NET_IN_METRIC_NAME, self._NET_OUT_METRIC_NAME]
|
||||
|
||||
for metric_name in metric_names:
|
||||
metric_def = self._conn.CIM_BaseMetricDefinition(Name=metric_name)
|
||||
if metric_def:
|
||||
metric_svc.ControlMetrics(
|
||||
Subject=port.path_(),
|
||||
Definition=metric_def[0].path_(),
|
||||
MetricCollectionEnabled=self._METRIC_ENABLED)
|
||||
|
||||
def can_enable_control_metrics(self, switch_port_name):
|
||||
port, found = self._get_switch_port_allocation(switch_port_name, False)
|
||||
if not found:
|
||||
|
@ -76,6 +76,11 @@ utils_map = {
|
||||
'max_version': None,
|
||||
'path': 'os_win.utils.compute.livemigrationutils.'
|
||||
'LiveMigrationUtils'}},
|
||||
'metricsutils': {
|
||||
'MetricsUtils': {
|
||||
'min_version': 6.2,
|
||||
'max_version': None,
|
||||
'path': 'os_win.utils.metrics.metricsutils.MetricsUtils'}},
|
||||
'networkutils': {
|
||||
'NetworkUtils': {
|
||||
'min_version': 6.2,
|
||||
@ -143,6 +148,10 @@ def get_vhdutils():
|
||||
return _get_class(class_type='vhdutils')
|
||||
|
||||
|
||||
def get_metricsutils():
|
||||
return _get_class(class_type='metricsutils')
|
||||
|
||||
|
||||
def get_networkutils():
|
||||
return _get_class(class_type='networkutils')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user