Adding VPC(Virtual Port Channel) support to the Cisco plugin.
Change-Id: I898e4355d05f6f43593deb2d977dfc1e55fb2fc8
Implements: Blueprint cisco-plugin-vpc-support
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)
+
+ return _lookup_all_nexus_bindings(vlan_id=vlan_id,
+ instance_id=instance_id)
def get_port_vlan_switch_binding(port_id, vlan_id, switch_ip):
"""
LOG.debug(_("delete_port() called"))
port = self.get_port(context, id)
- if self.config_nexus:
+ exclude_list = ('', 'compute:none', 'network:dhcp')
+ if self.config_nexus and port['device_owner'] not in exclude_list:
vlan_id = self._get_segmentation_id(port['network_id'])
n_args = [port['device_id'], vlan_id]
self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
confstr = self.create_xml_snippet(confstr)
self._edit_config(nexus_host, target='running', config=confstr)
- def enable_port_trunk(self, nexus_host, interface):
+ def enable_port_trunk(self, nexus_host, etype, interface):
"""Enable trunk mode an interface on Nexus Switch."""
- confstr = snipp.CMD_PORT_TRUNK % (interface)
+ confstr = snipp.CMD_PORT_TRUNK % (etype, interface, etype)
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):
+ def disable_switch_port(self, nexus_host, etype, interface):
"""Disable trunk mode an interface on Nexus Switch."""
- confstr = snipp.CMD_NO_SWITCHPORT % (interface)
+ confstr = snipp.CMD_NO_SWITCHPORT % (etype, interface, etype)
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, etype, interface):
"""Enable a VLAN on a trunk interface."""
# 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_host):
+ if nexus_db_v2.get_port_switch_bindings('%s:%s' % (etype, interface),
+ nexus_host):
snippet = snipp.CMD_INT_VLAN_ADD_SNIPPET
else:
snippet = snipp.CMD_INT_VLAN_SNIPPET
- confstr = snippet % (interface, vlanid)
+ confstr = snippet % (etype, interface, vlanid, etype)
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, etype, interface):
"""Disable a VLAN on a trunk interface."""
- confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (interface, vlanid)
+ confstr = snipp.CMD_NO_VLAN_INT_SNIPPET % (etype, interface,
+ vlanid, etype)
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):
+ etype, 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,
+ etype, nexus_port)
- def delete_and_untrunk_vlan(self, nexus_host, vlan_id, nexus_port):
+ def delete_and_untrunk_vlan(self, nexus_host, vlan_id, etype, 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,
+ etype, nexus_port)
def create_vlan_svi(self, nexus_host, vlan_id, gateway_ip):
confstr = snipp.CMD_VLAN_SVI_SNIPPET % (vlan_id, gateway_ip)
"""Extract configuration parameters from the configuration file."""
self._client = importutils.import_object(conf.CISCO.nexus_driver)
LOG.debug(_("Loaded driver %s"), conf.CISCO.nexus_driver)
+ self._nexus_switches = conf.get_device_dictionary()
def get_all_networks(self, tenant_id):
"""Get all networks.
appropriate interfaces for this VLAN.
"""
LOG.debug(_("NexusPlugin:create_network() called"))
- # Grab the switch IP and port for this host
- host = str(attachment[const.HOST_NAME])
- switch_ip, port_id = self._client.get_switch_and_port_id(host)
- if not switch_ip and not port_id:
+ # Grab the switch IPs and ports for this host
+ host_connections = []
+ host = attachment['host_name']
+ for switch_type, switch_ip, attr in self._nexus_switches:
+ if str(attr) == str(host):
+ port = self._nexus_switches[switch_type, switch_ip, attr]
+ # Get ether type for port, assume an ethernet type
+ # if none specified.
+ if ':' in port:
+ etype, port_id = port.split(':')
+ else:
+ etype, port_id = 'ethernet', port
+ host_connections.append((switch_ip, etype, port_id))
+ if not host_connections:
raise cisco_exc.NexusComputeHostNotConfigured(host=host)
vlan_id = network[const.NET_VLAN_ID]
auto_trunk = conf.CISCO.provider_vlan_auto_trunk
# Check if this network is already in the DB
- vlan_created = False
- vlan_trunked = False
-
- try:
- nxos_db.get_port_vlan_switch_binding(port_id, vlan_id, switch_ip)
- except cisco_exc.NexusPortBindingNotFound:
- # Check for vlan/switch binding
+ for switch_ip, etype, port_id in host_connections:
+ vlan_created = False
+ vlan_trunked = False
+ eport_id = '%s:%s' % (etype, port_id)
try:
- nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+ nxos_db.get_port_vlan_switch_binding(eport_id, vlan_id,
+ switch_ip)
except cisco_exc.NexusPortBindingNotFound:
if auto_create and auto_trunk:
# Create vlan and trunk vlan on the port
LOG.debug("Nexus: create & trunk vlan %s" % vlan_name)
self._client.create_and_trunk_vlan(
- switch_ip, vlan_id, vlan_name, port_id)
+ switch_ip, vlan_id, vlan_name, etype, port_id)
vlan_created = True
vlan_trunked = True
elif auto_create:
LOG.debug("Nexus: create vlan %s" % vlan_name)
self._client.create_vlan(switch_ip, vlan_id, vlan_name)
vlan_created = True
- else:
- if auto_trunk:
+ elif auto_trunk:
# Only trunk vlan on the port
LOG.debug("Nexus: trunk vlan %s" % vlan_name)
self._client.enable_vlan_on_trunk_int(
- switch_ip, vlan_id, port_id)
+ switch_ip, vlan_id, etype, port_id)
vlan_trunked = True
- try:
- instance = attachment[const.INSTANCE_ID]
- nxos_db.add_nexusport_binding(port_id, str(vlan_id),
- switch_ip, instance)
- except Exception:
- with excutils.save_and_reraise_exception():
- # Add binding failed, roll back any vlan creation/enabling
- if vlan_created and vlan_trunked:
- LOG.debug("Nexus: delete & untrunk vlan %s" % vlan_name)
- self._client.delete_and_untrunk_vlan(switch_ip, vlan_id,
- port_id)
- elif vlan_created:
- LOG.debug("Nexus: delete vlan %s" % vlan_name)
- self._client.delete_vlan(switch_ip, vlan_id)
- elif vlan_trunked:
- LOG.debug("Nexus: untrunk vlan %s" % vlan_name)
- self._client.disable_vlan_on_trunk_int(switch_ip, vlan_id,
- port_id)
+ try:
+ instance = attachment[const.INSTANCE_ID]
+ nxos_db.add_nexusport_binding(eport_id, str(vlan_id),
+ switch_ip, instance)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ # Add binding failed, roll back any vlan creation/enabling
+ if vlan_created and vlan_trunked:
+ LOG.debug("Nexus: delete & untrunk vlan %s" %
+ vlan_name)
+ self._client.delete_and_untrunk_vlan(switch_ip,
+ vlan_id,
+ etype, port_id)
+ elif vlan_created:
+ LOG.debug("Nexus: delete vlan %s" % vlan_name)
+ self._client.delete_vlan(switch_ip, vlan_id)
+ elif vlan_trunked:
+ LOG.debug("Nexus: untrunk vlan %s" % vlan_name)
+ self._client.disable_vlan_on_trunk_int(switch_ip,
+ vlan_id,
+ etype,
+ port_id)
net_id = network[const.NET_ID]
new_net_dict = {const.NET_ID: net_id,
except cisco_exc.NexusPortBindingNotFound:
# Create vlan and trunk vlan on the port
self._client.create_and_trunk_vlan(
- switch_ip, vlan_id, vlan_name, nexus_port=None)
+ switch_ip, vlan_id, vlan_name, etype=None, nexus_port=None)
# Check if a router interface has already been created
try:
- nxos_db.get_nexusvm_binding(vlan_id, router_id)
+ nxos_db.get_nexusvm_bindings(vlan_id, router_id)
raise cisco_exc.SubnetInterfacePresent(subnet_id=subnet_id,
router_id=router_id)
except cisco_exc.NexusPortBindingNotFound:
def remove_router_interface(self, vlan_id, router_id):
"""Remove VLAN SVI from the Nexus Switch."""
# Grab switch_ip from database
- switch_ip = nxos_db.get_nexusvm_binding(vlan_id,
- router_id).switch_ip
+ switch_ip = nxos_db.get_nexusvm_bindings(vlan_id,
+ router_id)[0].switch_ip
# Delete the SVI interface from the switch
self._client.delete_vlan_svi(switch_ip, vlan_id)
is still required on the interfaces trunked.
"""
LOG.debug(_("NexusPlugin:delete_port() called"))
- # Delete DB row for this port
+ # Delete DB row(s) for this port
try:
- row = nxos_db.get_nexusvm_binding(vlan_id, device_id)
+ rows = nxos_db.get_nexusvm_bindings(vlan_id, device_id)
except cisco_exc.NexusPortBindingNotFound:
return
auto_untrunk = conf.CISCO.provider_vlan_auto_trunk
LOG.debug("delete_network(): provider vlan %s" % vlan_id)
- switch_ip = row.switch_ip
- nexus_port = None
- if row.port_id != 'router':
- nexus_port = row.port_id
+ instance_id = False
+ for row in rows:
+ instance_id = row['instance_id']
+ switch_ip = row.switch_ip
+ etype, nexus_port = '', ''
+ if row['port_id'] == 'router':
+ etype, nexus_port = 'vlan', row['port_id']
+ else:
+ etype, nexus_port = row['port_id'].split(':')
- nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
- row.switch_ip,
- row.instance_id)
- # Check for any other bindings with the same vlan_id and switch_ip
- try:
- nxos_db.get_nexusvlan_binding(row.vlan_id, row.switch_ip)
- except cisco_exc.NexusPortBindingNotFound:
+ nxos_db.remove_nexusport_binding(row.port_id, row.vlan_id,
+ row.switch_ip,
+ row.instance_id)
+ # Check for any other bindings with the same vlan_id and switch_ip
try:
- # Delete this vlan from this switch
- if nexus_port and auto_untrunk:
- self._client.disable_vlan_on_trunk_int(
- switch_ip, row.vlan_id, nexus_port)
- if auto_delete:
- self._client.delete_vlan(switch_ip, row.vlan_id)
- except Exception:
- # The delete vlan operation on the Nexus failed,
- # so this delete_port request has failed. For
- # consistency, roll back the Nexus database to what
- # it was before this request.
- with excutils.save_and_reraise_exception():
- nxos_db.add_nexusport_binding(row.port_id,
- row.vlan_id,
- row.switch_ip,
- row.instance_id)
-
- return row.instance_id
+ nxos_db.get_nexusvlan_binding(row.vlan_id, row.switch_ip)
+ except cisco_exc.NexusPortBindingNotFound:
+ try:
+ # Delete this vlan from this switch
+ if nexus_port and auto_untrunk:
+ self._client.disable_vlan_on_trunk_int(
+ switch_ip, row.vlan_id, etype, nexus_port)
+ if auto_delete:
+ self._client.delete_vlan(switch_ip, row.vlan_id)
+ except Exception:
+ # The delete vlan operation on the Nexus failed,
+ # so this delete_port request has failed. For
+ # consistency, roll back the Nexus database to what
+ # it was before this request.
+ with excutils.save_and_reraise_exception():
+ nxos_db.add_nexusport_binding(row.port_id,
+ row.vlan_id,
+ row.switch_ip,
+ row.instance_id)
+
+ return instance_id
def update_port(self, tenant_id, net_id, port_id, port_state, **kwargs):
"""Update port.
# under the License.
#
# @author: Edgar Magana, Cisco Systems, Inc.
+# @author: Arvind Somya (asomya@cisco.com) Cisco Systems, Inc.
"""
Nexus-OS XML-based configuration snippets
CMD_INT_VLAN_HEADER = """
<interface>
- <ethernet>
+ <%s>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport>
</trunk>
</switchport>
</__XML__MODE_if-ethernet-switch>
- </ethernet>
+ </%s>
</interface>
"""
CMD_PORT_TRUNK = """
<interface>
- <ethernet>
+ <%s>
<interface>%s</interface>
<__XML__MODE_if-ethernet-switch>
<switchport></switchport>
</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>
</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>
</trunk>
</switchport>
</__XML__MODE_if-ethernet-switch>
- </ethernet>
+ </%s>
</interface>
"""
"""Disable trunk mode an interface on Nexus Switch."""
pass
- def enable_vlan_on_trunk_int(self, mgr, interface, vlanid):
+ def enable_vlan_on_trunk_int(self, mgr, etype, interface, vlanid):
"""Enable vlan on trunk interface.
Enable trunk mode vlan access an interface on Nexus Switch given
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)
+ res = self._create_port(self.fmt, net_id,
+ device_id='testdev',
+ device_owner='testowner')
port = self.deserialize(self.fmt, res)
try:
yield res
npb22 = self._npb_test_obj(20, 200)
self._add_to_db([npb11, npb21, npb22])
- npb = nxdb.get_nexusvm_binding(npb21.vlan, npb21.instance)
+ npb = nxdb.get_nexusvm_bindings(npb21.vlan, npb21.instance)[0]
self._assert_equal(npb, npb21)
- npb = nxdb.get_nexusvm_binding(npb22.vlan, npb22.instance)
+ npb = nxdb.get_nexusvm_bindings(npb22.vlan, npb22.instance)[0]
self._assert_equal(npb, npb22)
with testtools.ExpectedException(c_exc.NexusPortBindingNotFound):
- nxdb.get_nexusvm_binding(npb21.vlan, "dummyInstance")
+ nxdb.get_nexusvm_bindings(npb21.vlan, "dummyInstance")
def test_nexusportvlanswitchbinding_get(self):
npb11 = self._npb_test_obj(10, 100)
NEXUS_IP_ADDRESS = '1.1.1.1'
HOSTNAME1 = 'testhost1'
HOSTNAME2 = 'testhost2'
+HOSTNAME3 = 'testhost3'
INSTANCE1 = 'testvm1'
INSTANCE2 = 'testvm2'
+INSTANCE3 = 'testvm3'
NEXUS_PORT1 = '1/10'
NEXUS_PORT2 = '1/20'
+NEXUS_PC_IP_ADDRESS = '2.2.2.2'
+NEXUS_PORTCHANNELS = 'portchannel:2'
+PC_HOSTNAME = 'testpchost'
NEXUS_SSH_PORT = '22'
NEXUS_DRIVER = ('neutron.plugins.cisco.nexus.'
'cisco_nexus_network_driver_v2.CiscoNEXUSDriver')
self.second_net_id = 5
self.second_vlan_name = "q-" + str(self.second_net_id) + "vlan"
self.second_vlan_id = 265
+ self._pchostname = PC_HOSTNAME
+
self.attachment1 = {
const.TENANT_ID: self.tenant_id,
const.INSTANCE_ID: INSTANCE1,
const.INSTANCE_ID: INSTANCE2,
const.HOST_NAME: HOSTNAME2,
}
+ self.attachment3 = {
+ const.TENANT_ID: self.second_tenant_id,
+ const.INSTANCE_ID: INSTANCE3,
+ const.HOST_NAME: HOSTNAME3,
+ }
self.network1 = {
const.NET_ID: self.net_id,
const.NET_NAME: self.net_name,
const.NET_VLAN_NAME: self.second_vlan_name,
const.NET_VLAN_ID: self.second_vlan_id,
}
+ self.network3 = {
+ const.NET_ID: 8,
+ const.NET_NAME: 'vpc_net',
+ const.NET_VLAN_NAME: 'q-268',
+ const.NET_VLAN_ID: '268',
+ }
self.providernet = {
const.NET_ID: 9,
const.NET_NAME: 'pnet1',
(NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
(NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
(NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
+ (NEXUS_PC_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
+ }
+ self._nexus_switches = {
+ ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME1): NEXUS_PORT1,
+ ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME2): NEXUS_PORT2,
+ ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, HOSTNAME3):
+ NEXUS_PORTCHANNELS,
+ ('NEXUS_SWITCH', NEXUS_PC_IP_ADDRESS, 'ssh_port'):
+ NEXUS_SSH_PORT,
+ ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, HOSTNAME3):
+ NEXUS_PORTCHANNELS,
+ ('NEXUS_SWITCH', NEXUS_IP_ADDRESS, 'ssh_port'): NEXUS_SSH_PORT,
}
self._client.credentials = {
NEXUS_IP_ADDRESS: {
'username': 'admin',
'password': 'pass1234'
},
+ NEXUS_PC_IP_ADDRESS: {
+ 'username': 'admin',
+ 'password': 'password'
+ },
}
db.configure_db()
self.addCleanup(self.patch_obj.stop)
- def test_create_networks(self):
+ def test_create_delete_networks(self):
"""Tests creation of two new Virtual Networks."""
new_net_dict = self._cisco_nexus_plugin.create_network(
self.network1, self.attachment1)
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.network1[attr])
+ expected_instance_id = self._cisco_nexus_plugin.delete_port(
+ INSTANCE1, self.vlan_id)
+
+ self.assertEqual(expected_instance_id, INSTANCE1)
+
new_net_dict = self._cisco_nexus_plugin.create_network(
self.network2, self.attachment1)
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.network2[attr])
+ expected_instance_id = self._cisco_nexus_plugin.delete_port(
+ INSTANCE1, self.second_vlan_id)
+
+ self.assertEqual(expected_instance_id, INSTANCE1)
+
def test_create_providernet(self):
with mock.patch.object(cdb, 'is_provider_vlan',
return_value=True) as mock_db:
for attr in NET_ATTRS:
self.assertEqual(new_net_dict[attr], self.providernet[attr])
- def test_nexus_delete_port(self):
- """Test deletion of a vlan."""
- self._cisco_nexus_plugin.create_network(
- self.network1, self.attachment1)
-
- expected_instance_id = self._cisco_nexus_plugin.delete_port(
- INSTANCE1, self.vlan_id)
+ def test_create_delete_network_portchannel(self):
+ """Tests creation of a network over a portchannel."""
+ new_net_dict = self._cisco_nexus_plugin.create_network(
+ self.network3, self.attachment3)
+ self.assertEqual(new_net_dict[const.NET_ID],
+ self.network3[const.NET_ID])
+ self.assertEqual(new_net_dict[const.NET_NAME],
+ self.network3[const.NET_NAME])
+ self.assertEqual(new_net_dict[const.NET_VLAN_NAME],
+ self.network3[const.NET_VLAN_NAME])
+ self.assertEqual(new_net_dict[const.NET_VLAN_ID],
+ self.network3[const.NET_VLAN_ID])
- self.assertEqual(expected_instance_id, INSTANCE1)
+ self._cisco_nexus_plugin.delete_port(
+ INSTANCE3, self.network3[const.NET_VLAN_ID]
+ )
def test_nexus_add_remove_router_interface(self):
"""Tests addition of a router interface."""