Merge "Fix Cisco nexus plugin failure for first VLAN on phy interface"
This commit is contained in:
commit
0dbb0e9d54
@ -133,3 +133,18 @@ def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
|
||||
raise c_exc.NexusPortBindingNotFound(**filters)
|
||||
|
||||
return bindings
|
||||
|
||||
|
||||
def get_port_switch_bindings(port_id, switch_ip):
|
||||
"""List all vm/vlan bindings on a Nexus switch port."""
|
||||
LOG.debug(_("get_port_switch_bindings() called, "
|
||||
"port:'%(port_id)s', switch:'%(switch_ip)s'"),
|
||||
{'port_id': port_id, 'switch_ip': switch_ip})
|
||||
session = db.get_session()
|
||||
try:
|
||||
binding = (session.query(nexus_models_v2.NexusPortBinding).
|
||||
filter_by(port_id=port_id).
|
||||
filter_by(switch_ip=switch_ip).all())
|
||||
return binding
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
|
@ -27,6 +27,7 @@ from ncclient import manager
|
||||
|
||||
from quantum.plugins.cisco.common import cisco_exceptions as cexc
|
||||
from quantum.plugins.cisco.db import network_db_v2 as cdb
|
||||
from quantum.plugins.cisco.db import nexus_db_v2
|
||||
from quantum.plugins.cisco.nexus import cisco_nexus_snippets as snipp
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -135,13 +136,19 @@ class CiscoNEXUSDriver():
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(mgr, target='running', config=confstr)
|
||||
|
||||
def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
|
||||
def enable_vlan_on_trunk_int(self, mgr, nexus_switch, interface, vlanid):
|
||||
"""Enable vlan in trunk interface.
|
||||
|
||||
Enables trunk mode vlan access an interface on Nexus Switch given
|
||||
VLANID.
|
||||
"""
|
||||
confstr = snipp.CMD_VLAN_INT_SNIPPET % (interface, vlanid)
|
||||
# If one or more VLANs are already configured on this interface,
|
||||
# include the 'add' keyword.
|
||||
if nexus_db_v2.get_port_switch_bindings(interface, nexus_switch):
|
||||
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
|
||||
else:
|
||||
snippet = snipp.CMD_INT_VLAN_SNIPPET
|
||||
confstr = snippet % (interface, vlanid)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(mgr, target='running', config=confstr)
|
||||
@ -172,7 +179,7 @@ class CiscoNEXUSDriver():
|
||||
vlan_ids = self.build_vlans_cmd()
|
||||
LOG.debug(_("NexusDriver VLAN IDs: %s"), vlan_ids)
|
||||
for ports in nexus_ports:
|
||||
self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
|
||||
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)
|
||||
|
||||
def delete_vlan(self, vlan_id, nexus_host, nexus_user, nexus_password,
|
||||
nexus_ports, nexus_ssh_port):
|
||||
@ -208,7 +215,7 @@ class CiscoNEXUSDriver():
|
||||
if not vlan_ids:
|
||||
vlan_ids = self.build_vlans_cmd()
|
||||
for ports in nexus_ports:
|
||||
self.enable_vlan_on_trunk_int(man, ports, vlan_ids)
|
||||
self.enable_vlan_on_trunk_int(man, nexus_host, ports, vlan_ids)
|
||||
|
||||
def remove_vlan_int(self, vlan_id, nexus_host, nexus_user, nexus_password,
|
||||
nexus_ports, nexus_ssh_port):
|
||||
|
@ -86,7 +86,7 @@ CMD_NO_VLAN_CONF_SNIPPET = """
|
||||
</no>
|
||||
"""
|
||||
|
||||
CMD_VLAN_INT_SNIPPET = """
|
||||
CMD_INT_VLAN_HEADER = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
<interface>%s</interface>
|
||||
@ -94,10 +94,16 @@ CMD_VLAN_INT_SNIPPET = """
|
||||
<switchport>
|
||||
<trunk>
|
||||
<allowed>
|
||||
<vlan>
|
||||
<add>
|
||||
<add_vlans>%s</add_vlans>
|
||||
</add>
|
||||
<vlan>"""
|
||||
|
||||
CMD_VLAN_ID = """
|
||||
<vlan_id>%s</vlan_id>"""
|
||||
|
||||
CMD_VLAN_ADD_ID = """
|
||||
<add>%s
|
||||
</add>""" % CMD_VLAN_ID
|
||||
|
||||
CMD_INT_VLAN_TRAILER = """
|
||||
</vlan>
|
||||
</allowed>
|
||||
</trunk>
|
||||
@ -107,6 +113,14 @@ CMD_VLAN_INT_SNIPPET = """
|
||||
</interface>
|
||||
"""
|
||||
|
||||
CMD_INT_VLAN_SNIPPET = (CMD_INT_VLAN_HEADER +
|
||||
CMD_VLAN_ID +
|
||||
CMD_INT_VLAN_TRAILER)
|
||||
|
||||
CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
|
||||
CMD_VLAN_ADD_ID +
|
||||
CMD_INT_VLAN_TRAILER)
|
||||
|
||||
CMD_PORT_TRUNK = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
|
@ -155,28 +155,29 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
self.mock_ncclient.configure_mock(**config)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _create_port_res(self, fmt=None, no_delete=False,
|
||||
**kwargs):
|
||||
def _create_port_res(self, name='myname', cidr='1.0.0.0/24',
|
||||
do_delete=True):
|
||||
"""Create a network, subnet, and port and yield the result.
|
||||
|
||||
Create a network, subnet, and port, yield the result,
|
||||
then delete the port, subnet, and network.
|
||||
|
||||
:param fmt: Format to be used for API requests.
|
||||
:param no_delete: If set to True, don't delete the port at the
|
||||
end of testing.
|
||||
:param kwargs: Keyword args to be passed to self._create_port.
|
||||
:param name: Name of network to be created
|
||||
:param cidr: cidr address of subnetwork to be created
|
||||
:param do_delete: If set to True, delete the port at the
|
||||
end of testing
|
||||
|
||||
"""
|
||||
with self.subnet() as subnet:
|
||||
net_id = subnet['subnet']['network_id']
|
||||
res = self._create_port(fmt, net_id, **kwargs)
|
||||
port = self.deserialize(fmt, res)
|
||||
try:
|
||||
yield res
|
||||
finally:
|
||||
if not no_delete:
|
||||
self._delete('ports', port['port']['id'])
|
||||
with self.network(name=name) as network:
|
||||
with self.subnet(network=network, cidr=cidr) as subnet:
|
||||
net_id = subnet['subnet']['network_id']
|
||||
res = self._create_port(self.fmt, net_id)
|
||||
port = self.deserialize(self.fmt, res)
|
||||
try:
|
||||
yield res
|
||||
finally:
|
||||
if do_delete:
|
||||
self._delete('ports', port['port']['id'])
|
||||
|
||||
def _assertExpectedHTTP(self, status, exc):
|
||||
"""Confirm that an HTTP status corresponds to an expected exception.
|
||||
@ -255,6 +256,17 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
'ports',
|
||||
wexc.HTTPInternalServerError.code)
|
||||
|
||||
def test_nexus_enable_vlan_cmd(self):
|
||||
"""Verify the syntax of the command to enable a vlan on an intf."""
|
||||
# First vlan should be configured without 'add' keyword
|
||||
with self._create_port_res(name='net1', cidr='1.0.0.0/24'):
|
||||
self.assertTrue(self._is_in_last_nexus_cfg(['allowed', 'vlan']))
|
||||
self.assertFalse(self._is_in_last_nexus_cfg(['add']))
|
||||
# Second vlan should be configured with 'add' keyword
|
||||
with self._create_port_res(name='net2', cidr='1.0.1.0/24'):
|
||||
self.assertTrue(
|
||||
self._is_in_last_nexus_cfg(['allowed', 'vlan', 'add']))
|
||||
|
||||
def test_nexus_connect_fail(self):
|
||||
"""Test failure to connect to a Nexus switch.
|
||||
|
||||
@ -265,8 +277,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
"""
|
||||
with self._patch_ncclient('manager.connect.side_effect',
|
||||
AttributeError):
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NexusConnectFailed)
|
||||
|
||||
@ -281,8 +292,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
with self._patch_ncclient(
|
||||
'manager.connect.return_value.edit_config.side_effect',
|
||||
AttributeError):
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NexusConfigFailed)
|
||||
|
||||
@ -302,7 +312,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
with self._patch_ncclient(
|
||||
'manager.connect.return_value.edit_config.side_effect',
|
||||
mock_edit_config_a):
|
||||
with self._create_port_res(self.fmt, name='myname') as res:
|
||||
with self._create_port_res(name='myname') as res:
|
||||
self.assertEqual(res.status_int, wexc.HTTPCreated.code)
|
||||
|
||||
def mock_edit_config_b(target, config):
|
||||
@ -312,7 +322,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
with self._patch_ncclient(
|
||||
'manager.connect.return_value.edit_config.side_effect',
|
||||
mock_edit_config_b):
|
||||
with self._create_port_res(self.fmt, name='myname') as res:
|
||||
with self._create_port_res(name='myname') as res:
|
||||
self.assertEqual(res.status_int, wexc.HTTPCreated.code)
|
||||
|
||||
def test_nexus_vlan_config_rollback(self):
|
||||
@ -330,8 +340,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
with self._patch_ncclient(
|
||||
'manager.connect.return_value.edit_config.side_effect',
|
||||
mock_edit_config):
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(name='myname', do_delete=False) as res:
|
||||
# Confirm that the last configuration sent to the Nexus
|
||||
# switch was deletion of the VLAN.
|
||||
self.assertTrue(
|
||||
@ -362,8 +371,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
|
||||
with mock.patch.object(ovs_db_v2, 'get_network_binding',
|
||||
new=_return_none_if_nexus_caller):
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NetworkSegmentIDNotFound)
|
||||
|
||||
@ -377,8 +385,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
with mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
|
||||
'_get_instance_host') as mock_get_instance:
|
||||
mock_get_instance.return_value = 'fictitious_host'
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
self._assertExpectedHTTP(res.status_int,
|
||||
c_exc.NexusComputeHostNotConfigured)
|
||||
|
||||
@ -392,8 +399,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
"""
|
||||
with mock.patch.object(nexus_db_v2, 'add_nexusport_binding',
|
||||
side_effect=KeyError):
|
||||
with self._create_port_res(self.fmt, no_delete=True,
|
||||
name='myname') as res:
|
||||
with self._create_port_res(do_delete=False) as res:
|
||||
# Confirm that the last configuration sent to the Nexus
|
||||
# switch was a removal of vlan from the test interface.
|
||||
self.assertTrue(
|
||||
@ -409,7 +415,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
plugin for a delete port operation.
|
||||
|
||||
"""
|
||||
with self._create_port_res(self.fmt, name='myname') as res:
|
||||
with self._create_port_res() as res:
|
||||
|
||||
# After port is created, we should have one binding for this
|
||||
# vlan/nexus switch.
|
||||
@ -442,7 +448,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
||||
nexus switch during a delete_port operation.
|
||||
|
||||
"""
|
||||
with self._create_port_res(self.fmt, name='myname') as res:
|
||||
with self._create_port_res() as res:
|
||||
|
||||
port = self.deserialize(self.fmt, res)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user