/* * Copyright 2015-present Open Networking Laboratory * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.onosproject.segmentrouting; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; import org.onosproject.net.link.LinkService; import org.onosproject.segmentrouting.config.DeviceConfiguration; import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; import org.onosproject.segmentrouting.grouphandler.NeighborSet; import org.onosproject.store.service.EventuallyConsistentMap; import org.slf4j.Logger; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import static org.slf4j.LoggerFactory.getLogger; /** * Tunnel Handler. */ public class TunnelHandler { protected final Logger log = getLogger(getClass()); private final DeviceConfiguration config; private final EventuallyConsistentMap<String, Tunnel> tunnelStore; private Map<DeviceId, DefaultGroupHandler> groupHandlerMap; private LinkService linkService; /** * Result of tunnel creation or removal. */ public enum Result { /** * Success. */ SUCCESS, /** * More than one router needs to specified to created a tunnel. */ WRONG_PATH, /** * The same tunnel exists already. */ TUNNEL_EXISTS, /** * The same tunnel ID exists already. */ ID_EXISTS, /** * Tunnel not found. */ TUNNEL_NOT_FOUND, /** * Cannot remove the tunnel used by a policy. */ TUNNEL_IN_USE, /** * Failed to create/remove groups for the tunnel. */ INTERNAL_ERROR } /** * Constructs tunnel handler. * * @param linkService link service * @param deviceConfiguration device configuration * @param groupHandlerMap group handler map * @param tunnelStore tunnel store */ public TunnelHandler(LinkService linkService, DeviceConfiguration deviceConfiguration, Map<DeviceId, DefaultGroupHandler> groupHandlerMap, EventuallyConsistentMap<String, Tunnel> tunnelStore) { this.linkService = linkService; this.config = deviceConfiguration; this.groupHandlerMap = groupHandlerMap; this.tunnelStore = tunnelStore; } /** * Creates a tunnel. * * @param tunnel tunnel reference to create a tunnel * @return WRONG_PATH if the tunnel path is wrong, ID_EXISTS if the tunnel ID * exists already, TUNNEL_EXISTS if the same tunnel exists, INTERNAL_ERROR * if the tunnel creation failed internally, SUCCESS if the tunnel is created * successfully */ public Result createTunnel(Tunnel tunnel) { if (tunnel.labelIds().isEmpty() || tunnel.labelIds().size() < 3) { log.error("More than one router needs to specified to created a tunnel"); return Result.WRONG_PATH; } if (tunnelStore.containsKey(tunnel.id())) { log.warn("The same tunnel ID exists already"); return Result.ID_EXISTS; } if (tunnelStore.containsValue(tunnel)) { log.warn("The same tunnel exists already"); return Result.TUNNEL_EXISTS; } int groupId = createGroupsForTunnel(tunnel); if (groupId < 0) { log.error("Failed to create groups for the tunnel"); return Result.INTERNAL_ERROR; } tunnel.setGroupId(groupId); tunnelStore.put(tunnel.id(), tunnel); return Result.SUCCESS; } /** * Removes the tunnel with the tunnel ID given. * * @param tunnelInfo tunnel information to delete tunnels * @return TUNNEL_NOT_FOUND if the tunnel to remove does not exists, * INTERNAL_ERROR if the tunnel creation failed internally, SUCCESS * if the tunnel is created successfully. */ public Result removeTunnel(Tunnel tunnelInfo) { Tunnel tunnel = tunnelStore.get(tunnelInfo.id()); if (tunnel != null) { DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0)); if (tunnel.isAllowedToRemoveGroup()) { if (groupHandlerMap.get(deviceId).removeGroup(tunnel.groupId())) { tunnelStore.remove(tunnel.id()); } else { log.error("Failed to remove the tunnel {}", tunnelInfo.id()); return Result.INTERNAL_ERROR; } } else { log.debug("The group is not removed because it is being used."); tunnelStore.remove(tunnel.id()); } } else { log.error("No tunnel found for tunnel ID {}", tunnelInfo.id()); return Result.TUNNEL_NOT_FOUND; } return Result.SUCCESS; } /** * Returns the tunnel with the tunnel ID given. * * @param tid Tunnel ID * @return Tunnel reference */ public Tunnel getTunnel(String tid) { return tunnelStore.get(tid); } /** * Returns all tunnels. * * @return list of Tunnels */ public List<Tunnel> getTunnels() { List<Tunnel> tunnels = new ArrayList<>(); tunnelStore.values().forEach(tunnel -> tunnels.add( new DefaultTunnel((DefaultTunnel) tunnel))); return tunnels; } private int createGroupsForTunnel(Tunnel tunnel) { Set<Integer> portNumbers; final int groupError = -1; DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0)); if (deviceId == null) { log.warn("No device found for SID {}", tunnel.labelIds().get(0)); return groupError; } else if (groupHandlerMap.get(deviceId) == null) { log.warn("group handler not found for {}", deviceId); return groupError; } Set<DeviceId> deviceIds = new HashSet<>(); int sid = tunnel.labelIds().get(1); if (config.isAdjacencySid(deviceId, sid)) { portNumbers = config.getPortsForAdjacencySid(deviceId, sid); for (Link link: linkService.getDeviceEgressLinks(deviceId)) { for (Integer port: portNumbers) { if (link.src().port().toLong() == port) { deviceIds.add(link.dst().deviceId()); } } } } else { deviceIds.add(config.getDeviceId(sid)); } // For these NeighborSet isMpls is meaningless. NeighborSet ns = new NeighborSet(deviceIds, false, tunnel.labelIds().get(2)); // If the tunnel reuses any existing groups, then tunnel handler // should not remove the group. if (groupHandlerMap.get(deviceId).hasNextObjectiveId(ns)) { tunnel.allowToRemoveGroup(false); } else { tunnel.allowToRemoveGroup(true); } return groupHandlerMap.get(deviceId).getNextObjectiveId(ns, null, true); } }