diff --git a/neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py b/neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py index 60a1de35394..c14dfee0c22 100644 --- a/neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py +++ b/neutron/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc.py @@ -232,7 +232,8 @@ class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin, vlan_manager = vlanmanager.LocalVlanManager() for network_id, values in fdb_entries.items(): try: - lvm = vlan_manager.get(network_id) + lvm = vlan_manager.get( + network_id, values.get('segment_id')) except vlanmanager.MappingNotFound: continue agent_ports = values.get('ports') @@ -307,22 +308,23 @@ class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin, vlan_manager = vlanmanager.LocalVlanManager() for network_id, agent_ports in fdb_entries.items(): try: - lvm = vlan_manager.get(network_id) + lvms = vlan_manager.get_segments(network_id) except vlanmanager.MappingNotFound: continue - for agent_ip, state in agent_ports.items(): - if agent_ip == local_ip: - continue + for lvm in lvms.values(): + for agent_ip, state in agent_ports.items(): + if agent_ip == local_ip: + continue - after = state.get('after', []) - for mac_ip in after: - self.setup_entry_for_arp_reply(br, 'add', lvm.vlan, - mac_ip.mac_address, - mac_ip.ip_address) + after = state.get('after', []) + for mac_ip in after: + self.setup_entry_for_arp_reply(br, 'add', lvm.vlan, + mac_ip.mac_address, + mac_ip.ip_address) - before = state.get('before', []) - for mac_ip in before: - self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan, - mac_ip.mac_address, - mac_ip.ip_address) + before = state.get('before', []) + for mac_ip in before: + self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan, + mac_ip.mac_address, + mac_ip.ip_address) diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index ca94a09488c..2fe727bf167 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -490,15 +490,20 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, return try: - lvm = self.vlan_manager.get(network['id']) - except vlanmanager.MappingNotFound: + segmentation_id_old, lvm = ( + self.vlan_manager.update_segmentation_id( + network['id'], network[provider_net.SEGMENTATION_ID])) + except vlanmanager.NotUniqMapping: + # There is a design issue, the RPC update network should not accept + # to update the segmentation id. We still support it if only one + # segment per network. + LOG.warning("Can't update segmentation id on no uniq segment " + "for a network %s", network['id']) return - segmentation_id_old = lvm.segmentation_id - if segmentation_id_old == network[provider_net.SEGMENTATION_ID]: + if segmentation_id_old is None: + # The segmentation id did not changed. return - self.vlan_manager.update_segmentation_id( - network['id'], network[provider_net.SEGMENTATION_ID]) lvid = lvm.vlan physical_network = network[provider_net.PHYSICAL_NETWORK] @@ -716,9 +721,9 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, def _add_network_ports(self, network_id, segmentation_id, port_id): self.network_ports[network_id][segmentation_id].add(port_id) - def _get_net_local_vlan_or_none(self, net_id): + def _get_net_local_vlan_or_none(self, net_id, segmentation_id): try: - return self.vlan_manager.get(net_id).vlan + return self.vlan_manager.get(net_id, segmentation_id).vlan except vlanmanager.MappingNotFound: return None @@ -982,22 +987,25 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, tags available. """ try: - lvm = self.vlan_manager.get(net_uuid) + lvm = self.vlan_manager.get(net_uuid, segmentation_id) except vlanmanager.MappingNotFound: lvid = self._local_vlan_hints.pop(net_uuid, None) if lvid is None: if not self.available_local_vlans: - LOG.error("No local VLAN available for net-id=%s", - net_uuid) + LOG.error("No local VLAN available for net-id=%s, " + "seg-id=%s", + net_uuid, segmentation_id) return lvid = self.available_local_vlans.pop() self.vlan_manager.add( net_uuid, lvid, network_type, physical_network, segmentation_id) - lvm = self.vlan_manager.get(net_uuid) + lvm = self.vlan_manager.get(net_uuid, segmentation_id) LOG.info( - "Assigning %(vlan_id)s as local vlan for net-id=%(net_uuid)s", - {'vlan_id': lvm.vlan, 'net_uuid': net_uuid}) + "Assigning %(vlan_id)s as local vlan for net-id=%(net_uuid)s, " + "seg-id=%(seg_id)s", + {'vlan_id': lvm.vlan, 'net_uuid': net_uuid, + 'seg_id': segmentation_id}) return lvm @@ -1067,20 +1075,23 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, {'network_type': network_type, 'net_uuid': net_uuid}) - def reclaim_local_vlan(self, net_uuid): + def reclaim_local_vlan(self, net_uuid, segmentation_id): '''Reclaim a local VLAN. :param net_uuid: the network uuid associated with this vlan. ''' try: - lvm = vlanmanager.LocalVlanManager().pop(net_uuid) + lvm = vlanmanager.LocalVlanManager().pop( + net_uuid, segmentation_id) except vlanmanager.MappingNotFound: - LOG.debug("Network %s not used on agent.", net_uuid) + LOG.debug("Network %s, for segmentation %s, not used on agent.", + net_uuid, segmentation_id) return LOG.info("Reclaiming vlan = %(vlan_id)s from " - "net-id = %(net_uuid)s", - {'vlan_id': lvm.vlan, 'net_uuid': net_uuid}) + "net-id = %(net_uuid)s, seg-id = %(seg_id)s", + {'vlan_id': lvm.vlan, 'net_uuid': net_uuid, + 'seg_id': segmentation_id}) if lvm.network_type in ovs_const.TUNNEL_NETWORK_TYPES: if self.enable_tunneling: @@ -1158,10 +1169,15 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, restart or recreated physical bridges and requires to do local vlan provisioning ''' - if net_uuid not in self.vlan_manager or provisioning_needed: + try: + lvm = self.vlan_manager.get(net_uuid, segmentation_id) + except vlanmanager.MappingNotFound: + lvm = None + if lvm is None or provisioning_needed: self.provision_local_vlan(net_uuid, network_type, physical_network, segmentation_id) - lvm = self.vlan_manager.get(net_uuid) + lvm = self.vlan_manager.get(net_uuid, segmentation_id) + lvm.vif_ports[port.vif_id] = port self.dvr_agent.bind_port_to_dvr(port, lvm, @@ -1201,7 +1217,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, tags_by_name = {x['name']: x['tag'] for x in port_info} for port_detail in need_binding_ports: try: - lvm = self.vlan_manager.get(port_detail['network_id']) + lvm = self.vlan_manager.get(port_detail['network_id'], + port_detail['segmentation_id']) except vlanmanager.MappingNotFound: # network for port was deleted. skip this port since it # will need to be handled as a DEAD port in the next scan @@ -1322,13 +1339,21 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, def _get_port_lvm_and_vif(self, vif_id, net_uuid=None): try: - net_uuid = net_uuid or self.vlan_manager.get_net_uuid(vif_id) + net_uuid, segmentation_id = ( + self.vlan_manager.get_net_and_segmentation_id( + vif_id, net_uuid=net_uuid)) + lvm = self.vlan_manager.get(net_uuid, segmentation_id) except vlanmanager.VifIdNotFound: LOG.info('net_uuid %s not managed by VLAN manager', net_uuid) - return None, None, None - - lvm = self.vlan_manager.get(net_uuid) + if net_uuid: + # TODO(sahid); This needs to be fixed. It supposes a segment + # per network per host. Basically this code is to avoid + # changing logic which is not the aim of this commit. + segs = self.vlan_manager.get_segments(net_uuid) + lvm = self.vlan_manager.get(net_uuid, list(segs.keys())[0]) + else: + return None, None, None if vif_id in lvm.vif_ports: vif_port = lvm.vif_ports[vif_id] @@ -1352,9 +1377,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, if vif_port and vif_id in lvm.vif_ports: self.dvr_agent.unbind_port_from_dvr(vif_port, lvm) lvm.vif_ports.pop(vif_id, None) - if not lvm.vif_ports: - self.reclaim_local_vlan(net_uuid) + self.reclaim_local_vlan(net_uuid, lvm.segmentation_id) def port_alive(self, port, log_errors=True): cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag", @@ -1811,21 +1835,23 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, """ port_tags = self.int_br.get_port_tag_dict() changed_ports = set() - for lvm in self.vlan_manager: - for port in lvm.vif_ports.values(): - if ( - port.port_name in port_tags and - port_tags[port.port_name] != lvm.vlan - ): - LOG.info( - "Port '%(port_name)s' has lost " - "its vlan tag '%(vlan_tag)d'! " - "Current vlan tag on this port is '%(new_vlan_tag)s'.", - {'port_name': port.port_name, - 'vlan_tag': lvm.vlan, - 'new_vlan_tag': port_tags[port.port_name]} - ) - changed_ports.add(port.vif_id) + for vlan_mappings in self.vlan_manager: + for lvm in vlan_mappings.values(): + for port in lvm.vif_ports.values(): + if ( + port.port_name in port_tags and + port_tags[port.port_name] != lvm.vlan + ): + LOG.info( + "Port '%(port_name)s' has lost " + "its vlan tag '%(vlan_tag)d'! " + "Current vlan tag on this port is " + "'%(new_vlan_tag)s'.", + {'port_name': port.port_name, + 'vlan_tag': lvm.vlan, + 'new_vlan_tag': port_tags[port.port_name]} + ) + changed_ports.add(port.vif_id) if changed_ports: # explicitly mark these DOWN on the server since they have been # manipulated (likely a nova unplug/replug) and need to be rewired @@ -1906,11 +1932,12 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, ofports = self.tun_br_ofports[tunnel_type].values() if ofports and not self.l2_pop: # Update flooding flows to include the new tunnel - for vlan_mapping in self.vlan_manager: - if vlan_mapping.network_type == tunnel_type: - br.install_flood_to_tun(vlan_mapping.vlan, - vlan_mapping.segmentation_id, - ofports) + for vlan_mappings in self.vlan_manager: + for vlan_mapping in vlan_mappings.values(): + if vlan_mapping.network_type == tunnel_type: + br.install_flood_to_tun(vlan_mapping.vlan, + vlan_mapping.segmentation_id, + ofports) def setup_tunnel_port(self, br, remote_ip, network_type): port_name = self.get_tunnel_name( @@ -1926,19 +1953,21 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type): # Check if this tunnel port is still used - for lvm in self.vlan_manager: - if tun_ofport in lvm.tun_ofports: - break + for vlan_mappings in self.vlan_manager: + for lvm in vlan_mappings.values(): + if tun_ofport in lvm.tun_ofports: + # still used + return # If not, remove it - else: - items = list(self.tun_br_ofports[tunnel_type].items()) - for remote_ip, ofport in items: - if ofport == tun_ofport: - port_name = self.get_tunnel_name( - tunnel_type, self.local_ip, remote_ip) - br.delete_port(port_name) - br.cleanup_tunnel_port(ofport) - self.tun_br_ofports[tunnel_type].pop(remote_ip, None) + items = list(self.tun_br_ofports[tunnel_type].items()) + for remote_ip, ofport in items: + if ofport == tun_ofport: + port_name = self.get_tunnel_name( + tunnel_type, self.local_ip, remote_ip) + br.delete_port(port_name) + br.cleanup_tunnel_port(ofport) + self.tun_br_ofports[tunnel_type].pop( + remote_ip, None) def treat_devices_added_or_updated(self, devices, provisioning_needed, re_added): @@ -1984,7 +2013,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, if 'port_id' in details: details['vif_port'] = port details['local_vlan'] = self._get_net_local_vlan_or_none( - details['network_id']) + details['network_id'], details['segmentation_id']) LOG.info("Port %(device)s updated. Details: %(details)s", {'device': device, 'details': details}) need_binding = self.treat_vif_port(port, details['port_id'], @@ -2233,7 +2262,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, if self.enable_local_ips and port['device_owner'].startswith( n_const.DEVICE_OWNER_COMPUTE_PREFIX): try: - lvm = self.vlan_manager.get(port['network_id']) + lvm = self.vlan_manager.get( + port['network_id'], port['segmentation_id']) self.int_br.setup_local_egress_flows( port['vif_port'].ofport, lvm.vlan) except Exception as err: @@ -2250,7 +2280,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin, port['port_id'], err) def install_accepted_egress_direct_flow(self, port_detail, br_int): - lvm = self.vlan_manager.get(port_detail['network_id']) + lvm = self.vlan_manager.get( + port_detail['network_id'], port_detail['segmentation_id']) port = port_detail['vif_port'] # Adding the local vlan to register 6 in case of MAC overlapping diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py b/neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py index 15317670755..08d2a79ef62 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/vlanmanager.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +from collections import defaultdict + from neutron_lib import exceptions from neutron._i18n import _ @@ -24,11 +26,17 @@ class VifIdNotFound(exceptions.NeutronException): class MappingAlreadyExists(exceptions.NeutronException): - message = _('VLAN mapping for network with id %(net_id)s already exists') + message = _('VLAN mapping for network with id %(net_id)s and ' + 'segmentation id %(seg_id)s already exists') class MappingNotFound(exceptions.NeutronException): - message = _('Mapping for network %(net_id)s not found.') + message = _('Mapping VLAN for network %(net_id)s with segmentation id ' + '%(seg_id)s not found.') + + +class NotUniqMapping(exceptions.NeutronException): + message = _('Mapping VLAN for network %(net_id)s should be unique.') class LocalVLANMapping(object): @@ -71,7 +79,7 @@ class LocalVlanManager(object): def __init__(self): if not hasattr(self, 'mapping'): - self.mapping = {} + self.mapping = defaultdict(dict) def __contains__(self, key): return key in self.mapping @@ -86,31 +94,55 @@ class LocalVlanManager(object): def add(self, net_id, vlan, network_type, physical_network, segmentation_id, vif_ports=None): - if net_id in self.mapping: - raise MappingAlreadyExists(net_id=net_id) - self.mapping[net_id] = LocalVLANMapping( + try: + if self.get(net_id, segmentation_id): + raise MappingAlreadyExists( + net_id=net_id, seg_id=segmentation_id) + except MappingNotFound: + pass + self.mapping[net_id][segmentation_id] = LocalVLANMapping( vlan, network_type, physical_network, segmentation_id, vif_ports) - def get_net_uuid(self, vif_id): - for network_id, vlan_mapping in self.mapping.items(): - if vif_id in vlan_mapping.vif_ports: - return network_id + def get_net_and_segmentation_id(self, vif_id, net_uuid=None): + # TODO(sahid): We should improve algorithm if net_uuid is passed. + for network_id, vlan_mappings in self.mapping.items(): + for segmentation_id, vlan_mapping in vlan_mappings.items(): + if vif_id in vlan_mapping.vif_ports: + return network_id, segmentation_id raise VifIdNotFound(vif_id=vif_id) - def get(self, net_id): - try: - return self.mapping[net_id] - except KeyError: - raise MappingNotFound(net_id=net_id) + def get(self, net_id, segmentation_id): + if net_id in self.mapping and segmentation_id in self.mapping[net_id]: + return self.mapping[net_id][segmentation_id] + raise MappingNotFound(net_id=net_id, seg_id=segmentation_id) - def pop(self, net_id): - try: - return self.mapping.pop(net_id) - except KeyError: - raise MappingNotFound(net_id=net_id) + def get_segments(self, net_id): + if net_id not in self.mapping: + raise MappingNotFound(net_id=net_id, seg_id="") + return self.mapping[net_id] + + def pop(self, net_id, segmentation_id): + if self.get(net_id, segmentation_id): + ret = self.mapping[net_id].pop(segmentation_id) + # if it's the last seg id for a network, let's removed the network + # entry as-well. + if len(self.mapping[net_id]) == 0: + del self.mapping[net_id] + return ret def update_segmentation_id(self, net_id, segmentation_id): - try: - self.mapping[net_id].segmentation_id = segmentation_id - except KeyError: - raise MappingNotFound(net_id=net_id) + """Returns tuple with segmentation id, lvm in success or None, None""" + if len(self.get_segments(net_id)) != 1: + # Update of segmentation id can work only if network has one + # segment. This is a design issue that should be fixed in + # future. We should not accept segmentation update for a network. + raise NotUniqMapping(net_id=net_id) + mapping = list(self.mapping[net_id].values())[0] + if mapping.segmentation_id == segmentation_id: + # No need to update + return None, None + old = mapping.segmentation_id + del self.mapping[net_id][old] + mapping.segmentation_id = segmentation_id + self.mapping[net_id][segmentation_id] = mapping + return old, mapping diff --git a/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py b/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py index 4955f4967d4..3d3ef6ad920 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py +++ b/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/l2population_rpc_base.py @@ -120,7 +120,8 @@ class TestL2populationRpcCallBackTunnelMixinBase(base.BaseTestCase): self.lvms[i].vlan, self.type_gre, self.lvms[i].phys, self.lvms[i].segid, {self.lvms[i].vif: self.lvms[i].port}) setattr(self, 'lvm%d' % i, - self.vlan_manager.get(self.lvms[i].net)) + self.vlan_manager.get( + self.lvms[i].net, self.lvms[i].segid)) self.upd_fdb_entry1_val = { self.lvms[0].net: { diff --git a/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py b/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py index 99437fd0504..fe8aa4d5da0 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py +++ b/neutron/tests/unit/plugins/ml2/drivers/l2pop/rpc_manager/test_l2population_rpc.py @@ -33,7 +33,7 @@ class TestL2populationRpcCallBackTunnelMixin( def test_get_agent_ports_non_existence_key_in_lvm(self): results = {} - self.vlan_manager.pop(self.lvms[1].net) + self.vlan_manager.pop(self.lvms[1].net, self.lvms[1].segid) for lvm, agent_ports in self.fakeagent.get_agent_ports( self.fdb_entries1): results[lvm] = agent_ports diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index 1babe78ff0e..9c2be86d4db 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -69,8 +69,10 @@ TEST_PORT_ID3 = 'port-id-3' TEST_NETWORK_ID1 = 'net-id-1' TEST_NETWORK_ID2 = 'net-id-2' -TEST_SEGMENTATION_ID1 = 'seg-id-1' -TEST_SEGMENTATION_ID2 = 'seg-id-2' +# Currently it's only accepted to have one segmentation per network. In future +# we will accept more. +TEST_SEG_NET1_ID1 = 'net-id-1-seg-id-1' +TEST_SEG_NET2_ID1 = 'net-id-2-seg-id-1' TEST_MTU = 7824 @@ -189,7 +191,7 @@ class TestOvsNeutronAgent(object): int_br.db_get_val.return_value = db_get_val int_br.set_db_attribute.return_value = True needs_binding = self.agent.port_bound( - port, net_uuid, 'local', None, None, + port, net_uuid, 'local', None, 'seg1', fixed_ips, DEVICE_OWNER_COMPUTE, False) if db_get_val is None: int_br.assert_not_called() @@ -198,6 +200,7 @@ class TestOvsNeutronAgent(object): vlan_mapping = {'net_uuid': net_uuid, 'network_type': 'local', 'physical_network': 'None', + 'segmentation_id': 'seg1', 'tag': str(new_local_vlan)} set_vlan.assert_called_once_with(port, new_local_vlan) int_br.set_db_attribute.assert_called_once_with( @@ -700,7 +703,7 @@ class TestOvsNeutronAgent(object): def test_bind_devices(self): devices_up = ['tap1'] devices_down = ['tap2'] - self.agent.vlan_manager.mapping["net1"] = mock.Mock() + self.agent.vlan_manager.mapping["net1"]['seg1'] = mock.Mock() ovs_db_list = [{'name': 'tap1', 'tag': []}, {'name': 'tap2', 'tag': []}] vif_port1 = mock.Mock() @@ -709,10 +712,12 @@ class TestOvsNeutronAgent(object): vif_port2.port_name = 'tap2' port_details = [ {'network_id': 'net1', 'vif_port': vif_port1, + 'segmentation_id': 'seg1', 'device': devices_up[0], 'device_owner': 'network:dhcp', 'admin_state_up': True}, {'network_id': 'net1', 'vif_port': vif_port2, + 'segmentation_id': 'seg1', 'device': devices_down[0], 'device_owner': 'network:dhcp', 'admin_state_up': False}] @@ -737,11 +742,13 @@ class TestOvsNeutronAgent(object): self.agent.vlan_manager.add('fake_network', 1, n_const.TYPE_VXLAN, None, 1) ovs_db_list = [{'name': 'fake_device', 'tag': []}] - self.agent.vlan_manager.get('fake_network').tun_ofports = tun_ofports + self.agent.vlan_manager.get( + 'fake_network', 1).tun_ofports = tun_ofports vif_port = mock.Mock() vif_port.port_name = 'fake_device' vif_port.ofport = 1 need_binding_ports = [{'network_id': 'fake_network', + 'segmentation_id': 1, 'vif_port': vif_port, 'device': 'fake_device', 'admin_state_up': True}] @@ -774,6 +781,7 @@ class TestOvsNeutronAgent(object): vif_port.port_name = 'fake_device' vif_port.ofport = 1 need_binding_ports = [{'network_id': 'fake_network', + 'segmentation_id': 1, 'vif_port': vif_port, 'device': 'fake_device', 'admin_state_up': True}] @@ -1093,6 +1101,7 @@ class TestOvsNeutronAgent(object): vif_port = mock.Mock() vif_port.name.return_value = 'port' self.agent._bind_devices([{'network_id': 'non-existent', + 'segmentation_id': 'seg1', 'vif_port': vif_port}]) def _test_process_network_ports(self, port_info, skipped_devices=None, @@ -1398,7 +1407,7 @@ class TestOvsNeutronAgent(object): def test_process_deleted_ports_cleans_network_ports(self): self.agent._update_port_network( - TEST_PORT_ID1, TEST_NETWORK_ID1, TEST_SEGMENTATION_ID1) + TEST_PORT_ID1, TEST_NETWORK_ID1, TEST_SEG_NET1_ID1) self.agent.port_delete(context=None, port_id=TEST_PORT_ID1) self.agent.sg_agent = mock.Mock() self.agent.int_br = mock.Mock() @@ -1411,7 +1420,7 @@ class TestOvsNeutronAgent(object): self.agent.process_deleted_ports(port_info={}) self.assertEqual( set(), self.agent.network_ports[ - TEST_NETWORK_ID1][TEST_SEGMENTATION_ID1]) + TEST_NETWORK_ID1][TEST_SEG_NET1_ID1]) def test_network_update(self): """Network update marks port for update. """ @@ -1419,7 +1428,7 @@ class TestOvsNeutronAgent(object): port = {'id': TEST_PORT_ID1, 'network_id': network['id']} self.agent._update_port_network( - port['id'], port['network_id'], TEST_SEGMENTATION_ID1) + port['id'], port['network_id'], TEST_SEG_NET1_ID1) with mock.patch.object(self.agent.plugin_rpc, 'get_network_details'), \ mock.patch.object(self.agent, '_update_network_segmentation_id'): @@ -1436,7 +1445,7 @@ class TestOvsNeutronAgent(object): port = {'id': TEST_PORT_ID1, 'network_id': network['id']} self.agent._update_port_network( - port['id'], port['network_id'], TEST_SEGMENTATION_ID1) + port['id'], port['network_id'], TEST_SEG_NET1_ID1) self.agent.port_delete(context=None, port_id=port['id']) with mock.patch.object(self.agent.plugin_rpc, 'get_network_details'), \ mock.patch.object(self.agent, @@ -1447,20 +1456,20 @@ class TestOvsNeutronAgent(object): def test_update_port_network(self): """Ensure ports are associated and moved across networks correctly.""" self.agent._update_port_network( - TEST_PORT_ID1, TEST_NETWORK_ID1, TEST_SEGMENTATION_ID1) + TEST_PORT_ID1, TEST_NETWORK_ID1, TEST_SEG_NET1_ID1) self.agent._update_port_network( - TEST_PORT_ID2, TEST_NETWORK_ID1, TEST_SEGMENTATION_ID2) + TEST_PORT_ID2, TEST_NETWORK_ID1, TEST_SEG_NET1_ID1) self.agent._update_port_network( - TEST_PORT_ID3, TEST_NETWORK_ID2, TEST_SEGMENTATION_ID1) + TEST_PORT_ID3, TEST_NETWORK_ID2, TEST_SEG_NET2_ID1) self.agent._update_port_network( - TEST_PORT_ID1, TEST_NETWORK_ID2, TEST_SEGMENTATION_ID1) + TEST_PORT_ID1, TEST_NETWORK_ID2, TEST_SEG_NET2_ID1) self.assertEqual(set([TEST_PORT_ID2]), self.agent.network_ports[TEST_NETWORK_ID1][ - TEST_SEGMENTATION_ID2]) + TEST_SEG_NET1_ID1]) self.assertEqual(set([TEST_PORT_ID1, TEST_PORT_ID3]), self.agent.network_ports[TEST_NETWORK_ID2][ - TEST_SEGMENTATION_ID1]) + TEST_SEG_NET2_ID1]) def test_port_delete(self): vif = FakeVif() @@ -1802,9 +1811,11 @@ class TestOvsNeutronAgent(object): with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn: self.agent.enable_tunneling = True lvm = mock.Mock() + lvm.network_id = "netuid12345" + lvm.segmentation_id = "seg1" lvm.network_type = "gre" lvm.vif_ports = {"vif1": mock.Mock()} - self.agent.vlan_manager.mapping["netuid12345"] = lvm + self.agent.vlan_manager.mapping["netuid12345"]['seg1'] = lvm self.agent.port_unbound("vif1", "netuid12345") self.assertTrue(reclvl_fn.called) @@ -1827,7 +1838,8 @@ class TestOvsNeutronAgent(object): lvm2.vlan = 'vlan2' lvm2.segmentation_id = 'seg2' lvm2.tun_ofports = set(['1', '2']) - self.agent.vlan_manager.mapping = {'net1': lvm1, 'net2': lvm2} + self.agent.vlan_manager.mapping = {'net1': {"seg1": lvm1}, + 'net2': {"seg2": lvm2}} self.agent.tun_br_ofports = {'gre': {'1.1.1.1': '1', '2.2.2.2': '2'}} self.agent.arp_responder_enabled = True @@ -1870,7 +1882,7 @@ class TestOvsNeutronAgent(object): self._prepare_l2_pop_ofports() fdb_entry = {'net1': {'network_type': 'gre', - 'segment_id': 'tun1', + 'segment_id': 'seg1', 'ports': {'2.2.2.2': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), @@ -1895,7 +1907,7 @@ class TestOvsNeutronAgent(object): self._prepare_l2_pop_ofports() fdb_entry = {'net2': {'network_type': 'gre', - 'segment_id': 'tun2', + 'segment_id': 'seg2', 'ports': {'2.2.2.2': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1), @@ -1915,7 +1927,7 @@ class TestOvsNeutronAgent(object): self._prepare_l2_pop_ofports() fdb_entry = {'net1': {'network_type': 'gre', - 'segment_id': 'tun1', + 'segment_id': 'seg1', 'ports': {'1.1.1.1': [l2pop_rpc.PortInfo(FAKE_MAC, FAKE_IP1)]}}} with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br,\ @@ -1933,7 +1945,7 @@ class TestOvsNeutronAgent(object): self._prepare_l2_pop_ofports() fdb_entry = {'net2': {'network_type': 'gre', - 'segment_id': 'tun2', + 'segment_id': 'seg2', 'ports': {'2.2.2.2': [n_const.FLOODING_ENTRY]}}} with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: self.agent.fdb_remove(None, fdb_entry) @@ -1972,22 +1984,21 @@ class TestOvsNeutronAgent(object): self.agent.l2_pop = True self.agent.enable_tunneling = True with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: - self.agent.reclaim_local_vlan('net1') + self.agent.reclaim_local_vlan('net1', 'seg1') tun_br.cleanup_tunnel_port.assert_not_called() with mock.patch.object(self.mod_agent.LOG, 'debug') as log_debug_fn: - self.agent.reclaim_local_vlan('net999') + self.agent.reclaim_local_vlan('net999', 'seg999') log_debug_fn.assert_called_once_with( - 'Network %s not used on agent.', - 'net999', - ) + 'Network %s, for segmentation %s, not used on agent.', + 'net999', 'seg999') def test_recl_lv_port_to_remove(self): self._prepare_l2_pop_ofports() self.agent.l2_pop = True self.agent.enable_tunneling = True with mock.patch.object(self.agent, 'tun_br', autospec=True) as tun_br: - self.agent.reclaim_local_vlan('net2') + self.agent.reclaim_local_vlan('net2', 'seg2') tun_br.delete_port.assert_called_once_with('gre-02020202') def _test_ext_br_recreated(self, setup_bridges_side_effect): @@ -2671,9 +2682,10 @@ class TestOvsNeutronAgent(object): provider_net.NETWORK_TYPE: n_const.TYPE_VLAN, provider_net.PHYSICAL_NETWORK: 'default_network'} with mock.patch.object(self.agent.vlan_manager, - 'update_segmentation_id') as mock_update_segid: + 'update_segmentation_id', + return_value=(None, None)) as mock_update_segid: self.agent._update_network_segmentation_id(network) - mock_update_segid.assert_not_called() + mock_update_segid.assert_called_once_with('my-net-uuid', 1005) def test__update_network_segmentation_id_segmentation_id_not_updated(self): network = {'id': 'my-net-uuid', @@ -2683,9 +2695,10 @@ class TestOvsNeutronAgent(object): self.agent.vlan_manager.add('my-net-uuid', 5, n_const.TYPE_VLAN, 'provider_net', 1005, None) with mock.patch.object(self.agent.vlan_manager, - 'update_segmentation_id') as mock_update_segid: + 'update_segmentation_id', + return_value=(None, None)) as mock_update_segid: self.agent._update_network_segmentation_id(network) - mock_update_segid.assert_not_called() + mock_update_segid.assert_called_once_with('my-net-uuid', 1005) def test__update_network_segmentation_id_multisegments(self): network = {'id': 'my-net-uuid', @@ -3232,7 +3245,8 @@ class TestOvsDvrNeutronAgent(object): n_const.DEVICE_OWNER_DVR_INTERFACE, False) phy_ofp = self.agent.dvr_agent.phys_ofports[physical_network] int_ofp = self.agent.dvr_agent.int_ofports[physical_network] - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, segmentation_id).vlan expected_on_phys_br = [ mock.call.provision_local_vlan( port=phy_ofp, @@ -3329,7 +3343,8 @@ class TestOvsDvrNeutronAgent(object): self._port, self._net_uuid, network_type, physical_network, segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan expected_on_int_br = self._expected_port_bound( self._port, lvid) if ip_version == n_const.IP_VERSION_4: @@ -3418,7 +3433,8 @@ class TestOvsDvrNeutronAgent(object): def test_port_bound_for_dvr_with_csnat_ports(self): self._setup_for_dvr_test() int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type='vxlan', @@ -3433,7 +3449,7 @@ class TestOvsDvrNeutronAgent(object): mock.call.provision_local_vlan( network_type='vxlan', lvid=lvid, - segmentation_id=None, + segmentation_id=self._segmentation_id, distributed=True, ), ] @@ -3471,7 +3487,8 @@ class TestOvsDvrNeutronAgent(object): # simulate a replug self._port.ofport = 12 int_br, tun_br = self._port_bound_for_dvr_with_csnat_ports() - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan expected_on_int_br = [ mock.call.delete_dvr_to_src_mac( network_type='vxlan', @@ -3517,7 +3534,7 @@ class TestOvsDvrNeutronAgent(object): mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, + None, self._segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) return int_br, tun_br @@ -3537,7 +3554,7 @@ class TestOvsDvrNeutronAgent(object): mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, + None, self._segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) int_br.install_dvr_to_src_mac.assert_not_called() @@ -3603,7 +3620,8 @@ class TestOvsDvrNeutronAgent(object): physical_network, segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan # Bound non-gateway port self.agent.port_bound( @@ -3751,16 +3769,19 @@ class TestOvsDvrNeutronAgent(object): else: self.agent.port_bound( self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, + None, self._segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan int_br.assert_has_calls( self._expected_port_bound(self._port, lvid), any_order=True) expected_on_tun_br = [ - mock.call.provision_local_vlan(network_type='vxlan', - lvid=lvid, segmentation_id=None, distributed=True), + mock.call.provision_local_vlan( + network_type='vxlan', + lvid=lvid, segmentation_id=self._segmentation_id, + distributed=True), ] + self._expected_install_dvr_process( port=self._port, lvid=lvid, @@ -3792,7 +3813,8 @@ class TestOvsDvrNeutronAgent(object): failed_devices = {'added': set(), 'removed': set()} failed_devices['removed'] = self.agent.treat_devices_removed( [self._port.vif_id]) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan if ip_version == n_const.IP_VERSION_4: expected = [ mock.call.delete_dvr_process_ipv4( @@ -3857,16 +3879,17 @@ class TestOvsDvrNeutronAgent(object): mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, + None, self._segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_DVR_INTERFACE, False) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan int_br.assert_has_calls( self._expected_port_bound(self._port, lvid), any_order=True) expected_on_tun_br = [ mock.call.provision_local_vlan( network_type='vxlan', - segmentation_id=None, + segmentation_id=self._segmentation_id, lvid=lvid, distributed=True), ] + self._expected_install_dvr_process( @@ -3879,7 +3902,7 @@ class TestOvsDvrNeutronAgent(object): tun_br.reset_mock() self.agent.port_bound(self._compute_port, self._net_uuid, 'vxlan', - None, None, + None, self._segmentation_id, self._compute_fixed_ips, device_owner, False) int_br.assert_has_calls( @@ -3958,10 +3981,11 @@ class TestOvsDvrNeutronAgent(object): mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br): self.agent.port_bound( self._port, self._net_uuid, 'vxlan', - None, None, self._fixed_ips, + None, self._segmentation_id, self._fixed_ips, n_const.DEVICE_OWNER_ROUTER_SNAT, False) - lvid = self.agent.vlan_manager.get(self._net_uuid).vlan + lvid = self.agent.vlan_manager.get( + self._net_uuid, self._segmentation_id).vlan expected_on_int_br = [ mock.call.install_dvr_to_src_mac( network_type='vxlan', @@ -3976,7 +4000,7 @@ class TestOvsDvrNeutronAgent(object): mock.call.provision_local_vlan( network_type='vxlan', lvid=lvid, - segmentation_id=None, + segmentation_id=self._segmentation_id, distributed=True, ), ] diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py index c8f2d13b183..5ee8357834f 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py @@ -455,7 +455,7 @@ class TunnelTest(object): a = self._build_agent() a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_DATA) - a.reclaim_local_vlan(NET_UUID) + a.reclaim_local_vlan(NET_UUID, LS_ID) self.assertIn(self.LVM_DATA[0], a.available_local_vlans) self._verify_mock_calls() @@ -475,7 +475,7 @@ class TunnelTest(object): a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_FLAT_DATA) - a.reclaim_local_vlan(NET_UUID) + a.reclaim_local_vlan(NET_UUID, LS_ID) self.assertIn(self.LVM_FLAT_DATA[0], a.available_local_vlans) self._verify_mock_calls() @@ -495,7 +495,7 @@ class TunnelTest(object): a.available_local_vlans = set() a.vlan_manager.add(NET_UUID, *self.LVM_VLAN_DATA) - a.reclaim_local_vlan(NET_UUID) + a.reclaim_local_vlan(NET_UUID, LS_ID) self.assertIn(self.LVM_VLAN_DATA[0], a.available_local_vlans) self._verify_mock_calls() @@ -528,7 +528,7 @@ class TunnelTest(object): a.vlan_manager.add(NET_UUID, *self.LVM_DATA) a.port_unbound(VIF_ID, NET_UUID) - reclaim_local_vlan.assert_called_once_with(NET_UUID) + reclaim_local_vlan.assert_called_once_with(NET_UUID, LS_ID) self._verify_mock_calls() def test_port_dead(self): diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py index a4e46b3c3bd..6496cf9ff6b 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_vlanmanager.py @@ -72,29 +72,30 @@ class TestLocalVlanManager(base.BaseTestCase): created_vlans = [] for val in range(3): self.vlan_manager.add(val, val, val, val, val) - created_vlans.append(self.vlan_manager.get(val)) + created_vlans.append({val: self.vlan_manager.get(val, val)}) self.assertCountEqual(created_vlans, list(self.vlan_manager)) - def test_get_net_uuid_existing(self): + def test_get_net_and_segmentation_id_existing(self): port_id = 'port-id' vlan_data = (2, 3, 4, 5, {port_id: 'port'}) net_id = 1 self.vlan_manager.add(net_id, *vlan_data) - obtained_net_id = self.vlan_manager.get_net_uuid(port_id) - self.assertEqual(net_id, obtained_net_id) + obtained_net_id = ( + self.vlan_manager.get_net_and_segmentation_id(port_id)) + self.assertEqual((net_id, 5), obtained_net_id) - def test_get_net_uuid_non_existing_raises_exception(self): + def test_get_net_and_segmentation_id_non_existing_raises_exception(self): vlan_data = (1, 2, 3, 4, 5, {'port_id': 'port'}) self.vlan_manager.add(*vlan_data) with testtools.ExpectedException(vlanmanager.VifIdNotFound): - self.vlan_manager.get_net_uuid('non-existing-port') + self.vlan_manager.get_net_and_segmentation_id('non-existing-port') def test_add_and_get(self): vlan_data = (2, 3, 4, 5, 6) expected_vlan_mapping = vlanmanager.LocalVLANMapping(*vlan_data) self.vlan_manager.add(1, *vlan_data) - vlan_mapping = self.vlan_manager.get(1) + vlan_mapping = self.vlan_manager.get(1, 5) self.assertEqual(expected_vlan_mapping, vlan_mapping) def test_add_existing_raises_exception(self): @@ -105,23 +106,39 @@ class TestLocalVlanManager(base.BaseTestCase): def test_get_non_existing_raises_keyerror(self): with testtools.ExpectedException(vlanmanager.MappingNotFound): - self.vlan_manager.get(1) + self.vlan_manager.get(1, 5) def test_pop(self): vlan_data = (2, 3, 4, 5, 6) expected_vlan_mapping = vlanmanager.LocalVLANMapping(*vlan_data) self.vlan_manager.add(1, *vlan_data) - vlan_mapping = self.vlan_manager.pop(1) + vlan_mapping = self.vlan_manager.pop(1, 5) self.assertEqual(expected_vlan_mapping, vlan_mapping) self.assertFalse(self.vlan_manager.mapping) def test_pop_non_existing_raises_exception(self): with testtools.ExpectedException(vlanmanager.MappingNotFound): - self.vlan_manager.pop(1) + self.vlan_manager.pop(1, 5) def test_update_segmentation_id(self): self.vlan_manager.add('net_id', 'vlan_id', 'vlan', 'phys_net', 1001, None) - self.assertEqual(1001, self.vlan_manager.get('net_id').segmentation_id) + self.assertEqual(1001, self.vlan_manager.get( + 'net_id', 1001).segmentation_id) self.vlan_manager.update_segmentation_id('net_id', 1002) - self.assertEqual(1002, self.vlan_manager.get('net_id').segmentation_id) + self.assertEqual(1002, self.vlan_manager.get( + 'net_id', 1002).segmentation_id) + + def test_update_segmentation_id_not_found(self): + with testtools.ExpectedException(vlanmanager.MappingNotFound): + self.vlan_manager.update_segmentation_id( + 'net_id-notfound', 1002) + + def test_update_segmentation_id_not_uniq(self): + self.vlan_manager.add('net_id-not-uniq', 'vlan_id', 'vlan', 'phys_net', + 1001, None) + self.vlan_manager.add('net_id-not-uniq', 'vlan_id', 'vlan', 'phys_net', + 1002, None) + with testtools.ExpectedException(vlanmanager.NotUniqMapping): + self.vlan_manager.update_segmentation_id( + 'net_id-not-uniq', 1003)