Fixes Hyper-V 2008 R2 agent VLAN Settings issue

This issue affects Hyper-V 2008 R2 and does not affect
Hyper-V 2012 and above.

Hyper-V agent now also sets the vswitch external port's
DesiredEndpointMode to TRUNKED and adds the vlan_id in
the external port's TrunkedVLANList.

Note: This commit is not a classic backport, it is not merged in
the master branch, since the Hyper-V Neutron Agent was decomposed from the master branch.
This commit is already included in the networking_hyperv project:
fc11b58bef

Co-Authored-By: Simona Iuliana Toader <itoader@cloudbasesolutions.com>

Change-Id: I632054ad15f33b13616f6fbbe4a637391ba8d374
Closes-Bug: #1361211
(cherry picked from commit 9a19563cd4)
This commit is contained in:
Simona Iuliana Toader 2014-09-05 10:24:21 +03:00 committed by Ihar Hrachyshka
parent 91a4593346
commit 6687864f70
7 changed files with 204 additions and 39 deletions

View File

@ -242,7 +242,10 @@ class HyperVNeutronAgent(n_rpc.RpcCallback):
vswitch_name = self._get_vswitch_name(network_type, physical_network)
if network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]:
if network_type == p_const.TYPE_VLAN:
self._utils.set_switch_external_port_trunk_vlan(vswitch_name,
segmentation_id, constants.TRUNK_ENDPOINT_MODE)
elif network_type == p_const.TYPE_FLAT:
#Nothing to do
pass
elif network_type == p_const.TYPE_LOCAL:

View File

@ -41,6 +41,10 @@ WMI_JOB_STATE_COMPLETED = 7
class HyperVUtils(object):
_ETHERNET_SWITCH_PORT = 'Msvm_SwitchPort'
_SWITCH_LAN_ENDPOINT = 'Msvm_SwitchLanEndpoint'
_VIRTUAL_SWITCH = 'Msvm_VirtualSwitch'
_BINDS_TO = 'Msvm_BindsTo'
_VLAN_ENDPOINT_SET_DATA = 'Msvm_VLANEndpointSettingData'
_wmi_namespace = '//./root/virtualization'
@ -207,17 +211,21 @@ class HyperVUtils(object):
vswitch_name)
return vswitch[0]
def _get_vswitch_external_port(self, vswitch):
vswitch_ports = vswitch.associators(
wmi_result_class=self._ETHERNET_SWITCH_PORT)
for vswitch_port in vswitch_ports:
lan_endpoints = vswitch_port.associators(
def _get_vswitch_external_port(self, vswitch_name):
ext_ports = self._conn.Msvm_ExternalEthernetPort()
for ext_port in ext_ports:
lan_endpoint_list = ext_port.associators(
wmi_result_class='Msvm_SwitchLanEndpoint')
if lan_endpoints:
ext_port = lan_endpoints[0].associators(
wmi_result_class='Msvm_ExternalEthernetPort')
if ext_port:
return vswitch_port
if lan_endpoint_list:
vswitch_port_list = lan_endpoint_list[0].associators(
wmi_result_class=self._ETHERNET_SWITCH_PORT)
if vswitch_port_list:
vswitch_port = vswitch_port_list[0]
vswitch_list = vswitch_port.associators(
wmi_result_class='Msvm_VirtualSwitch')
if (vswitch_list and
vswitch_list[0].ElementName == vswitch_name):
return vswitch_port
def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name):
vlan_endpoint_settings = self._conn.Msvm_VLANEndpointSettingData(
@ -226,6 +234,21 @@ class HyperVUtils(object):
vlan_endpoint_settings.AccessVLAN = vlan_id
vlan_endpoint_settings.put()
def set_switch_external_port_trunk_vlan(self, vswitch_name, vlan_id,
desired_endpoint_mode):
vswitch_external_port = self._get_vswitch_external_port(vswitch_name)
if vswitch_external_port:
vlan_endpoint = vswitch_external_port.associators(
wmi_association_class=self._BINDS_TO)[0]
if vlan_endpoint.DesiredEndpointMode != desired_endpoint_mode:
vlan_endpoint.DesiredEndpointMode = desired_endpoint_mode
vlan_endpoint.put()
vlan_endpoint_settings = vlan_endpoint.associators(
wmi_result_class=self._VLAN_ENDPOINT_SET_DATA)[0]
if vlan_id not in vlan_endpoint_settings.TrunkedVLANList:
vlan_endpoint_settings.TrunkedVLANList += (vlan_id,)
vlan_endpoint_settings.put()
def _get_switch_port_path_by_name(self, switch_port_name):
vswitch = self._conn.Msvm_SwitchPort(ElementName=switch_port_name)
if vswitch:

View File

@ -134,21 +134,6 @@ class HyperVUtilsV2(utils.HyperVUtils):
vswitch_name)
return vswitch[0]
def _get_vswitch_external_port(self, vswitch):
vswitch_ports = vswitch.associators(
wmi_result_class=self._ETHERNET_SWITCH_PORT)
for vswitch_port in vswitch_ports:
lan_endpoints = vswitch_port.associators(
wmi_result_class=self._LAN_ENDPOINT)
if len(lan_endpoints):
lan_endpoints = lan_endpoints[0].associators(
wmi_result_class=self._LAN_ENDPOINT)
if len(lan_endpoints):
ext_port = lan_endpoints[0].associators(
wmi_result_class=self._EXTERNAL_PORT)
if ext_port:
return vswitch_port
def set_vswitch_port_vlan_id(self, vlan_id, switch_port_name):
port_alloc, found = self._get_switch_port_allocation(switch_port_name)
if not found:
@ -171,6 +156,10 @@ class HyperVUtilsV2(utils.HyperVUtils):
port_alloc.path_(), [vlan_settings.GetText_(1)])
self._check_job_status(ret_val, job_path)
def set_switch_external_port_trunk_vlan(self, vswitch_name, vlan_id,
desired_endpoint_mode):
pass
def _get_vlan_setting_data_from_port_alloc(self, port_alloc):
return self._get_first_item(port_alloc.associators(
wmi_result_class=self._PORT_VLAN_SET_DATA))

View File

@ -18,3 +18,4 @@ TUNNEL = 'tunnel'
# Special vlan_id value in ovs_vlan_allocations table indicating flat network
FLAT_VLAN_ID = -1
TRUNK_ENDPOINT_MODE = 5

View File

@ -21,8 +21,11 @@ Unit tests for Windows Hyper-V virtual switch neutron driver
import mock
from oslo.config import cfg
from neutron.plugins.common import constants as p_const
from neutron.plugins.hyperv.agent import hyperv_neutron_agent
from neutron.plugins.hyperv.agent import utils
from neutron.plugins.hyperv.agent import utilsfactory
from neutron.plugins.hyperv.common import constants
from neutron.tests import base
cfg.CONF.import_opt('enable_metrics_collection',
@ -54,6 +57,7 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
'neutron.agent.firewall.NoopFirewallDriver',
group='SECURITYGROUP')
self.agent = hyperv_neutron_agent.HyperVNeutronAgent()
self.agent._utils = mock.MagicMock()
self.agent.plugin_rpc = mock.Mock()
self.agent.sec_groups_agent = mock.MagicMock()
self.agent.context = mock.Mock()
@ -214,3 +218,54 @@ class TestHyperVNeutronAgent(base.BaseTestCase):
self.assertTrue(common_config.init.called)
self.assertTrue(common_config.setup_logging.called)
plugin.assert_has_calls([mock.call().daemon_loop()])
@mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgent,
"_get_vswitch_name")
def test_provision_network_exception(self, mock_get_vswitch_name):
self.assertRaises(utils.HyperVException, self.agent._provision_network,
mock.sentinel.FAKE_PORT_ID,
mock.sentinel.FAKE_NET_UUID,
mock.sentinel.FAKE_NETWORK_TYPE,
mock.sentinel.FAKE_PHYSICAL_NETWORK,
mock.sentinel.FAKE_SEGMENTATION_ID)
mock_get_vswitch_name.assert_called_once_with(
mock.sentinel.FAKE_NETWORK_TYPE,
mock.sentinel.FAKE_PHYSICAL_NETWORK)
@mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgent,
"_get_vswitch_name")
def test_provision_network_vlan(self, mock_get_vswitch_name):
vswitch_name = mock_get_vswitch_name.return_value
self.agent._provision_network(mock.sentinel.FAKE_PORT_ID,
mock.sentinel.FAKE_NET_UUID,
p_const.TYPE_VLAN,
mock.sentinel.FAKE_PHYSICAL_NETWORK,
mock.sentinel.FAKE_SEGMENTATION_ID)
mock_get_vswitch_name.assert_called_once_with(p_const.TYPE_VLAN,
mock.sentinel.FAKE_PHYSICAL_NETWORK)
set_switch = self.agent._utils.set_switch_external_port_trunk_vlan
set_switch.assert_called_once_with(vswitch_name,
mock.sentinel.FAKE_SEGMENTATION_ID,
constants.TRUNK_ENDPOINT_MODE)
@mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgent,
"_get_vswitch_name")
def test_provision_network_flat(self, mock_get_vswitch_name):
self.agent._provision_network(mock.sentinel.FAKE_PORT_ID,
mock.sentinel.FAKE_NET_UUID,
p_const.TYPE_FLAT,
mock.sentinel.FAKE_PHYSICAL_NETWORK,
mock.sentinel.FAKE_SEGMENTATION_ID)
mock_get_vswitch_name.assert_called_once_with(p_const.TYPE_FLAT,
mock.sentinel.FAKE_PHYSICAL_NETWORK)
@mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgent,
"_get_vswitch_name")
def test_provision_network_local(self, mock_get_vswitch_name):
self.agent._provision_network(mock.sentinel.FAKE_PORT_ID,
mock.sentinel.FAKE_NET_UUID,
p_const.TYPE_LOCAL,
mock.sentinel.FAKE_PHYSICAL_NETWORK,
mock.sentinel.FAKE_SEGMENTATION_ID)
mock_get_vswitch_name.assert_called_once_with(p_const.TYPE_LOCAL,
mock.sentinel.FAKE_PHYSICAL_NETWORK)

View File

@ -0,0 +1,107 @@
# Copyright 2014 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.
"""
Unit tests for the Hyper-V Utils class.
"""
import mock
from neutron.plugins.hyperv.agent import utils
from neutron.plugins.hyperv.common import constants
from neutron.tests import base
class HyperVUtilsTestCase(base.BaseTestCase):
FAKE_VLAN_ID = 500
def setUp(self):
super(HyperVUtilsTestCase, self).setUp()
self.utils = utils.HyperVUtils()
self.utils._wmi_conn = mock.MagicMock()
def test_get_vswitch_external_port(self):
ext_port = mock.MagicMock()
self.utils._conn.Msvm_ExternalEthernetPort.return_value = [ext_port]
lan_endpoint = mock.MagicMock()
ext_port.associators.return_value = [lan_endpoint]
vswitch_port = mock.MagicMock()
lan_endpoint.associators.return_value = [vswitch_port]
vswitch = mock.MagicMock()
vswitch.ElementName = mock.sentinel.FAKE_VSWITCH_NAME
vswitch_port.associators.return_value = [vswitch]
result = self.utils._get_vswitch_external_port(
mock.sentinel.FAKE_VSWITCH_NAME)
self.assertEqual(vswitch_port, result)
ext_port.associators.assert_called_once_with(
wmi_result_class=self.utils._SWITCH_LAN_ENDPOINT)
lan_endpoint.associators.assert_called_once_with(
wmi_result_class=self.utils._ETHERNET_SWITCH_PORT)
vswitch_port.associators.assert_called_once_with(
wmi_result_class=self.utils._VIRTUAL_SWITCH)
@mock.patch.object(utils.HyperVUtils, "_get_vswitch_external_port")
def _check_set_switch_ext_port_trunk_vlan(self,
mock_get_vswitch_external_port, desired_endpoint_mode,
trunked_list):
vswitch_external_port = mock_get_vswitch_external_port.return_value
vlan_endpoint = mock.MagicMock()
vswitch_external_port.associators.return_value = [vlan_endpoint]
vlan_endpoint_settings = mock.MagicMock()
vlan_endpoint_settings.TrunkedVLANList = trunked_list
vlan_endpoint.associators.return_value = [vlan_endpoint_settings]
self.utils.set_switch_external_port_trunk_vlan(
mock.sentinel.FAKE_VSWITCH_NAME, self.FAKE_VLAN_ID,
desired_endpoint_mode)
mock_get_vswitch_external_port.assert_called_once_with(
mock.sentinel.FAKE_VSWITCH_NAME)
vswitch_external_port.associators.assert_called_once_with(
wmi_association_class=self.utils._BINDS_TO)
vlan_endpoint.associators.assert_called_once_with(
wmi_result_class=self.utils._VLAN_ENDPOINT_SET_DATA)
self.assertEqual(desired_endpoint_mode,
vlan_endpoint.DesiredEndpointMode)
self.assertIn(self.FAKE_VLAN_ID,
vlan_endpoint_settings.TrunkedVLANList)
@mock.patch.object(utils.HyperVUtils, "_get_vswitch_external_port")
def test_set_switch_ext_port_trunk_vlan_internal(self,
mock_get_vswitch_external_port):
mock_get_vswitch_external_port.return_value = None
self.utils.set_switch_external_port_trunk_vlan(
mock.sentinel.FAKE_VSWITCH_NAME, self.FAKE_VLAN_ID,
constants.TRUNK_ENDPOINT_MODE)
mock_get_vswitch_external_port.assert_called_once_with(
mock.sentinel.FAKE_VSWITCH_NAME)
def test_set_switch_ext_port_trunk_vlan_trunked_missing(self):
self._check_set_switch_ext_port_trunk_vlan(
desired_endpoint_mode=constants.TRUNK_ENDPOINT_MODE,
trunked_list=[])
def test_set_switch_ext_port_trunk_vlan_trunked_added(self):
self._check_set_switch_ext_port_trunk_vlan(
desired_endpoint_mode=constants.TRUNK_ENDPOINT_MODE,
trunked_list=[self.FAKE_VLAN_ID])

View File

@ -180,19 +180,6 @@ class TestHyperVUtilsV2(base.BaseTestCase):
self.assertRaises(utils.HyperVException, self._utils._get_vswitch,
self._FAKE_VSWITCH_NAME)
def test_get_vswitch_external_port(self):
mock_vswitch = mock.MagicMock()
mock_sw_port = mock.MagicMock()
mock_vswitch.associators.return_value = [mock_sw_port]
mock_le = mock_sw_port.associators.return_value
mock_le.__len__.return_value = 1
mock_le1 = mock_le[0].associators.return_value
mock_le1.__len__.return_value = 1
vswitch_port = self._utils._get_vswitch_external_port(mock_vswitch)
self.assertEqual(mock_sw_port, vswitch_port)
def test_set_vswitch_port_vlan_id(self):
mock_port_alloc = mock.MagicMock()
self._utils._get_switch_port_allocation = mock.MagicMock(return_value=(