Merge "Cisco nexus plugin fails to untrunk vlan if other hosts using vlan"
This commit is contained in:
commit
0b2d56cda7
@ -299,16 +299,27 @@ class NexusPlugin(L2DevicePluginBase):
|
|||||||
nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
|
nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
|
||||||
row.switch_ip,
|
row.switch_ip,
|
||||||
row.instance_id)
|
row.instance_id)
|
||||||
# Check for any other bindings with the same vlan_id and switch_ip
|
# Check whether there are any remaining instances using this
|
||||||
|
# vlan on this Nexus port.
|
||||||
try:
|
try:
|
||||||
nxos_db.get_nexusvlan_binding(row.vlan_id, row.switch_ip)
|
nxos_db.get_port_vlan_switch_binding(row.port_id,
|
||||||
|
row.vlan_id,
|
||||||
|
row.switch_ip)
|
||||||
except cisco_exc.NexusPortBindingNotFound:
|
except cisco_exc.NexusPortBindingNotFound:
|
||||||
try:
|
try:
|
||||||
# Delete this vlan from this switch
|
|
||||||
if nexus_port and auto_untrunk:
|
if nexus_port and auto_untrunk:
|
||||||
|
# Untrunk the vlan from this Nexus interface
|
||||||
self._client.disable_vlan_on_trunk_int(
|
self._client.disable_vlan_on_trunk_int(
|
||||||
switch_ip, row.vlan_id, etype, nexus_port)
|
switch_ip, row.vlan_id, etype, nexus_port)
|
||||||
|
|
||||||
|
# Check whether there are any remaining instances
|
||||||
|
# using this vlan on the Nexus switch.
|
||||||
if auto_delete:
|
if auto_delete:
|
||||||
|
try:
|
||||||
|
nxos_db.get_nexusvlan_binding(row.vlan_id,
|
||||||
|
row.switch_ip)
|
||||||
|
except cisco_exc.NexusPortBindingNotFound:
|
||||||
|
# Delete this vlan from this switch
|
||||||
self._client.delete_vlan(switch_ip, row.vlan_id)
|
self._client.delete_vlan(switch_ip, row.vlan_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
# The delete vlan operation on the Nexus failed,
|
# The delete vlan operation on the Nexus failed,
|
||||||
|
@ -170,6 +170,7 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
configlet = call[2]['config']
|
configlet = call[2]['config']
|
||||||
if all(word in configlet for word in words):
|
if all(word in configlet for word in words):
|
||||||
return True
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def _is_in_last_nexus_cfg(self, words):
|
def _is_in_last_nexus_cfg(self, words):
|
||||||
"""Check if last config sent to Nexus contains all words in a list."""
|
"""Check if last config sent to Nexus contains all words in a list."""
|
||||||
@ -177,6 +178,20 @@ class CiscoNetworkPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase):
|
|||||||
edit_config.mock_calls[-1][2]['config'])
|
edit_config.mock_calls[-1][2]['config'])
|
||||||
return all(word in last_cfg for word in words)
|
return all(word in last_cfg for word in words)
|
||||||
|
|
||||||
|
def _is_vlan_configured(self, vlan_creation_expected=True,
|
||||||
|
add_keyword_expected=False):
|
||||||
|
vlan_created = self._is_in_nexus_cfg(['vlan', 'vlan-name'])
|
||||||
|
add_appears = self._is_in_last_nexus_cfg(['add'])
|
||||||
|
return (self._is_in_last_nexus_cfg(['allowed', 'vlan']) and
|
||||||
|
vlan_created == vlan_creation_expected and
|
||||||
|
add_appears == add_keyword_expected)
|
||||||
|
|
||||||
|
def _is_vlan_unconfigured(self, vlan_deletion_expected=True):
|
||||||
|
vlan_deleted = self._is_in_last_nexus_cfg(
|
||||||
|
['no', 'vlan', 'vlan-id-create-delete'])
|
||||||
|
return (self._is_in_nexus_cfg(['allowed', 'vlan', 'remove']) and
|
||||||
|
vlan_deleted == vlan_deletion_expected)
|
||||||
|
|
||||||
|
|
||||||
class TestCiscoBasicGet(CiscoNetworkPluginV2TestCase,
|
class TestCiscoBasicGet(CiscoNetworkPluginV2TestCase,
|
||||||
test_db_plugin.TestBasicGet):
|
test_db_plugin.TestBasicGet):
|
||||||
@ -307,14 +322,64 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
|
|||||||
|
|
||||||
def test_nexus_enable_vlan_cmd(self):
|
def test_nexus_enable_vlan_cmd(self):
|
||||||
"""Verify the syntax of the command to enable a vlan on an intf."""
|
"""Verify the syntax of the command to enable a vlan on an intf."""
|
||||||
|
|
||||||
# First vlan should be configured without 'add' keyword
|
# First vlan should be configured without 'add' keyword
|
||||||
with self._create_port_res(name='net1', cidr=CIDR_1):
|
with self._create_port_res(name='net1', cidr=CIDR_1):
|
||||||
self.assertTrue(self._is_in_last_nexus_cfg(['allowed', 'vlan']))
|
self.assertTrue(self._is_vlan_configured(
|
||||||
self.assertFalse(self._is_in_last_nexus_cfg(['add']))
|
vlan_creation_expected=True,
|
||||||
|
add_keyword_expected=False))
|
||||||
|
self.mock_ncclient.reset_mock()
|
||||||
|
|
||||||
# Second vlan should be configured with 'add' keyword
|
# Second vlan should be configured with 'add' keyword
|
||||||
with self._create_port_res(name='net2', cidr=CIDR_2):
|
with self._create_port_res(name='net2', cidr=CIDR_2):
|
||||||
self.assertTrue(
|
self.assertTrue(self._is_vlan_configured(
|
||||||
self._is_in_last_nexus_cfg(['allowed', 'vlan', 'add']))
|
vlan_creation_expected=True,
|
||||||
|
add_keyword_expected=True))
|
||||||
|
|
||||||
|
def test_nexus_vlan_config_two_hosts(self):
|
||||||
|
"""Verify config/unconfig of vlan on two compute hosts."""
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _create_port_check_vlan(comp_host_name, device_id,
|
||||||
|
vlan_creation_expected=True):
|
||||||
|
arg_list = (portbindings.HOST_ID,)
|
||||||
|
port_dict = {portbindings.HOST_ID: comp_host_name,
|
||||||
|
'device_id': device_id,
|
||||||
|
'device_owner': DEVICE_OWNER}
|
||||||
|
with self.port(subnet=subnet, fmt=self.fmt,
|
||||||
|
arg_list=arg_list, **port_dict):
|
||||||
|
self.assertTrue(self._is_vlan_configured(
|
||||||
|
vlan_creation_expected=vlan_creation_expected,
|
||||||
|
add_keyword_expected=False))
|
||||||
|
self.mock_ncclient.reset_mock()
|
||||||
|
yield
|
||||||
|
|
||||||
|
# Create network and subnet
|
||||||
|
with self.network(name=NETWORK_NAME) as network:
|
||||||
|
with self.subnet(network=network, cidr=CIDR_1) as subnet:
|
||||||
|
|
||||||
|
# Create an instance on first compute host
|
||||||
|
with _create_port_check_vlan(
|
||||||
|
COMP_HOST_NAME, DEVICE_ID_1, vlan_creation_expected=True):
|
||||||
|
|
||||||
|
# Create an instance on second compute host
|
||||||
|
with _create_port_check_vlan(
|
||||||
|
COMP_HOST_NAME_2, DEVICE_ID_2,
|
||||||
|
vlan_creation_expected=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Instance on second host is now terminated.
|
||||||
|
# Vlan should be untrunked from port, but vlan should
|
||||||
|
# still exist on the switch.
|
||||||
|
self.assertTrue(self._is_vlan_unconfigured(
|
||||||
|
vlan_deletion_expected=False))
|
||||||
|
self.mock_ncclient.reset_mock()
|
||||||
|
|
||||||
|
# Instance on first host is now terminated.
|
||||||
|
# Vlan should be untrunked from port and vlan should have
|
||||||
|
# been deleted from the switch.
|
||||||
|
self.assertTrue(self._is_vlan_unconfigured(
|
||||||
|
vlan_deletion_expected=True))
|
||||||
|
|
||||||
def test_nexus_connect_fail(self):
|
def test_nexus_connect_fail(self):
|
||||||
"""Test failure to connect to a Nexus switch.
|
"""Test failure to connect to a Nexus switch.
|
||||||
|
Loading…
Reference in New Issue
Block a user