Merge "ML2 Cisco Nexus MD: Support portchannel interfaces"

This commit is contained in:
Jenkins 2014-04-10 14:29:21 +00:00 committed by Gerrit Code Review
commit 2ddc62d0c0
8 changed files with 94 additions and 78 deletions

View File

@ -23,13 +23,16 @@
# #
# Cisco Nexus Switch Format. # Cisco Nexus Switch Format.
# [ml2_mech_cisco_nexus:<IP address of switch>] # [ml2_mech_cisco_nexus:<IP address of switch>]
# <hostname>=<port> (1) # <hostname>=<intf_type:port> (1)
# ssh_port=<ssh port> (2) # ssh_port=<ssh port> (2)
# username=<credential username> (3) # username=<credential username> (3)
# password=<credential password> (4) # password=<credential password> (4)
# #
# (1) For each host connected to a port on the switch, specify the hostname # (1) For each host connected to a port on the switch, specify the hostname
# and the Nexus physical port (interface) it is connected to. # 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 # (2) The TCP port for connecting via SSH to manage the switch. This is
# port number 22 unless the switch has been configured otherwise. # port number 22 unless the switch has been configured otherwise.
# (3) The username for logging into the switch to manage it. # (3) The username for logging into the switch to manage it.
@ -38,7 +41,8 @@
# Example: # Example:
# [ml2_mech_cisco_nexus:1.1.1.1] # [ml2_mech_cisco_nexus:1.1.1.1]
# compute1=1/1 # compute1=1/1
# compute2=1/2 # compute2=ethernet:1/2
# compute3=port-channel:1
# ssh_port=22 # ssh_port=22
# username=admin # username=admin
# password=mySecretPassword # password=mySecretPassword

View File

@ -68,10 +68,18 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
return port['status'] == n_const.PORT_STATUS_ACTIVE return port['status'] == n_const.PORT_STATUS_ACTIVE
def _get_switch_info(self, host_id): def _get_switch_info(self, host_id):
host_connections = []
for switch_ip, attr in self._nexus_switches: for switch_ip, attr in self._nexus_switches:
if str(attr) == str(host_id): if str(attr) == str(host_id):
port_id = self._nexus_switches[switch_ip, attr] 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: else:
raise excep.NexusComputeHostNotConfigured(host=host_id) raise excep.NexusComputeHostNotConfigured(host=host_id)
@ -80,7 +88,9 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
Called during update precommit port event. Called during update precommit port event.
""" """
port_id, switch_ip = self._get_switch_info(host_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, nxos_db.add_nexusport_binding(port_id, str(vlan_id), switch_ip,
device_id) device_id)
@ -92,19 +102,21 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
Called during update postcommit port event. 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) vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id)
host_connections = self._get_switch_info(host_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 # Check to see if this is the first binding to use this vlan on the
# switch/port. Configure switch accordingly. # switch/port. Configure switch accordingly.
bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip) bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
if len(bindings) == 1: if len(bindings) == 1:
LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name) LOG.debug(_("Nexus: create & trunk vlan %s"), vlan_name)
self.driver.create_and_trunk_vlan(switch_ip, vlan_id, vlan_name, self.driver.create_and_trunk_vlan(
port_id) switch_ip, vlan_id, vlan_name, intf_type, nexus_port)
else: else:
LOG.debug(_("Nexus: trunk vlan %s"), vlan_name) LOG.debug(_("Nexus: trunk vlan %s"), vlan_name)
self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id, port_id) 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): def _delete_nxos_db(self, vlan_id, device_id, host_id):
"""Delete the nexus database entry. """Delete the nexus database entry.
@ -112,9 +124,10 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
Called during delete precommit port event. Called during delete precommit port event.
""" """
try: try:
row = nxos_db.get_nexusvm_binding(vlan_id, device_id) rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id, for row in rows:
row.switch_ip, row.instance_id) nxos_db.remove_nexusport_binding(
row.port_id, row.vlan_id, row.switch_ip, row.instance_id)
except excep.NexusPortBindingNotFound: except excep.NexusPortBindingNotFound:
return return
@ -126,14 +139,17 @@ class CiscoNexusMechanismDriver(api.MechanismDriver):
Called during update postcommit port event. Called during update postcommit port event.
""" """
port_id, switch_ip = self._get_switch_info(host_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 # if there are no remaining db entries using this vlan on this
# switch port then remove vlan from the switchport trunk. # nexus switch port then remove vlan from the switchport trunk.
port_id = '%s:%s' % (intf_type, nexus_port)
try: try:
nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip) nxos_db.get_port_vlan_switch_binding(port_id, vlan_id,
switch_ip)
except excep.NexusPortBindingNotFound: except excep.NexusPortBindingNotFound:
self.driver.disable_vlan_on_trunk_int(switch_ip, vlan_id, port_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 # if there are no remaining db entries using this vlan on this
# nexus switch then remove the vlan. # nexus switch then remove the vlan.

View File

@ -82,10 +82,10 @@ def update_nexusport_binding(port_id, new_vlan_id):
return binding return binding
def get_nexusvm_binding(vlan_id, instance_id): def get_nexusvm_bindings(vlan_id, instance_id):
"""Lists nexusvm bindings.""" """Lists nexusvm bindings."""
LOG.debug(_("get_nexusvm_binding() called")) LOG.debug(_("get_nexusvm_bindings() called"))
return _lookup_first_nexus_binding(instance_id=instance_id, return _lookup_all_nexus_bindings(instance_id=instance_id,
vlan_id=vlan_id) vlan_id=vlan_id)

View File

@ -154,54 +154,46 @@ class CiscoNexusDriver(object):
confstr = self.create_xml_snippet(confstr) confstr = self.create_xml_snippet(confstr)
self._edit_config(nexus_host, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def enable_port_trunk(self, nexus_host, interface): def enable_vlan_on_trunk_int(self, nexus_host, vlanid, intf_type,
"""Enable trunk mode an interface on Nexus Switch.""" interface):
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):
"""Enable a VLAN on a trunk interface.""" """Enable a VLAN on a trunk interface."""
# If more than one VLAN is configured on this interface then # If more than one VLAN is configured on this interface then
# include the 'add' keyword. # include the 'add' keyword.
if len(nexus_db_v2.get_port_switch_bindings(interface, if len(nexus_db_v2.get_port_switch_bindings(
nexus_host)) == 1: '%s:%s' % (intf_type, interface), nexus_host)) == 1:
snippet = snipp.CMD_INT_VLAN_SNIPPET snippet = snipp.CMD_INT_VLAN_SNIPPET
else: else:
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET 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) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(nexus_host, target='running', config=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.""" """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) confstr = self.create_xml_snippet(confstr)
LOG.debug(_("NexusDriver: %s"), confstr) LOG.debug(_("NexusDriver: %s"), confstr)
self._edit_config(nexus_host, target='running', config=confstr) self._edit_config(nexus_host, target='running', config=confstr)
def create_and_trunk_vlan(self, nexus_host, vlan_id, vlan_name, 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.""" """Create VLAN and trunk it on the specified ports."""
self.create_vlan(nexus_host, vlan_id, vlan_name) self.create_vlan(nexus_host, vlan_id, vlan_name)
LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id) LOG.debug(_("NexusDriver created VLAN: %s"), vlan_id)
if nexus_port: 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.""" """Delete VLAN and untrunk it from the specified ports."""
self.delete_vlan(nexus_host, vlan_id) self.delete_vlan(nexus_host, vlan_id)
if nexus_port: 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): def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip) confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)

View File

@ -85,7 +85,7 @@ CMD_NO_VLAN_CONF_SNIPPET = """
CMD_INT_VLAN_HEADER = """ CMD_INT_VLAN_HEADER = """
<interface> <interface>
<ethernet> <%s>
<interface>%s</interface> <interface>%s</interface>
<__XML__MODE_if-ethernet-switch> <__XML__MODE_if-ethernet-switch>
<switchport> <switchport>
@ -106,7 +106,7 @@ CMD_INT_VLAN_TRAILER = """
</trunk> </trunk>
</switchport> </switchport>
</__XML__MODE_if-ethernet-switch> </__XML__MODE_if-ethernet-switch>
</ethernet> </%s>
</interface> </interface>
""" """
@ -120,7 +120,7 @@ CMD_INT_VLAN_ADD_SNIPPET = (CMD_INT_VLAN_HEADER +
CMD_PORT_TRUNK = """ CMD_PORT_TRUNK = """
<interface> <interface>
<ethernet> <%s>
<interface>%s</interface> <interface>%s</interface>
<__XML__MODE_if-ethernet-switch> <__XML__MODE_if-ethernet-switch>
<switchport></switchport> <switchport></switchport>
@ -131,13 +131,13 @@ CMD_PORT_TRUNK = """
</mode> </mode>
</switchport> </switchport>
</__XML__MODE_if-ethernet-switch> </__XML__MODE_if-ethernet-switch>
</ethernet> </%s>
</interface> </interface>
""" """
CMD_NO_SWITCHPORT = """ CMD_NO_SWITCHPORT = """
<interface> <interface>
<ethernet> <%s>
<interface>%s</interface> <interface>%s</interface>
<__XML__MODE_if-ethernet-switch> <__XML__MODE_if-ethernet-switch>
<no> <no>
@ -145,13 +145,13 @@ CMD_NO_SWITCHPORT = """
</switchport> </switchport>
</no> </no>
</__XML__MODE_if-ethernet-switch> </__XML__MODE_if-ethernet-switch>
</ethernet> </%s>
</interface> </interface>
""" """
CMD_NO_VLAN_INT_SNIPPET = """ CMD_NO_VLAN_INT_SNIPPET = """
<interface> <interface>
<ethernet> <%s>
<interface>%s</interface> <interface>%s</interface>
<__XML__MODE_if-ethernet-switch> <__XML__MODE_if-ethernet-switch>
<switchport></switchport> <switchport></switchport>
@ -167,7 +167,7 @@ CMD_NO_VLAN_INT_SNIPPET = """
</trunk> </trunk>
</switchport> </switchport>
</__XML__MODE_if-ethernet-switch> </__XML__MODE_if-ethernet-switch>
</ethernet> </%s>
</interface> </interface>
""" """

View File

@ -418,8 +418,10 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
with self._create_resources() as result: with self._create_resources() as result:
# Verify initial database entry. # Verify initial database entry.
# Use port_id to verify that 1st host name was used. # Use port_id to verify that 1st host name was used.
binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1) binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
self.assertEqual(binding.port_id, NEXUS_INTERFACE) 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 = self.deserialize(self.fmt, result)
port_id = port['port']['id'] port_id = port['port']['id']
@ -434,7 +436,7 @@ class TestCiscoPortsV2(CiscoML2MechanismTestCase,
# Verify that port entry has been deleted. # Verify that port entry has been deleted.
self.assertRaises(c_exc.NexusPortBindingNotFound, self.assertRaises(c_exc.NexusPortBindingNotFound,
nexus_db_v2.get_nexusvm_binding, nexus_db_v2.get_nexusvm_bindings,
VLAN_START, DEVICE_ID_1) VLAN_START, DEVICE_ID_1)
# Trigger update event to bind segment with new host. # 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. # Verify that port entry has been added using new host name.
# Use port_id to verify that 2nd host name was used. # Use port_id to verify that 2nd host name was used.
binding = nexus_db_v2.get_nexusvm_binding(VLAN_START, DEVICE_ID_1) binding = nexus_db_v2.get_nexusvm_bindings(VLAN_START,
self.assertEqual(binding.port_id, NEXUS_INTERFACE_2) 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): def test_nexus_config_fail(self):
"""Test a Nexus switch configuration failure. """Test a Nexus switch configuration failure.

View File

@ -38,8 +38,8 @@ HOST_NAME_PC = 'testpchost'
INSTANCE_1 = 'testvm1' INSTANCE_1 = 'testvm1'
INSTANCE_2 = 'testvm2' INSTANCE_2 = 'testvm2'
INSTANCE_PC = 'testpcvm' INSTANCE_PC = 'testpcvm'
NEXUS_PORT_1 = '1/10' NEXUS_PORT_1 = 'ethernet:1/10'
NEXUS_PORT_2 = '1/20' NEXUS_PORT_2 = 'ethernet:1/20'
NEXUS_PORTCHANNELS = 'portchannel:2' NEXUS_PORTCHANNELS = 'portchannel:2'
VLAN_ID_1 = 267 VLAN_ID_1 = 267
VLAN_ID_2 = 265 VLAN_ID_2 = 265

View File

@ -76,8 +76,8 @@ class CiscoNexusDbTest(base.BaseTestCase):
return nexus_db_v2.get_nexusvlan_binding(npb.vlan, npb.switch) return nexus_db_v2.get_nexusvlan_binding(npb.vlan, npb.switch)
def _get_nexusvm_binding(self, npb): def _get_nexusvm_binding(self, npb):
"""Gets port bindings based on vlan and instance.""" """Gets port binding based on vlan and instance."""
return nexus_db_v2.get_nexusvm_binding(npb.vlan, npb.instance) return nexus_db_v2.get_nexusvm_bindings(npb.vlan, npb.instance)[0]
def _get_port_vlan_switch_binding(self, npb): def _get_port_vlan_switch_binding(self, npb):
"""Gets port bindings based on port, vlan, and switch.""" """Gets port bindings based on port, vlan, and switch."""
@ -149,7 +149,7 @@ class CiscoNexusDbTest(base.BaseTestCase):
self._assert_bindings_match(npb, npb22) self._assert_bindings_match(npb, npb22)
with testtools.ExpectedException(exceptions.NexusPortBindingNotFound): 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): def test_nexusportvlanswitchbinding_get(self):
"""Tests get of port bindings based on port, vlan, and switch.""" """Tests get of port bindings based on port, vlan, and switch."""