host_connections = []
for switch_ip, attr in self._nexus_switches:
if str(attr) == str(host_id):
- port_id = self._nexus_switches[switch_ip, attr]
- 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))
+ for port_id in (
+ self._nexus_switches[switch_ip, attr].split(',')):
+ 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
vlan_name = cfg.CONF.ml2_cisco.vlan_name_prefix + str(vlan_id)
host_connections = self._get_switch_info(host_id)
+ # (nexus_port,switch_ip) will be unique in each iteration.
+ # But switch_ip will repeat if host has >1 connection to same switch.
+ # So track which switch_ips already have vlan created in this loop.
+ vlan_already_created = []
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)
+
+ # The VLAN needs to be created on the switch if no other
+ # instance has been placed in this VLAN on a different host
+ # attached to this switch. Search the existing bindings in the
+ # database. If all the instance_id in the database match the
+ # current device_id, then create the VLAN, but only once per
+ # switch_ip. Otherwise, just trunk.
+ all_bindings = nxos_db.get_nexusvlan_binding(vlan_id, switch_ip)
+ previous_bindings = [row for row in all_bindings
+ if row.instance_id != device_id]
+ if previous_bindings or (switch_ip in vlan_already_created):
+ LOG.debug("Nexus: trunk vlan %s"), vlan_name
self.driver.enable_vlan_on_trunk_int(switch_ip, vlan_id,
intf_type, nexus_port)
+ else:
+ vlan_already_created.append(switch_ip)
+ 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)
def _delete_nxos_db(self, vlan_id, device_id, host_id):
"""Delete the nexus database entry.
Called during update postcommit port event.
"""
host_connections = self._get_switch_info(host_id)
+
+ # (nexus_port,switch_ip) will be unique in each iteration.
+ # But switch_ip will repeat if host has >1 connection to same switch.
+ # So track which switch_ips already have vlan removed in this loop.
+ vlan_already_removed = []
for switch_ip, intf_type, nexus_port in host_connections:
+
# if there are no remaining db entries using this vlan on this
# 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)
except excep.NexusPortBindingNotFound:
- self.driver.delete_vlan(switch_ip, vlan_id)
+
+ # Do not perform a second time on same switch
+ if switch_ip not in vlan_already_removed:
+ self.driver.delete_vlan(switch_ip, vlan_id)
+ vlan_already_removed.append(switch_ip)
def _is_vm_migration(self, context):
if not context.bound_segment and context.original_bound_segment:
'ssh_port': [22],
'compute1': ['1/1'],
'compute2': ['1/2'],
+ 'compute5': ['1/3,1/4']
},
'ml2_mech_cisco_nexus:2.2.2.2': {
'username': ['admin'],
'ssh_port': [22],
'compute3': ['1/1'],
'compute4': ['1/2'],
+ 'compute5': ['portchannel:20,portchannel:30']
},
}
expected_dev_dict = {
('1.1.1.1', 'ssh_port'): 22,
('1.1.1.1', 'compute1'): '1/1',
('1.1.1.1', 'compute2'): '1/2',
+ ('1.1.1.1', 'compute5'): '1/3,1/4',
('2.2.2.2', 'username'): 'admin',
('2.2.2.2', 'password'): 'mySecretPassword',
('2.2.2.2', 'ssh_port'): 22,
('2.2.2.2', 'compute3'): '1/1',
('2.2.2.2', 'compute4'): '1/2',
+ ('2.2.2.2', 'compute5'): 'portchannel:20,portchannel:30',
}
with mock.patch.object(cfg, 'MultiConfigParser') as parser:
parser.return_value.read.return_value = cfg.CONF.config_file
NEXUS_IP_ADDRESS = '1.1.1.1'
NEXUS_IP_ADDRESS_PC = '2.2.2.2'
+NEXUS_IP_ADDRESS_DUAL = '3.3.3.3'
HOST_NAME_1 = 'testhost1'
HOST_NAME_2 = 'testhost2'
HOST_NAME_PC = 'testpchost'
+HOST_NAME_DUAL = 'testdualhost'
INSTANCE_1 = 'testvm1'
INSTANCE_2 = 'testvm2'
INSTANCE_PC = 'testpcvm'
+INSTANCE_DUAL = 'testdualvm'
NEXUS_PORT_1 = 'ethernet:1/10'
NEXUS_PORT_2 = 'ethernet:1/20'
NEXUS_PORTCHANNELS = 'portchannel:2'
+NEXUS_DUAL = 'ethernet:1/3,portchannel:2'
VLAN_ID_1 = 267
VLAN_ID_2 = 265
VLAN_ID_PC = 268
+VLAN_ID_DUAL = 269
DEVICE_OWNER = 'compute:test'
NEXUS_SSH_PORT = '22'
PORT_STATE = n_const.PORT_STATUS_ACTIVE
NEXUS_PORTCHANNELS,
INSTANCE_PC,
VLAN_ID_PC),
+ 'test_config_dual': TestConfigObj(
+ NEXUS_IP_ADDRESS_DUAL,
+ HOST_NAME_DUAL,
+ NEXUS_DUAL,
+ INSTANCE_DUAL,
+ VLAN_ID_DUAL),
}
def setUp(self):
self._cisco_mech_driver.update_port_precommit(port_context)
self._cisco_mech_driver.update_port_postcommit(port_context)
- bindings = nexus_db_v2.get_nexusport_binding(nexus_port,
- vlan_id,
- nexus_ip_addr,
- instance_id)
- self.assertEqual(len(bindings), 1)
+ for port_id in nexus_port.split(','):
+ bindings = nexus_db_v2.get_nexusport_binding(port_id,
+ vlan_id,
+ nexus_ip_addr,
+ instance_id)
+ self.assertEqual(len(bindings), 1)
self._cisco_mech_driver.delete_port_precommit(port_context)
self._cisco_mech_driver.delete_port_postcommit(port_context)
- with testtools.ExpectedException(exceptions.NexusPortBindingNotFound):
- nexus_db_v2.get_nexusport_binding(nexus_port,
- vlan_id,
- nexus_ip_addr,
- instance_id)
+ for port_id in nexus_port.split(','):
+ with testtools.ExpectedException(
+ exceptions.NexusPortBindingNotFound):
+ nexus_db_v2.get_nexusport_binding(port_id,
+ vlan_id,
+ nexus_ip_addr,
+ instance_id)
def test_create_delete_ports(self):
"""Tests creation and deletion of two new virtual Ports."""
"""Tests creation of a port over a portchannel."""
self._create_delete_port(
TestCiscoNexusDevice.test_configs['test_config_portchannel'])
+
+ def test_create_delete_dual(self):
+ """Tests creation and deletion of dual ports for single server"""
+ self._create_delete_port(
+ TestCiscoNexusDevice.test_configs['test_config_dual'])