'Hyper-V\'s metric APIs. Collected data can by '
'retrieved by other apps and services, e.g.: '
'Ceilometer. Requires Hyper-V / Windows Server 2012 '
- 'and above'))
+ 'and above')),
+ cfg.IntOpt('metrics_max_retries',
+ default=100,
+ help=_('Specifies the maximum number of retries to enable '
+ 'Hyper-V\'s port metrics collection. The agent will try '
+ 'to enable the feature once every polling_interval '
+ 'period for at most metrics_max_retries or until it '
+ 'succeedes.'))
]
self._polling_interval = CONF.AGENT.polling_interval
self._load_physical_network_mappings()
self._network_vswitch_map = {}
+ self._port_metric_retries = {}
self._set_agent_state()
self._setup_rpc()
if CONF.AGENT.enable_metrics_collection:
self._utils.enable_port_metrics_collection(port_id)
+ self._port_metric_retries[port_id] = CONF.AGENT.metrics_max_retries
def _port_unbound(self, port_id):
(net_uuid, map) = self._get_network_vswitch_map_by_port_id(port_id)
if not map['ports']:
self._reclaim_local_network(net_uuid)
+ def _port_enable_control_metrics(self):
+ if not CONF.AGENT.enable_metrics_collection:
+ return
+
+ for port_id in self._port_metric_retries.keys():
+ if self._utils.can_enable_control_metrics(port_id):
+ self._utils.enable_control_metrics(port_id)
+ LOG.info(_('Port metrics enabled for port: %s'), port_id)
+ del self._port_metric_retries[port_id]
+ elif self._port_metric_retries[port_id] < 1:
+ self._utils.enable_control_metrics(port_id)
+ LOG.error(_('Port metrics raw enabling for port: %s'), port_id)
+ del self._port_metric_retries[port_id]
+ else:
+ self._port_metric_retries[port_id] -= 1
+
def _update_ports(self, registered_ports):
ports = self._utils.get_vnic_ids()
if ports == registered_ports:
# If treat devices fails - must resync with plugin
sync = self._process_network_ports(port_info)
ports = port_info['current']
+
+ self._port_enable_control_metrics()
except Exception as e:
LOG.exception(_("Error in agent event loop: %s"), e)
sync = True
def enable_port_metrics_collection(self, switch_port_name):
raise NotImplementedError(_("Metrics collection is not supported on "
"this version of Hyper-V"))
+
+ def enable_control_metrics(self, switch_port_name):
+ raise NotImplementedError(_("Metrics collection is not supported on "
+ "this version of Hyper-V"))
+
+ def can_enable_control_metrics(self, switch_port_name):
+ return False
_STATE_DISABLED = 3
_OPERATION_MODE_ACCESS = 1
+ _VIRTUAL_SYSTEM_SETTING_DATA = 'Msvm_VirtualSystemSettingData'
+ _VM_SUMMARY_ENABLED_STATE = 100
+ _HYPERV_VM_STATE_ENABLED = 2
+
_ACL_DIR_IN = 1
_ACL_DIR_OUT = 2
_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
acl_dir, acl_type, self._ACL_ACTION_METER)
self._add_virt_feature(port, acl)
+ 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:
+ return False
+
+ if not self._is_port_vm_started(port):
+ return False
+
+ # all 4 meter ACLs must be existent first. (2 x direction)
+ acls = port.associators(wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA)
+ acls = [a for a in acls if a.Action == self._ACL_ACTION_METER]
+ if len(acls) < 2:
+ return False
+ return True
+
+ def _is_port_vm_started(self, port):
+ vs_man_svc = self._conn.Msvm_VirtualSystemManagementService()[0]
+ vmsettings = port.associators(
+ wmi_result_class=self._VIRTUAL_SYSTEM_SETTING_DATA)
+ #See http://msdn.microsoft.com/en-us/library/cc160706%28VS.85%29.aspx
+ (ret_val, summary_info) = vs_man_svc.GetSummaryInformation(
+ [self._VM_SUMMARY_ENABLED_STATE],
+ [v.path_() for v in vmsettings])
+ if ret_val or not summary_info:
+ raise utils.HyperVException(msg=_('Cannot get VM summary data '
+ 'for: %s') % port.ElementName)
+
+ return summary_info[0].EnabledState is self._HYPERV_VM_STATE_ENABLED
+
def create_security_rule(self, switch_port_name, direction, acl_type,
local_port, protocol, remote_address):
port, found = self._get_switch_port_allocation(switch_port_name, False)
class TestHyperVNeutronAgent(base.BaseTestCase):
+ _FAKE_PORT_ID = 'fake_port_id'
+
def setUp(self):
super(TestHyperVNeutronAgent, self).setUp()
self.addCleanup(cfg.CONF.reset)
'disconnect_switch_port'):
self.agent._port_unbound(net_uuid)
+ def test_port_enable_control_metrics_ok(self):
+ cfg.CONF.set_override('enable_metrics_collection', True, 'AGENT')
+ self.agent._port_metric_retries[self._FAKE_PORT_ID] = (
+ cfg.CONF.AGENT.metrics_max_retries)
+
+ with mock.patch.multiple(self.agent._utils,
+ can_enable_control_metrics=mock.MagicMock(),
+ enable_control_metrics=mock.MagicMock()):
+
+ self.agent._utils.can_enable_control_metrics.return_value = True
+ self.agent._port_enable_control_metrics()
+ self.agent._utils.enable_control_metrics.assert_called_with(
+ self._FAKE_PORT_ID)
+
+ self.assertNotIn(self._FAKE_PORT_ID, self.agent._port_metric_retries)
+
+ def test_port_enable_control_metrics_maxed(self):
+ cfg.CONF.set_override('enable_metrics_collection', True, 'AGENT')
+ cfg.CONF.set_override('metrics_max_retries', 3, 'AGENT')
+ self.agent._port_metric_retries[self._FAKE_PORT_ID] = (
+ cfg.CONF.AGENT.metrics_max_retries)
+
+ with mock.patch.multiple(self.agent._utils,
+ can_enable_control_metrics=mock.MagicMock(),
+ enable_control_metrics=mock.MagicMock()):
+
+ self.agent._utils.can_enable_control_metrics.return_value = False
+ for i in range(cfg.CONF.AGENT.metrics_max_retries + 1):
+ self.assertIn(self._FAKE_PORT_ID,
+ self.agent._port_metric_retries)
+ self.agent._port_enable_control_metrics()
+
+ self.assertNotIn(self._FAKE_PORT_ID, self.agent._port_metric_retries)
+
def test_treat_devices_added_returns_true_for_missing_device(self):
attrs = {'get_device_details.side_effect': Exception()}
self.agent.plugin_rpc.configure_mock(**attrs)
_FAKE_VLAN_ID = "fake_vlan_id"
_FAKE_CLASS_NAME = "fake_class_name"
_FAKE_ELEMENT_NAME = "fake_element_name"
+ _FAKE_HYPERV_VM_STATE = 'fake_hyperv_state'
_FAKE_ACL_ACT = 'fake_acl_action'
_FAKE_ACL_DIR = 'fake_acl_dir'
self._utils._add_virt_feature.assert_called_with(
mock_port, mock_acl)
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._get_switch_port_allocation')
+ def test_enable_control_metrics_ok(self, mock_get_port_allocation):
+ mock_metrics_svc = self._utils._conn.Msvm_MetricService()[0]
+ mock_metrics_def_source = self._utils._conn.CIM_BaseMetricDefinition
+ mock_metric_def = mock.MagicMock()
+ mock_port = mock.MagicMock()
+ mock_get_port_allocation.return_value = (mock_port, True)
+
+ 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._utils._METRIC_ENABLED)
+
+ self._utils.enable_control_metrics(self._FAKE_PORT_NAME)
+
+ mock_metrics_svc.ControlMetrics.assert_has_calls([m_call, m_call])
+
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._get_switch_port_allocation')
+ def test_enable_control_metrics_no_port(self, mock_get_port_allocation):
+ mock_metrics_svc = self._utils._conn.Msvm_MetricService()[0]
+ mock_get_port_allocation.return_value = (None, False)
+
+ self._utils.enable_control_metrics(self._FAKE_PORT_NAME)
+ self.assertEqual(0, mock_metrics_svc.ControlMetrics.call_count)
+
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._get_switch_port_allocation')
+ def test_enable_control_metrics_no_def(self, mock_get_port_allocation):
+ mock_metrics_svc = self._utils._conn.Msvm_MetricService()[0]
+ mock_metrics_def_source = self._utils._conn.CIM_BaseMetricDefinition
+ mock_port = mock.MagicMock()
+
+ mock_get_port_allocation.return_value = (mock_port, True)
+ mock_metrics_def_source.return_value = None
+
+ self._utils.enable_control_metrics(self._FAKE_PORT_NAME)
+ self.assertEqual(0, mock_metrics_svc.ControlMetrics.call_count)
+
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._is_port_vm_started')
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._get_switch_port_allocation')
+ def test_can_enable_control_metrics_true(self, mock_get, mock_is_started):
+ mock_acl = mock.MagicMock()
+ mock_acl.Action = self._utils._ACL_ACTION_METER
+ self._test_can_enable_control_metrics(mock_get, mock_is_started,
+ [mock_acl, mock_acl], True)
+
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._is_port_vm_started')
+ @mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
+ '._get_switch_port_allocation')
+ def test_can_enable_control_metrics_false(self, mock_get, mock_is_started):
+ self._test_can_enable_control_metrics(mock_get, mock_is_started, [],
+ False)
+
+ def _test_can_enable_control_metrics(self, mock_get_port, mock_vm_started,
+ acls, expected_result):
+ mock_port = mock.MagicMock()
+ mock_acl = mock.MagicMock()
+ mock_acl.Action = self._utils._ACL_ACTION_METER
+
+ mock_port.associators.return_value = acls
+ mock_get_port.return_value = (mock_port, True)
+ mock_vm_started.return_value = True
+
+ result = self._utils.can_enable_control_metrics(self._FAKE_PORT_NAME)
+ self.assertEqual(expected_result, result)
+
+ def test_is_port_vm_started_true(self):
+ self._test_is_port_vm_started(self._utils._HYPERV_VM_STATE_ENABLED,
+ True)
+
+ def test_is_port_vm_started_false(self):
+ self._test_is_port_vm_started(self._FAKE_HYPERV_VM_STATE, False)
+
+ def _test_is_port_vm_started(self, vm_state, expected_result):
+ mock_svc = self._utils._conn.Msvm_VirtualSystemManagementService()[0]
+ mock_port = mock.MagicMock()
+ mock_vmsettings = mock.MagicMock()
+ mock_summary = mock.MagicMock()
+ mock_summary.EnabledState = vm_state
+ mock_vmsettings.path_.return_value = self._FAKE_RES_PATH
+
+ mock_port.associators.return_value = [mock_vmsettings]
+ mock_svc.GetSummaryInformation.return_value = (self._FAKE_RET_VAL,
+ [mock_summary])
+
+ result = self._utils._is_port_vm_started(mock_port)
+ self.assertEqual(expected_result, result)
+ mock_svc.GetSummaryInformation.assert_called_once_with(
+ [self._utils._VM_SUMMARY_ENABLED_STATE],
+ [self._FAKE_RES_PATH])
+
@mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'
'._remove_virt_feature')
@mock.patch('neutron.plugins.hyperv.agent.utilsv2.HyperVUtilsV2'