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.
# [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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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