/* * Copyright 2016-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.ui.model.topo; import org.onlab.util.Identifier; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.ElementId; import org.onosproject.net.HostId; import org.onosproject.net.Link; import org.onosproject.net.LinkKey; import org.onosproject.net.PortNumber; import org.onosproject.net.region.RegionId; import java.util.Comparator; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** * A canonical representation of an identifier for {@link UiLink}s. */ public final class UiLinkId { private static final String E_PORT_NULL = "Port number cannot be null"; private static final String E_DEVICE_ID_NULL = "Device ID cannot be null"; private static final String E_HOST_ID_NULL = "Host ID cannot be null"; private static final String E_REGION_ID_NULL = "Region ID cannot be null"; private static final String E_IDENTICAL = "Region IDs cannot be same"; private static final Comparator<RegionId> REGION_ID_COMPARATOR = Comparator.comparing(Identifier::toString); private static final Comparator<DeviceId> DEVICE_ID_COMPARATOR = Comparator.comparing(DeviceId::toString); /** * Designates the directionality of an underlying (uni-directional) link. */ public enum Direction { A_TO_B, B_TO_A } /** * Designates the type of link the identifier represents. */ public enum Type { REGION_REGION, REGION_DEVICE, DEVICE_DEVICE, HOST_DEVICE } static final String CP_DELIMITER = "~"; static final String ID_PORT_DELIMITER = "/"; private final RegionId regionA; private final ElementId elementA; private final PortNumber portA; private final RegionId regionB; private final ElementId elementB; private final PortNumber portB; private final String idStr; private final String idA; private final String idB; private final Type type; /** * Creates a UI link identifier. It is expected that A comes before B when * the two identifiers are naturally sorted, thus providing a representation * which is invariant to whether A or B is source or destination of the * underlying link. * * @param a first element ID * @param pa first element port * @param b second element ID * @param pb second element port */ private UiLinkId(ElementId a, PortNumber pa, ElementId b, PortNumber pb) { elementA = a; portA = pa; elementB = b; portB = pb; regionA = null; regionB = null; boolean isEdgeLink = (a instanceof HostId); // NOTE: for edgelinks, hosts are always element A idA = isEdgeLink ? a.toString() : a + ID_PORT_DELIMITER + pa; idB = b + ID_PORT_DELIMITER + pb; idStr = idA + CP_DELIMITER + idB; type = isEdgeLink ? Type.HOST_DEVICE : Type.DEVICE_DEVICE; } /** * Creates a UI link identifier. It is expected that A comes before B when * the two identifiers are naturally sorted. * * @param a first region ID * @param b second region ID */ private UiLinkId(RegionId a, RegionId b) { regionA = a; regionB = b; elementA = null; elementB = null; portA = null; portB = null; idA = a.toString(); idB = b.toString(); idStr = idA + CP_DELIMITER + idB; type = Type.REGION_REGION; } /** * Creates a UI link identifier, with region at one end and a device/port * at the other. * * @param r region ID * @param d device ID * @param p port number */ private UiLinkId(RegionId r, DeviceId d, PortNumber p) { regionA = r; elementB = d; portB = p; regionB = null; elementA = null; portA = null; idA = r.toString(); idB = elementB + ID_PORT_DELIMITER + portB; idStr = idA + CP_DELIMITER + idB; type = Type.REGION_DEVICE; } @Override public String toString() { return idStr; } /** * String representation of endpoint A. * * @return string rep of endpoint A */ public String idA() { return idA; } /** * String representation of endpoint B. * * @return string rep of endpoint B */ public String idB() { return idB; } /** * Returns the identifier of the first element. Note that the returned * value will be null if this identifier is for a region-region link. * * @return first element identity */ public ElementId elementA() { return elementA; } /** * Returns the port of the first element. Note that the returned * value will be null if this identifier is for a region-region link. * * @return first element port */ public PortNumber portA() { return portA; } /** * Returns the identifier of the second element. Note that the returned * value will be null if this identifier is for a region-region link. * * @return second element identity */ public ElementId elementB() { return elementB; } /** * Returns the port of the second element. Note that the returned * value will be null if this identifier is for a region-region link. * * @return second element port */ public PortNumber portB() { return portB; } /** * Returns the identity of the first region. Note that the returned value * will be null if this identifier is for a device-device or device-host * link. * * @return first region ID */ public RegionId regionA() { return regionA; } /** * Returns the identity of the second region. Note that the returned value * will be null if this identifier is for a device-device or device-host * link. * * @return second region ID */ public RegionId regionB() { return regionB; } /** * Returns the type of link this identifier represents. * * @return the link identifier type */ public Type type() { return type; } /** * Returns true if this identifier represents a region-region link. * * @return true if region-region link identifier; false otherwise */ public boolean isRegionRegion() { return type == Type.REGION_REGION; } /** * Returns true if this identifier represents a region-device link. * * @return true if region-device link identifier; false otherwise */ public boolean isRegionDevice() { return type == Type.REGION_DEVICE; } /** * Returns true if this identifier represents a device-device * (infrastructure) link. * * @return true if device-device link identifier; false otherwise */ public boolean isDeviceDevice() { return type == Type.DEVICE_DEVICE; } /** * Returns true if this identifier represents a host-device (edge) link. * * @return true if host-device link identifier; false otherwise */ public boolean isHostDevice() { return type == Type.HOST_DEVICE; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } UiLinkId uiLinkId = (UiLinkId) o; return idStr.equals(uiLinkId.idStr); } @Override public int hashCode() { return idStr.hashCode(); } /** * Returns the direction of the given link, or null if this link ID does * not correspond to the given link. * * @param link the link to examine * @return corresponding direction */ Direction directionOf(Link link) { ConnectPoint src = link.src(); ElementId srcId = src.elementId(); return elementA.equals(srcId) ? Direction.A_TO_B : elementB.equals(srcId) ? Direction.B_TO_A : null; } /** * Generates the canonical link identifier for the given link. * * @param link link for which the identifier is required * @return link identifier * @throws NullPointerException if src or dst connect point is null */ public static UiLinkId uiLinkId(Link link) { return canonicalizeIdentifier(link.src(), link.dst()); } /** * Creates the canonical link identifier from the given link key. * * @param lk link key * @return equivalent link identifier * @throws NullPointerException if src or dst connect point is null */ public static UiLinkId uiLinkId(LinkKey lk) { return canonicalizeIdentifier(lk.src(), lk.dst()); } private static UiLinkId canonicalizeIdentifier(ConnectPoint src, ConnectPoint dst) { if (src == null || dst == null) { throw new NullPointerException( "null src or dst connect point (illegal for UiLinkId)"); } ElementId srcId = src.elementId(); ElementId dstId = dst.elementId(); // canonicalize int comp = srcId.toString().compareTo(dstId.toString()); return comp <= 0 ? new UiLinkId(srcId, src.port(), dstId, dst.port()) : new UiLinkId(dstId, dst.port(), srcId, src.port()); } /** * Generates the canonical link identifier for a link between the * specified region nodes. * * @param one the first region ID * @param two the second region ID * @return link identifier * @throws NullPointerException if any of the required fields are null * @throws IllegalArgumentException if the identifiers are identical */ public static UiLinkId uiLinkId(RegionId one, RegionId two) { checkNotNull(one, E_REGION_ID_NULL); checkNotNull(two, E_REGION_ID_NULL); checkArgument(!one.equals(two), E_IDENTICAL); boolean flip = REGION_ID_COMPARATOR.compare(one, two) > 0; return flip ? new UiLinkId(two, one) : new UiLinkId(one, two); } /** * Generates the canonical link identifier for a link between the specified * region and device/port. * * @param regionId region ID * @param deviceId device ID * @param portNumber port number * @return link identifier * @throws NullPointerException if any of the required fields are null */ public static UiLinkId uiLinkId(RegionId regionId, DeviceId deviceId, PortNumber portNumber) { checkNotNull(regionId, E_REGION_ID_NULL); checkNotNull(deviceId, E_DEVICE_ID_NULL); checkNotNull(portNumber, E_PORT_NULL); return new UiLinkId(regionId, deviceId, portNumber); } /** * Generates an identifier for a link between two devices. * * @param a device A * @param pa port A * @param b device B * @param pb port B * @return link identifier * @throws NullPointerException if any of the required fields are null */ public static UiLinkId uiLinkId(DeviceId a, PortNumber pa, DeviceId b, PortNumber pb) { checkNotNull(a, E_DEVICE_ID_NULL + " (A)"); checkNotNull(b, E_DEVICE_ID_NULL + " (B)"); checkNotNull(pa, E_PORT_NULL + " (A)"); checkNotNull(pb, E_PORT_NULL + " (B)"); boolean flip = DEVICE_ID_COMPARATOR.compare(a, b) > 0; return flip ? new UiLinkId(b, pb, a, pa) : new UiLinkId(a, pa, b, pb); } /** * Generates an identifier for an edge link. Note that host is always * element A. * * @param h host * @param d device * @param p port * @return link identifier * @throws NullPointerException if any of the required fields are null */ public static UiLinkId uiLinkId(HostId h, DeviceId d, PortNumber p) { checkNotNull(h, E_HOST_ID_NULL); checkNotNull(d, E_DEVICE_ID_NULL); checkNotNull(p, E_PORT_NULL); return new UiLinkId(h, PortNumber.P0, d, p); } }