Merge "ML2 Cisco Nexus MD: Support portchannel interfaces"
This commit is contained in:
commit
2ddc62d0c0
@ -23,13 +23,16 @@
|
||||
#
|
||||
# Cisco Nexus Switch Format.
|
||||
# [ml2_mech_cisco_nexus:<IP address of switch>]
|
||||
# <hostname>=<port> (1)
|
||||
# <hostname>=<intf_type:port> (1)
|
||||
# ssh_port=<ssh port> (2)
|
||||
# username=<credential username> (3)
|
||||
# password=<credential password> (4)
|
||||
#
|
||||
# (1) For each host connected to a port on the switch, specify the hostname
|
||||
# and the Nexus physical port (interface) it is connected to.
|
||||
# Valid intf_type's are 'ethernet' and 'port-channel'.
|
||||
# The default setting for <intf_type:> is 'ethernet' and need not be
|
||||
# added to this setting.
|
||||
# (2) The TCP port for connecting via SSH to manage the switch. This is
|
||||
# port number 22 unless the switch has been configured otherwise.
|
||||
# (3) The username for logging into the switch to manage it.
|
||||
@ -38,7 +41,8 @@
|
||||
# Example:
|
||||
# [ml2_mech_cisco_nexus:1.1.1.1]
|
||||
# compute1=1/1
|
||||
# compute2=1/2
|
||||
# compute2=ethernet:1/2
|
||||
# compute3=port-channel:1
|
||||
# ssh_port=22
|
||||
# username=admin
|
||||
# password=mySecretPassword
|
||||
|
@ -68,10 +68,18 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
|
||||
return port['status'] == n_const.PORT_STATUS_ACTIVE
|
||||
|
||||
def _get_switch_info(self, host_id):
|
||||
host_connections = []
|
||||
for switch_ip, attr in self._nexus_switches:
|
||||
if str(attr) == str(host_id):
|
||||
port_id = self._nexus_switches[switch_ip, attr]
|
||||
return port_id, switch_ip
|
||||
if ':' in port_id:
|
||||
intf_type, port = port_id.split(':')
|
||||
else:
|
||||
intf_type, port = 'ethernet', port_id
|
||||
host_connections.append((switch_ip, intf_type, port))
|
||||
|
||||
if host_connections:
|
||||
return host_connections
|
||||
else:
|
||||
raise excep.NexusComputeHostNotConfigured(host=host_id)
|
||||
|
||||
@ -80,9 +88,11 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
|
||||
|
||||
Called during update precommit port event.
|
||||
"""
|
||||
port_id, switch_ip = self._get_switch_info(host_id)
|
||||
nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
|
||||
device_id)
|
||||
host_connections = self._get_switch_info(host_id)
|
||||
for switch_ip, intf_type, nexus_port in host_connections:
|
||||
port_id = '%s:%s' % (intf_type, nexus_port)
|
||||
nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
|
||||
device_id)
|
||||
|
||||
def _configure_switch_entry(self, vlan_id, device_id, host_id):
|
||||
"""Create a nexus switch entry.
|
||||
@ -92,19 +102,21 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
|
||||
|
||||
Called during update postcommit port event.
|
||||
"""
|
||||
port_id, switch_ip = self._get_switch_info(host_id)
|
||||
vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id)
|
||||
host_connections = self._get_switch_info(host_id)
|
||||
|
||||
# Check to see if this is the first binding to use this vlan on the
|
||||
# switch/port. Configure switch accordingly.
|
||||
bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
if len(bindings) == 1:
|
||||
LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
|
||||
self.driver.create_and_trunk_vlan(switch_ip, vlan_id, vlan_name,
|
||||
port_id)
|
||||
else:
|
||||
LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
|
||||
self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id, port_id)
|
||||
for switch_ip, intf_type, nexus_port in host_connections:
|
||||
# Check to see if this is the first binding to use this vlan on the
|
||||
# switch/port. Configure switch accordingly.
|
||||
bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
if len(bindings) == 1:
|
||||
LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
|
||||
self.driver.create_and_trunk_vlan(
|
||||
switch_ip, vlan_id, vlan_name, intf_type, nexus_port)
|
||||
else:
|
||||
LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
|
||||
self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id,
|
||||
intf_type, nexus_port)
|
||||
|
||||
def _delete_nxos_db(self, vlan_id, device_id, host_id):
|
||||
"""Delete the nexus database entry.
|
||||
@ -112,9 +124,10 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
|
||||
Called during delete precommit port event.
|
||||
"""
|
||||
try:
|
||||
row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
|
||||
nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
|
||||
row.switch_ip, row.instance_id)
|
||||
rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
|
||||
for row in rows:
|
||||
nxos_db.remove_nexusport_binding(
|
||||
row.port_id, row.vlan_id, row.switch_ip, row.instance_id)
|
||||
except excep.NexusPortBindingNotFound:
|
||||
return
|
||||
|
||||
@ -126,21 +139,24 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
|
||||
|
||||
Called during update postcommit port event.
|
||||
"""
|
||||
port_id, switch_ip = self._get_switch_info(host_id)
|
||||
|
||||
# if there are no remaining db entries using this vlan on this nexus
|
||||
# switch port then remove vlan from the switchport trunk.
|
||||
try:
|
||||
nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip)
|
||||
except excep.NexusPortBindingNotFound:
|
||||
self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id, port_id)
|
||||
|
||||
host_connections = self._get_switch_info(host_id)
|
||||
for switch_ip, intf_type, nexus_port in host_connections:
|
||||
# if there are no remaining db entries using this vlan on this
|
||||
# nexus switch then remove the vlan.
|
||||
# nexus switch port then remove vlan from the switchport trunk.
|
||||
port_id = '%s:%s' % (intf_type, nexus_port)
|
||||
try:
|
||||
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
nxos_db.get_port_vlan_switch_binding(port_id, vlan_id,
|
||||
switch_ip)
|
||||
except excep.NexusPortBindingNotFound:
|
||||
self.driver.delete_vlan(switch_ip, vlan_id)
|
||||
self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id,
|
||||
intf_type, nexus_port)
|
||||
|
||||
# if there are no remaining db entries using this vlan on this
|
||||
# nexus switch then remove the vlan.
|
||||
try:
|
||||
nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
|
||||
except excep.NexusPortBindingNotFound:
|
||||
self.driver.delete_vlan(switch_ip, vlan_id)
|
||||
|
||||
def _is_vm_migration(self, context):
|
||||
if not context.bound_segment and context.original_bound_segment:
|
||||
|
@ -82,11 +82,11 @@ def update_nexusport_binding(port_id, new_vlan_id):
|
||||
return binding
|
||||
|
||||
|
||||
def get_nexusvm_binding(vlan_id, instance_id):
|
||||
def get_nexusvm_bindings(vlan_id, instance_id):
|
||||
"""Lists nexusvm bindings."""
|
||||
LOG.debug(_("get_nexusvm_binding() called"))
|
||||
return _lookup_first_nexus_binding(instance_id=instance_id,
|
||||
vlan_id=vlan_id)
|
||||
LOG.debug(_("get_nexusvm_bindings() called"))
|
||||
return _lookup_all_nexus_bindings(instance_id=instance_id,
|
||||
vlan_id=vlan_id)
|
||||
|
||||
|
||||
def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
|
||||
|
@ -154,54 +154,46 @@ class CiscoNexusDriver(object):
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def enable_port_trunk(self, nexus_host, interface):
|
||||
"""Enable trunk mode an interface on Nexus Switch."""
|
||||
confstr = snipp.CMD_PORT_TRUNK % (interface)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def disable_switch_port(self, nexus_host, interface):
|
||||
"""Disable trunk mode an interface on Nexus Switch."""
|
||||
confstr = snipp.CMD_NO_SWITCHPORT % (interface)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def enable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
|
||||
def enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
|
||||
interface):
|
||||
"""Enable a VLAN on a trunk interface."""
|
||||
# If more than one VLAN is configured on this interface then
|
||||
# include the 'add' keyword.
|
||||
if len(nexus_db_v2.get_port_switch_bindings(interface,
|
||||
nexus_host)) == 1:
|
||||
if len(nexus_db_v2.get_port_switch_bindings(
|
||||
'%s:%s' % (intf_type, interface), nexus_host)) == 1:
|
||||
snippet = snipp.CMD_INT_VLAN_SNIPPET
|
||||
else:
|
||||
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
|
||||
confstr = snippet % (interface, vlanid)
|
||||
confstr = snippet % (intf_type, interface, vlanid, intf_type)
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def disable_vlan_on_trunk_int(self, nexus_host, vlanid, interface):
|
||||
def disable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
|
||||
interface):
|
||||
"""Disable a VLAN on a trunk interface."""
|
||||
confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
|
||||
confstr = (snipp.CMD_NO_VLAN_INT_SNIPPET %
|
||||
(intf_type, interface, vlanid, intf_type))
|
||||
confstr = self.create_xml_snippet(confstr)
|
||||
LOG.debug(_("NexusDriver: %s"), confstr)
|
||||
self._edit_config(nexus_host, target='running', config=confstr)
|
||||
|
||||
def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name,
|
||||
nexus_port):
|
||||
intf_type, nexus_port):
|
||||
"""Create VLAN and trunk it on the specified ports."""
|
||||
self.create_vlan(nexus_host, vlan_id, vlan_name)
|
||||
LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id)
|
||||
if nexus_port:
|
||||
self.enable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
|
||||
self.enable_vlan_on_trunk_int(nexus_host, vlan_id, intf_type,
|
||||
nexus_port)
|
||||
|
||||
def delete_and_untrunk_vlan(self, nexus_host, vlan_id, nexus_port):
|
||||
def delete_and_untrunk_vlan(self, nexus_host, vlan_id, intf_type,
|
||||
nexus_port):
|
||||
"""Delete VLAN and untrunk it from the specified ports."""
|
||||
self.delete_vlan(nexus_host, vlan_id)
|
||||
if nexus_port:
|
||||
self.disable_vlan_on_trunk_int(nexus_host, vlan_id, nexus_port)
|
||||
self.disable_vlan_on_trunk_int(nexus_host, vlan_id, intf_type,
|
||||
nexus_port)
|
||||
|
||||
def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
|
||||
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
|
||||
|
@ -85,7 +85,7 @@ CMD_NO_VLAN_CONF_SNIPPET = """
|
||||
|
||||
CMD_INT_VLAN_HEADER = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<switchport>
|
||||
@ -106,7 +106,7 @@ CMD_INT_VLAN_TRAILER = """
|
||||
</trunk>
|
||||
</switchport>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</ethernet>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
@ -120,7 +120,7 @@ CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
|
||||
|
||||
CMD_PORT_TRUNK = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<switchport></switchport>
|
||||
@ -131,13 +131,13 @@ CMD_PORT_TRUNK = """
|
||||
</mode>
|
||||
</switchport>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</ethernet>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
CMD_NO_SWITCHPORT = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<no>
|
||||
@ -145,13 +145,13 @@ CMD_NO_SWITCHPORT = """
|
||||
</switchport>
|
||||
</no>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</ethernet>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
CMD_NO_VLAN_INT_SNIPPET = """
|
||||
<interface>
|
||||
<ethernet>
|
||||
<%s>
|
||||
<interface>%s</interface>
|
||||
<__XML__MODE_if-ethernet-switch>
|
||||
<switchport></switchport>
|
||||
@ -167,7 +167,7 @@ CMD_NO_VLAN_INT_SNIPPET = """
|
||||
</trunk>
|
||||
</switchport>
|
||||
</__XML__MODE_if-ethernet-switch>
|
||||
</ethernet>
|
||||
</%s>
|
||||
</interface>
|
||||
"""
|
||||
|
||||
|
@ -418,8 +418,10 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
|
||||
with self._create_resources() as result:
|
||||
# Verify initial database entry.
|
||||
# Use port_id to verify that 1st host name was used.
|
||||
binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1)
|
||||
self.assertEqual(binding.port_id, NEXUS_INTERFACE)
|
||||
binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
|
||||
DEVICE_ID_1)[0]
|
||||
intf_type, nexus_port = binding.port_id.split(':')
|
||||
self.assertEqual(nexus_port, NEXUS_INTERFACE)
|
||||
|
||||
port = self.deserialize(self.fmt, result)
|
||||
port_id = port['port']['id']
|
||||
@ -434,7 +436,7 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
|
||||
|
||||
# Verify that port entry has been deleted.
|
||||
self.assertRaises(c_exc.NexusPortBindingNotFound,
|
||||
nexus_db_v2.get_nexusvm_binding,
|
||||
nexus_db_v2.get_nexusvm_bindings,
|
||||
VLAN_START, DEVICE_ID_1)
|
||||
|
||||
# Trigger update event to bind segment with new host.
|
||||
@ -445,8 +447,10 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
|
||||
|
||||
# Verify that port entry has been added using new host name.
|
||||
# Use port_id to verify that 2nd host name was used.
|
||||
binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1)
|
||||
self.assertEqual(binding.port_id, NEXUS_INTERFACE_2)
|
||||
binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
|
||||
DEVICE_ID_1)[0]
|
||||
intf_type, nexus_port = binding.port_id.split(':')
|
||||
self.assertEqual(nexus_port, NEXUS_INTERFACE_2)
|
||||
|
||||
def test_nexus_config_fail(self):
|
||||
"""Test a Nexus switch configuration failure.
|
||||
|
@ -38,8 +38,8 @@ HOST_NAME_PC = 'testpchost'
|
||||
INSTANCE_1 = 'testvm1'
|
||||
INSTANCE_2 = 'testvm2'
|
||||
INSTANCE_PC = 'testpcvm'
|
||||
NEXUS_PORT_1 = '1/10'
|
||||
NEXUS_PORT_2 = '1/20'
|
||||
NEXUS_PORT_1 = 'ethernet:1/10'
|
||||
NEXUS_PORT_2 = 'ethernet:1/20'
|
||||
NEXUS_PORTCHANNELS = 'portchannel:2'
|
||||
VLAN_ID_1 = 267
|
||||
VLAN_ID_2 = 265
|
||||
|
@ -76,8 +76,8 @@ class CiscoNexusDbTest(base.BaseTestCase):
|
||||
return nexus_db_v2.get_nexusvlan_binding(npb.vlan, npb.switch)
|
||||
|
||||
def _get_nexusvm_binding(self, npb):
|
||||
"""Gets port bindings based on vlan and instance."""
|
||||
return nexus_db_v2.get_nexusvm_binding(npb.vlan, npb.instance)
|
||||
"""Gets port binding based on vlan and instance."""
|
||||
return nexus_db_v2.get_nexusvm_bindings(npb.vlan, npb.instance)[0]
|
||||
|
||||
def _get_port_vlan_switch_binding(self, npb):
|
||||
"""Gets port bindings based on port, vlan, and switch."""
|
||||
@ -149,7 +149,7 @@ class CiscoNexusDbTest(base.BaseTestCase):
|
||||
self._assert_bindings_match(npb, npb22)
|
||||
|
||||
with testtools.ExpectedException(exceptions.NexusPortBindingNotFound):
|
||||
nexus_db_v2.get_nexusvm_binding(npb21.vlan, "dummyInstance")
|
||||
nexus_db_v2.get_nexusvm_bindings(npb21.vlan, "dummyInstance")[0]
|
||||
|
||||
def test_nexusportvlanswitchbinding_get(self):
|
||||
"""Tests get of port bindings based on port, vlan, and switch."""
|
||||
|
Loading…
Reference in New Issue
Block a user