Merge "Fix Cisco nexus plugin failure for first VLAN on phy interface"

This commit is contained in:
Jenkins 2013-05-31 08:03:53 +00:00 committed by Gerrit Code Review
commit 0dbb0e9d54
4 changed files with 82 additions and 40 deletions

View File

@ -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

View File

@ -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):

View File

@ -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>

View File

@ -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)