diff --git a/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini b/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini index b3b5218062..236bc7e0f0 100644 --- a/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini +++ b/etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini @@ -39,6 +39,12 @@ # local_network_vswitch = private # Example: local_network_vswitch = custom_vswitch +# (BoolOpt) Enables metrics collections for switch ports by using 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. +# +# enable_metrics_collection = False + #----------------------------------------------------------------------------- # Sample Configurations. #----------------------------------------------------------------------------- diff --git a/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py b/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py index 6082086365..d5befb11c7 100644 --- a/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py +++ b/neutron/plugins/hyperv/agent/hyperv_neutron_agent.py @@ -52,6 +52,13 @@ agent_opts = [ cfg.IntOpt('polling_interval', default=2, help=_("The number of seconds the agent will wait between " "polling for local device changes.")), + cfg.BoolOpt('enable_metrics_collection', + default=False, + help=_('Enables metrics collections for switch ports by using ' + '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')) ] @@ -210,6 +217,9 @@ class HyperVNeutronAgent(object): else: LOG.error(_('Unsupported network type %s'), network_type) + if CONF.AGENT.enable_metrics_collection: + self._utils.enable_port_metrics_collection(port_id) + def _port_unbound(self, port_id): (net_uuid, map) = self._get_network_vswitch_map_by_port_id(port_id) if net_uuid not in self._network_vswitch_map: diff --git a/neutron/plugins/hyperv/agent/utils.py b/neutron/plugins/hyperv/agent/utils.py index 11bdf7c240..2b6d56f66f 100644 --- a/neutron/plugins/hyperv/agent/utils.py +++ b/neutron/plugins/hyperv/agent/utils.py @@ -243,3 +243,7 @@ class HyperVUtils(object): for switch_port in switch_ports: if (switch_port.ElementName == port_id): return switch_port + + def enable_port_metrics_collection(self, switch_port_name): + raise NotImplementedError(_("Metrics collection is not supported on " + "this version of Hyper-V")) diff --git a/neutron/plugins/hyperv/agent/utilsv2.py b/neutron/plugins/hyperv/agent/utilsv2.py index c0fbb5559c..73eed9824f 100644 --- a/neutron/plugins/hyperv/agent/utilsv2.py +++ b/neutron/plugins/hyperv/agent/utilsv2.py @@ -26,10 +26,18 @@ class HyperVUtilsV2(utils.HyperVUtils): _ETHERNET_SWITCH_PORT = 'Msvm_EthernetSwitchPort' _PORT_ALLOC_SET_DATA = 'Msvm_EthernetPortAllocationSettingData' _PORT_VLAN_SET_DATA = 'Msvm_EthernetSwitchPortVlanSettingData' + _PORT_ALLOC_ACL_SET_DATA = 'Msvm_EthernetSwitchPortAclSettingData' _LAN_ENDPOINT = 'Msvm_LANEndpoint' _STATE_DISABLED = 3 _OPERATION_MODE_ACCESS = 1 + _ACL_DIR_IN = 1 + _ACL_DIR_OUT = 2 + _ACL_TYPE_IPV4 = 2 + _ACL_TYPE_IPV6 = 3 + _ACL_ACTION_METER = 3 + _ACL_APPLICABILITY_LOCAL = 1 + _wmi_namespace = '//./root/virtualization/v2' def __init__(self): @@ -159,3 +167,26 @@ class HyperVUtilsV2(utils.HyperVUtils): def _get_first_item(self, obj): if obj: return obj[0] + + def enable_port_metrics_collection(self, switch_port_name): + port, found = self._get_switch_port_allocation(switch_port_name, False) + if not found: + return + + # Add the ACLs only if they don't already exist + acls = port.associators(wmi_result_class=self._PORT_ALLOC_ACL_SET_DATA) + for acl_type in [self._ACL_TYPE_IPV4, self._ACL_TYPE_IPV6]: + for acl_dir in [self._ACL_DIR_IN, self._ACL_DIR_OUT]: + acls = [v for v in acls + if v.Action == self._ACL_ACTION_METER and + v.Applicability == self._ACL_APPLICABILITY_LOCAL and + v.Direction == acl_dir and + v.AclType == acl_type] + if not acls: + acl = self._get_default_setting_data( + self._PORT_ALLOC_ACL_SET_DATA) + acl.AclType = acl_type + acl.Direction = acl_dir + acl.Action = self._ACL_ACTION_METER + acl.Applicability = self._ACL_APPLICABILITY_LOCAL + self._add_virt_feature(port, acl) diff --git a/neutron/tests/unit/hyperv/test_hyperv_utilsv2.py b/neutron/tests/unit/hyperv/test_hyperv_utilsv2.py index 5a40fdd6cf..76f88ead2b 100644 --- a/neutron/tests/unit/hyperv/test_hyperv_utilsv2.py +++ b/neutron/tests/unit/hyperv/test_hyperv_utilsv2.py @@ -213,3 +213,18 @@ class TestHyperVUtilsV2(base.BaseTestCase): True) self.assertEqual(ret_val, (mock_data, False)) + + def test_enable_port_metrics_collection(self): + mock_port = mock.MagicMock() + self._utils._get_switch_port_allocation = mock.MagicMock(return_value=( + mock_port, True)) + + mock_acl = mock.MagicMock() + self._utils._get_default_setting_data = mock.MagicMock( + return_value=mock_acl) + self._utils._add_virt_feature = mock.MagicMock() + + self._utils.enable_port_metrics_collection(self._FAKE_PORT_NAME) + + self.assertEqual(4, len(self._utils._add_virt_feature.mock_calls)) + self._utils._add_virt_feature.assert_called_with(mock_port, mock_acl)