| /* |
| * Copyright 2014-present Open Networking Foundation |
| * |
| * 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.net.intent; |
| |
| import com.google.common.base.MoreObjects; |
| import com.google.common.collect.Sets; |
| import org.onlab.graph.ScalarWeight; |
| import org.onlab.graph.Weight; |
| import org.onosproject.core.GroupId; |
| import org.onosproject.net.ConnectPoint; |
| import org.onosproject.net.DefaultLink; |
| import org.onosproject.net.DefaultPath; |
| import org.onosproject.net.DeviceId; |
| import org.onosproject.net.ElementId; |
| import org.onosproject.net.HostId; |
| import org.onosproject.net.Link; |
| import org.onosproject.net.NetTestTools; |
| import org.onosproject.net.NetworkResource; |
| import org.onosproject.net.Path; |
| import org.onosproject.net.device.DeviceServiceAdapter; |
| import org.onosproject.net.flow.FlowId; |
| import org.onosproject.net.flow.FlowRule; |
| import org.onosproject.net.flow.IndexTableId; |
| import org.onosproject.net.flow.TableId; |
| import org.onosproject.net.flow.TrafficSelector; |
| import org.onosproject.net.flow.TrafficTreatment; |
| import org.onosproject.net.flow.criteria.Criterion; |
| import org.onosproject.net.flow.criteria.Criterion.Type; |
| import org.onosproject.net.flow.instructions.Instruction; |
| import org.onosproject.net.flow.instructions.Instructions; |
| import org.onosproject.net.flow.instructions.Instructions.MetadataInstruction; |
| import org.onosproject.net.link.LinkServiceAdapter; |
| import org.onosproject.net.provider.ProviderId; |
| import org.onosproject.net.topology.DefaultTopologyEdge; |
| import org.onosproject.net.topology.DefaultTopologyVertex; |
| import org.onosproject.net.topology.LinkWeigher; |
| import org.onosproject.net.topology.PathServiceAdapter; |
| import org.onosproject.net.topology.TopologyVertex; |
| import org.onosproject.store.Timestamp; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicLong; |
| import java.util.stream.Collectors; |
| import java.util.stream.IntStream; |
| |
| import static org.onosproject.net.Link.Type.DIRECT; |
| import static org.onosproject.net.NetTestTools.*; |
| |
| /** |
| * Common mocks used by the intent framework tests. |
| */ |
| public class IntentTestsMocks { |
| /** |
| * Mock traffic selector class used for satisfying API requirements. |
| */ |
| public static class MockSelector implements TrafficSelector { |
| @Override |
| public Set<Criterion> criteria() { |
| return new HashSet<>(); |
| } |
| |
| @Override |
| public Criterion getCriterion(Type type) { |
| return null; |
| } |
| } |
| |
| /** |
| * Mock traffic treatment class used for satisfying API requirements. |
| */ |
| public static class MockTreatment implements TrafficTreatment { |
| @Override |
| public List<Instruction> deferred() { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public List<Instruction> immediate() { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public List<Instruction> allInstructions() { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public Instructions.TableTypeTransition tableTransition() { |
| return null; |
| } |
| |
| @Override |
| public boolean clearedDeferred() { |
| return false; |
| } |
| |
| @Override |
| public MetadataInstruction writeMetadata() { |
| return null; |
| } |
| |
| @Override |
| public Instructions.StatTriggerInstruction statTrigger() { |
| return null; |
| } |
| |
| @Override |
| public Instructions.MeterInstruction metered() { |
| return null; |
| } |
| |
| @Override |
| public Set<Instructions.MeterInstruction> meters() { |
| return Sets.newHashSet(); |
| } |
| } |
| |
| /** |
| * Mock path service for creating paths within the test. |
| */ |
| public static class MockPathService extends PathServiceAdapter { |
| |
| final String[] pathHops; |
| final String[] reversePathHops; |
| |
| /** |
| * Constructor that provides a set of hops to mock. |
| * |
| * @param pathHops path hops to mock |
| */ |
| public MockPathService(String[] pathHops) { |
| this.pathHops = pathHops; |
| String[] reversed = pathHops.clone(); |
| Collections.reverse(Arrays.asList(reversed)); |
| reversePathHops = reversed; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst) { |
| Set<Path> result = new HashSet<>(); |
| |
| String[] allHops = new String[pathHops.length]; |
| |
| if (src.toString().endsWith(pathHops[0])) { |
| System.arraycopy(pathHops, 0, allHops, 0, pathHops.length); |
| } else { |
| System.arraycopy(reversePathHops, 0, allHops, 0, pathHops.length); |
| } |
| |
| result.add(createPath(src instanceof HostId, dst instanceof HostId, allHops)); |
| return result; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) { |
| Set<Path> paths = getPaths(src, dst); |
| |
| for (Path path : paths) { |
| DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null; |
| DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null; |
| if (srcDevice != null && dstDevice != null) { |
| TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice); |
| TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice); |
| Link link = link(src.toString(), 1, dst.toString(), 1); |
| |
| Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link)); |
| if (weightValue.isNegative()) { |
| return new HashSet<>(); |
| } |
| } |
| } |
| return paths; |
| } |
| } |
| |
| /** |
| * Mock path service for creating paths within the test with multiple possible paths. |
| */ |
| public static class MockMultiplePathService extends PathServiceAdapter { |
| |
| final String[][] pathsHops; |
| |
| /** |
| * Constructor that provides a set of hops to mock. |
| * |
| * @param pathHops multiple path hops to mock |
| */ |
| public MockMultiplePathService(String[][] pathHops) { |
| this.pathsHops = pathHops; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst) { |
| |
| //Extracts all the paths that goes from src to dst |
| Set<Path> allPaths = new HashSet<>(); |
| allPaths.addAll(IntStream.range(0, pathsHops.length) |
| .filter(i -> src.toString().endsWith(pathsHops[i][0]) |
| && dst.toString().endsWith(pathsHops[i][pathsHops[i].length - 1])) |
| .mapToObj(i -> createPath(src instanceof HostId, |
| dst instanceof HostId, |
| pathsHops[i])) |
| .collect(Collectors.toSet())); |
| |
| // Maintain only the shortest paths |
| int minPathLength = allPaths.stream() |
| .mapToInt(o -> o.links().size()) |
| .min() |
| .orElse(Integer.MAX_VALUE); |
| Set<Path> shortestPaths = allPaths.stream() |
| .filter(path -> path.links().size() <= minPathLength) |
| .collect(Collectors.toSet()); |
| |
| return shortestPaths; |
| } |
| |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) { |
| Set<Path> paths = getPaths(src, dst); |
| |
| for (Path path : paths) { |
| DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null; |
| DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null; |
| if (srcDevice != null && dstDevice != null) { |
| TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice); |
| TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice); |
| Link link = link(src.toString(), 1, dst.toString(), 1); |
| |
| Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link)); |
| if (weightValue.isNegative()) { |
| return new HashSet<>(); |
| } |
| } |
| } |
| return paths; |
| } |
| } |
| |
| |
| /** |
| * Mock path service for creating paths within the test. |
| * |
| */ |
| public static class Mp2MpMockPathService extends PathServiceAdapter { |
| |
| final String[] pathHops; |
| final String[] reversePathHops; |
| |
| /** |
| * Constructor that provides a set of hops to mock. |
| * |
| * @param pathHops path hops to mock |
| */ |
| public Mp2MpMockPathService(String[] pathHops) { |
| this.pathHops = pathHops; |
| String[] reversed = pathHops.clone(); |
| Collections.reverse(Arrays.asList(reversed)); |
| reversePathHops = reversed; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst) { |
| Set<Path> result = new HashSet<>(); |
| |
| String[] allHops = new String[pathHops.length + 2]; |
| allHops[0] = src.toString(); |
| allHops[allHops.length - 1] = dst.toString(); |
| |
| if (pathHops.length != 0) { |
| System.arraycopy(pathHops, 0, allHops, 1, pathHops.length); |
| } |
| |
| result.add(createPath(allHops)); |
| |
| return result; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) { |
| final Set<Path> paths = getPaths(src, dst); |
| |
| for (Path path : paths) { |
| final DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null; |
| final DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null; |
| if (srcDevice != null && dstDevice != null) { |
| final TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice); |
| final TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice); |
| final Link link = link(src.toString(), 1, dst.toString(), 1); |
| |
| final Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link)); |
| if (weightValue.isNegative()) { |
| return new HashSet<>(); |
| } |
| } |
| } |
| return paths; |
| } |
| } |
| |
| /** |
| * Mock active and direct link. |
| */ |
| public static class FakeLink extends DefaultLink { |
| |
| /** |
| * Constructor that provides source and destination of the fake link. |
| * |
| * @param src Source connect point of the fake link |
| * @param dst Destination connect point of the fake link |
| */ |
| public FakeLink(ConnectPoint src, ConnectPoint dst) { |
| super(null, src, dst, DIRECT, Link.State.ACTIVE); |
| } |
| } |
| |
| /** |
| * Mock path service for creating paths for MP2SP intent tests, returning |
| * pre-determined paths. |
| */ |
| public static class FixedMP2MPMockPathService extends PathServiceAdapter { |
| |
| final String[] pathHops; |
| |
| public static final String DPID_1 = "of:s1"; |
| public static final String DPID_2 = "of:s2"; |
| public static final String DPID_3 = "of:s3"; |
| public static final String DPID_4 = "of:s4"; |
| |
| /** |
| * Constructor that provides a set of hops to mock. |
| * |
| * @param pathHops path hops to mock |
| */ |
| public FixedMP2MPMockPathService(String[] pathHops) { |
| this.pathHops = pathHops; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst) { |
| List<Link> links = new ArrayList<>(); |
| Set<Path> result = new HashSet<>(); |
| ProviderId providerId = new ProviderId("of", "foo"); |
| DefaultPath path; |
| if (src.toString().equals(DPID_1) && dst.toString().equals(DPID_4)) { |
| links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1)); |
| links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1)); |
| } else if (src.toString().equals(DPID_2) && dst.toString().equals(DPID_4)) { |
| links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 3)); |
| links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1)); |
| } else if (src.toString().equals(DPID_4) && dst.toString().equals(DPID_1)) { |
| links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1)); |
| links.add(NetTestTools.linkNoPrefixes(pathHops[0], 2, dst.toString(), 1)); |
| } else if (src.toString().equals(DPID_4) && dst.toString().equals(DPID_2)) { |
| links.add(NetTestTools.linkNoPrefixes(src.toString(), 2, pathHops[0], 1)); |
| links.add(NetTestTools.linkNoPrefixes(pathHops[0], 3, dst.toString(), 1)); |
| } else { |
| return result; |
| } |
| path = new DefaultPath(providerId, links, ScalarWeight.toWeight(3)); |
| result.add(path); |
| |
| return result; |
| } |
| |
| @Override |
| public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeigher weigher) { |
| final Set<Path> paths = getPaths(src, dst); |
| |
| for (Path path : paths) { |
| final DeviceId srcDevice = path.src().elementId() instanceof DeviceId ? path.src().deviceId() : null; |
| final DeviceId dstDevice = path.dst().elementId() instanceof DeviceId ? path.dst().deviceId() : null; |
| if (srcDevice != null && dstDevice != null) { |
| final TopologyVertex srcVertex = new DefaultTopologyVertex(srcDevice); |
| final TopologyVertex dstVertex = new DefaultTopologyVertex(dstDevice); |
| final Link link = link(src.toString(), 1, dst.toString(), 1); |
| |
| final Weight weightValue = weigher.weight(new DefaultTopologyEdge(srcVertex, dstVertex, link)); |
| if (weightValue.isNegative()) { |
| return new HashSet<>(); |
| } |
| } |
| } |
| return paths; |
| } |
| } |
| |
| /** |
| * Mock link service for getting links to check path availability |
| * when a suggested path is submitted. |
| */ |
| public static class MockLinkService extends LinkServiceAdapter { |
| final String[][] linksHops; |
| |
| /** |
| * Constructor that provides a set of links (as a list of hops). |
| * |
| * @param linksHops links to to mock (link as a set of hops) |
| */ |
| public MockLinkService(String[][] linksHops) { |
| this.linksHops = linksHops; |
| } |
| |
| @Override |
| public Set<Link> getLinks() { |
| return Arrays.asList(linksHops).stream() |
| .map(path -> createPath(path).links()) |
| .flatMap(List::stream) |
| .collect(Collectors.toSet()); |
| } |
| @Override |
| public Set<Link> getLinks(ConnectPoint connectPoint) { |
| return getLinks().stream() |
| .filter(link -> link.src().deviceId().equals(connectPoint.deviceId()) |
| || link.dst().deviceId().equals(connectPoint.deviceId())) |
| .collect(Collectors.toSet()); |
| } |
| } |
| |
| private static final IntentTestsMocks.MockSelector SELECTOR = |
| new IntentTestsMocks.MockSelector(); |
| private static final IntentTestsMocks.MockTreatment TREATMENT = |
| new IntentTestsMocks.MockTreatment(); |
| |
| public static class MockFlowRule implements FlowRule { |
| static int nextId = 0; |
| |
| int priority; |
| IndexTableId tableId; |
| long timestamp; |
| int id; |
| |
| public MockFlowRule(int priority) { |
| this.priority = priority; |
| this.tableId = DEFAULT_TABLE; |
| this.timestamp = System.currentTimeMillis(); |
| this.id = nextId++; |
| } |
| |
| @Override |
| public FlowId id() { |
| return FlowId.valueOf(id); |
| } |
| |
| @Override |
| public short appId() { |
| return 0; |
| } |
| |
| @Override |
| public GroupId groupId() { |
| return new GroupId(0); |
| } |
| |
| @Override |
| public int priority() { |
| return priority; |
| } |
| |
| @Override |
| public DeviceId deviceId() { |
| return did("1"); |
| } |
| |
| @Override |
| public TrafficSelector selector() { |
| return SELECTOR; |
| } |
| |
| @Override |
| public TrafficTreatment treatment() { |
| return TREATMENT; |
| } |
| |
| @Override |
| public int timeout() { |
| return 0; |
| } |
| |
| @Override |
| public int hardTimeout() { |
| return 0; |
| } |
| |
| @Override |
| public FlowRemoveReason reason() { |
| return FlowRemoveReason.NO_REASON; |
| } |
| |
| @Override |
| public boolean isPermanent() { |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| return priority; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || getClass() != obj.getClass()) { |
| return false; |
| } |
| final MockFlowRule other = (MockFlowRule) obj; |
| return Objects.equals(this.timestamp, other.timestamp) && |
| this.id == other.id; |
| } |
| |
| @Override |
| public boolean exactMatch(FlowRule rule) { |
| return this.equals(rule); |
| } |
| |
| @Override |
| public int tableId() { |
| return tableId.id(); |
| } |
| |
| @Override |
| public TableId table() { |
| return tableId; |
| } |
| } |
| |
| public static class MockIntent extends Intent { |
| private static AtomicLong counter = new AtomicLong(0); |
| |
| private final Long number; |
| |
| public MockIntent(Long number) { |
| super(NetTestTools.APP_ID, null, Collections.emptyList(), |
| Intent.DEFAULT_INTENT_PRIORITY, null); |
| this.number = number; |
| } |
| |
| public MockIntent(Long number, Collection<NetworkResource> resources) { |
| super(NetTestTools.APP_ID, null, resources, Intent.DEFAULT_INTENT_PRIORITY, null); |
| this.number = number; |
| } |
| |
| public Long number() { |
| return number; |
| } |
| |
| public static Long nextId() { |
| return counter.getAndIncrement(); |
| } |
| |
| @Override |
| public String toString() { |
| return MoreObjects.toStringHelper(getClass()) |
| .add("id", id()) |
| .add("appId", appId()) |
| .toString(); |
| } |
| } |
| |
| public static class MockTimestamp implements Timestamp { |
| final int value; |
| |
| public MockTimestamp(int value) { |
| this.value = value; |
| } |
| |
| @Override |
| public int compareTo(Timestamp o) { |
| if (!(o instanceof MockTimestamp)) { |
| return -1; |
| } |
| MockTimestamp that = (MockTimestamp) o; |
| return this.value - that.value; |
| } |
| |
| @Override |
| public int hashCode() { |
| return value; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (obj instanceof MockTimestamp) { |
| return this.compareTo((MockTimestamp) obj) == 0; |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * Mocks the device service so that a device appears available in the test. |
| */ |
| public static class MockDeviceService extends DeviceServiceAdapter { |
| @Override |
| public boolean isAvailable(DeviceId deviceId) { |
| return true; |
| } |
| } |
| } |