Merge "Fixes the Hyper-V agent individual ports metrics"

This commit is contained in:
Jenkins 2014-03-21 07:03:31 +00:00 committed by Gerrit Code Review
commit 386aa20710
5 changed files with 221 additions and 1 deletions

View File

@ -62,7 +62,14 @@ agent_opts = [
'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.'))
]
@ -119,6 +126,7 @@ class HyperVNeutronAgent(object):
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()
@ -294,6 +302,7 @@ class HyperVNeutronAgent(object):
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)
@ -308,6 +317,22 @@ class HyperVNeutronAgent(object):
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:
@ -413,6 +438,8 @@ class HyperVNeutronAgent(object):
# 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

View File

@ -247,3 +247,10 @@ class HyperVUtils(object):
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

View File

@ -33,6 +33,10 @@ class HyperVUtilsV2(utils.HyperVUtils):
_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
@ -43,6 +47,10 @@ class HyperVUtilsV2(utils.HyperVUtils):
_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
@ -212,6 +220,51 @@ class HyperVUtilsV2(utils.HyperVUtils):
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)

View File

@ -34,6 +34,8 @@ cfg.CONF.import_opt('enable_metrics_collection',
class TestHyperVNeutronAgent(base.BaseTestCase):
_FAKE_PORT_ID = 'fake_port_id'
def setUp(self):
super(TestHyperVNeutronAgent, self).setUp()
# Avoid rpc initialization for unit tests
@ -108,6 +110,40 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
'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)

View File

@ -40,6 +40,7 @@ class TestHyperVUtilsV2(base.BaseTestCase):
_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'
@ -249,6 +250,102 @@ class TestHyperVUtilsV2(base.BaseTestCase):
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'