From 643ee33289bd2cb9e6afbfb09b4ed72d467ba1c2 Mon Sep 17 00:00:00 2001 From: Ashlee Young Date: Tue, 3 Nov 2015 14:08:10 -0800 Subject: This updates ONOS src tree to commit id 03fa5e571cabbd001ddb1598847e1150b11c7333 Change-Id: I13b554026d6f902933e35887d29bd5fdb669c0bd Signed-off-by: Ashlee Young --- .../onosproject/cluster/impl/ClusterManager.java | 48 ++- .../cluster/impl/ClusterMetadataManager.java | 116 ++++++ .../net/device/impl/OpticalPortOperator.java | 2 +- .../composition/FlowObjectiveCompositionUtil.java | 24 +- .../impl/compiler/MplsPathIntentCompiler.java | 110 +++--- .../compiler/OpticalCircuitIntentCompiler.java | 3 +- .../OpticalConnectivityIntentCompiler.java | 108 +++--- .../net/newresource/impl/ResourceManager.java | 13 +- .../net/resource/impl/LinkResourceManager.java | 98 +---- .../intent/impl/compiler/MockResourceService.java | 100 ++++++ .../impl/compiler/MplsPathIntentCompilerTest.java | 15 +- .../net/proxyarp/impl/ProxyArpManagerTest.java | 395 ++++++++++++++++++++- 12 files changed, 809 insertions(+), 223 deletions(-) create mode 100644 framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java create mode 100644 framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java (limited to 'framework/src/onos/core/net') diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java index 04d1dfdf..7ddac0ce 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java @@ -25,17 +25,26 @@ import org.apache.karaf.system.SystemService; import org.joda.time.DateTime; import org.onlab.packet.IpAddress; import org.onosproject.cluster.ClusterAdminService; -import org.onosproject.cluster.ClusterDefinitionService; import org.onosproject.cluster.ClusterEvent; import org.onosproject.cluster.ClusterEventListener; +import org.onosproject.cluster.ClusterMetadata; +import org.onosproject.cluster.ClusterMetadataService; import org.onosproject.cluster.ClusterService; import org.onosproject.cluster.ClusterStore; import org.onosproject.cluster.ClusterStoreDelegate; import org.onosproject.cluster.ControllerNode; import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.Partition; import org.onosproject.event.AbstractListenerManager; import org.slf4j.Logger; +import com.google.common.collect.Lists; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Set; import static com.google.common.base.Preconditions.checkArgument; @@ -44,8 +53,6 @@ import static org.onosproject.security.AppGuard.checkPermission; import static org.slf4j.LoggerFactory.getLogger; import static org.onosproject.security.AppPermission.Type.*; - - /** * Implementation of the cluster service. */ @@ -61,7 +68,7 @@ public class ClusterManager private ClusterStoreDelegate delegate = new InternalStoreDelegate(); @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected ClusterDefinitionService clusterDefinitionService; + protected ClusterMetadataService clusterMetadataService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected ClusterStore store; @@ -73,8 +80,9 @@ public class ClusterManager public void activate() { store.setDelegate(delegate); eventDispatcher.addSink(ClusterEvent.class, listenerRegistry); - clusterDefinitionService.seedNodes() - .forEach(node -> store.addNode(node.id(), node.ip(), node.tcpPort())); + clusterMetadataService.getClusterMetadata() + .getNodes() + .forEach(node -> store.addNode(node.id(), node.ip(), node.tcpPort())); log.info("Started"); } @@ -119,11 +127,16 @@ public class ClusterManager } @Override - public void formCluster(Set nodes, String ipPrefix) { + public void formCluster(Set nodes) { checkNotNull(nodes, "Nodes cannot be null"); checkArgument(!nodes.isEmpty(), "Nodes cannot be empty"); - checkNotNull(ipPrefix, "IP prefix cannot be null"); - clusterDefinitionService.formCluster(nodes, ipPrefix); + + ClusterMetadata metadata = ClusterMetadata.builder() + .withName("default") + .withControllerNodes(nodes) + .withPartitions(buildDefaultPartitions(nodes)) + .build(); + clusterMetadataService.setClusterMetadata(metadata); try { log.warn("Shutting down container for cluster reconfiguration!"); systemService.reboot("now", SystemService.Swipe.NONE); @@ -153,4 +166,21 @@ public class ClusterManager post(event); } } + + private static Collection buildDefaultPartitions(Collection nodes) { + List sorted = new ArrayList<>(nodes); + Collections.sort(sorted, (o1, o2) -> o1.id().toString().compareTo(o2.id().toString())); + Collection partitions = Lists.newArrayList(); + + int length = nodes.size(); + int count = 3; + for (int i = 0; i < length; i++) { + Set set = new HashSet<>(count); + for (int j = 0; j < count; j++) { + set.add(sorted.get((i + j) % length).id()); + } + partitions.add(new Partition("p" + (i + 1), set)); + } + return partitions; + } } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java new file mode 100644 index 00000000..a0f7a833 --- /dev/null +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterMetadataManager.java @@ -0,0 +1,116 @@ +package org.onosproject.cluster.impl; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Collection; +import java.util.Enumeration; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpAddress; +import org.onosproject.cluster.ClusterMetadata; +import org.onosproject.cluster.ClusterMetadataEvent; +import org.onosproject.cluster.ClusterMetadataEventListener; +import org.onosproject.cluster.ClusterMetadataService; +import org.onosproject.cluster.ClusterMetadataStore; +import org.onosproject.cluster.ClusterMetadataStoreDelegate; +import org.onosproject.cluster.ControllerNode; +import org.onosproject.event.AbstractListenerManager; +import org.onosproject.store.service.Versioned; +import org.slf4j.Logger; + +/** + * Implementation of ClusterMetadataService. + */ +@Component(immediate = true) +@Service +public class ClusterMetadataManager + extends AbstractListenerManager + implements ClusterMetadataService { + + private ControllerNode localNode; + private final Logger log = getLogger(getClass()); + + private ClusterMetadataStoreDelegate delegate = new InternalStoreDelegate(); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterMetadataStore store; + + @Activate + public void activate() { + store.setDelegate(delegate); + eventDispatcher.addSink(ClusterMetadataEvent.class, listenerRegistry); + establishSelfIdentity(); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + store.unsetDelegate(delegate); + eventDispatcher.removeSink(ClusterMetadataEvent.class); + log.info("Stopped"); + } + + @Override + public ClusterMetadata getClusterMetadata() { + return Versioned.valueOrElse(store.getClusterMetadata(), null); + } + + @Override + public ControllerNode getLocalNode() { + return localNode; + } + + @Override + public void setClusterMetadata(ClusterMetadata metadata) { + checkNotNull(metadata, "Cluster metadata cannot be null"); + store.setClusterMetadata(metadata); + } + + // Store delegate to re-post events emitted from the store. + private class InternalStoreDelegate implements ClusterMetadataStoreDelegate { + @Override + public void notify(ClusterMetadataEvent event) { + post(event); + } + } + + private IpAddress findLocalIp(Collection controllerNodes) throws SocketException { + Enumeration interfaces = + NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + NetworkInterface iface = interfaces.nextElement(); + Enumeration inetAddresses = iface.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + IpAddress ip = IpAddress.valueOf(inetAddresses.nextElement()); + if (controllerNodes.stream() + .map(ControllerNode::ip) + .anyMatch(nodeIp -> ip.equals(nodeIp))) { + return ip; + } + } + } + throw new IllegalStateException("Unable to determine local ip"); + } + + private void establishSelfIdentity() { + try { + IpAddress ip = findLocalIp(getClusterMetadata().getNodes()); + localNode = getClusterMetadata().getNodes() + .stream() + .filter(node -> node.ip().equals(ip)) + .findFirst() + .get(); + } catch (SocketException e) { + throw new IllegalStateException("Cannot determine local IP", e); + } + } +} \ No newline at end of file diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java index 19377cf6..8f601497 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java @@ -112,10 +112,10 @@ public final class OpticalPortOperator implements ConfigOperator { return new OduCltPortDescription(port, odu.isEnabled(), odu.signalType(), sa); case PACKET: case FIBER: + case COPPER: return new DefaultPortDescription(port, descr.isEnabled(), descr.type(), descr.portSpeed(), sa); default: - // this includes copper ports. log.warn("Unsupported optical port type {} - can't update", descr.type()); return descr; } diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java index 137aca1e..0a1af6f5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java @@ -29,9 +29,11 @@ import org.onosproject.net.flow.criteria.VlanPcpCriterion; import org.onosproject.net.flow.criteria.MplsCriterion; import org.onosproject.net.flow.criteria.IPCriterion; import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion; +import org.onosproject.net.flow.criteria.OduSignalIdCriterion; import org.onosproject.net.flow.criteria.Criteria; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.L0ModificationInstruction; +import org.onosproject.net.flow.instructions.L1ModificationInstruction; import org.onosproject.net.flow.instructions.L2ModificationInstruction; import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.flowobjective.DefaultForwardingObjective; @@ -155,6 +157,7 @@ public final class FlowObjectiveCompositionUtil { return treatmentBuilder.build(); } + //CHECKSTYLE:OFF public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment, TrafficSelector trafficSelector) { @@ -195,14 +198,30 @@ public final class FlowObjectiveCompositionUtil { } else { return null; } - } else { - break; } default: break; } break; } + case L1MODIFICATION: { + L1ModificationInstruction l1 = (L1ModificationInstruction) instruction; + switch (l1.subtype()) { + case ODU_SIGID: + if (criterionMap.containsKey(Criterion.Type.ODU_SIGID)) { + if (((OduSignalIdCriterion) criterionMap.get((Criterion.Type.ODU_SIGID))).oduSignalId() + .equals(((L1ModificationInstruction.ModOduSignalIdInstruction) l1) + .oduSignalId())) { + criterionMap.remove(Criterion.Type.ODU_SIGID); + } else { + return null; + } + } + default: + break; + } + break; + } case L2MODIFICATION: { L2ModificationInstruction l2 = (L2ModificationInstruction) instruction; switch (l2.subtype()) { @@ -344,6 +363,7 @@ public final class FlowObjectiveCompositionUtil { return selectorBuilder.build(); } + //CHECKSTYLE:ON public static Set getTypeSet(TrafficSelector trafficSelector) { Set typeSet = new HashSet<>(); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java index 5fd1c85d..acc5a5d5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java @@ -24,12 +24,14 @@ import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.onlab.packet.EthType; import org.onlab.packet.Ethernet; +import org.onlab.packet.MplsLabel; import org.onlab.packet.VlanId; import org.onosproject.core.ApplicationId; import org.onosproject.core.CoreService; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; import org.onosproject.net.Link; +import org.onosproject.net.LinkKey; import org.onosproject.net.PortNumber; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficSelector; @@ -46,24 +48,23 @@ import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentCompiler; import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.MplsPathIntent; -import org.onosproject.net.link.LinkStore; -import org.onosproject.net.resource.link.DefaultLinkResourceRequest; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; import org.onosproject.net.resource.link.LinkResourceAllocations; -import org.onosproject.net.resource.link.LinkResourceRequest; -import org.onosproject.net.resource.link.LinkResourceService; -import org.onosproject.net.resource.link.MplsLabel; -import org.onosproject.net.resource.link.MplsLabelResourceAllocation; -import org.onosproject.net.resource.ResourceAllocation; -import org.onosproject.net.resource.ResourceType; import org.slf4j.Logger; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.net.LinkKey.linkKey; import static org.slf4j.LoggerFactory.getLogger; @Component(immediate = true) @@ -78,18 +79,15 @@ public class MplsPathIntentCompiler implements IntentCompiler { protected CoreService coreService; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkResourceService resourceService; - - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) - protected LinkStore linkStore; + protected ResourceService resourceService; protected ApplicationId appId; @Override public List compile(MplsPathIntent intent, List installable, Set resources) { - LinkResourceAllocations allocations = assignMplsLabel(intent); - List rules = generateRules(intent, allocations); + Map labels = assignMplsLabel(intent); + List rules = generateRules(intent, labels); return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources())); } @@ -105,39 +103,60 @@ public class MplsPathIntentCompiler implements IntentCompiler { intentExtensionService.unregisterCompiler(MplsPathIntent.class); } - private LinkResourceAllocations assignMplsLabel(MplsPathIntent intent) { + private Map assignMplsLabel(MplsPathIntent intent) { // TODO: do it better... Suggestions? - Set linkRequest = Sets.newHashSetWithExpectedSize(intent.path() + Set linkRequest = Sets.newHashSetWithExpectedSize(intent.path() .links().size() - 2); for (int i = 1; i <= intent.path().links().size() - 2; i++) { - Link link = intent.path().links().get(i); + LinkKey link = linkKey(intent.path().links().get(i)); linkRequest.add(link); // add the inverse link. I want that the label is reserved both for // the direct and inverse link - linkRequest.add(linkStore.getLink(link.dst(), link.src())); + linkRequest.add(linkKey(link.dst(), link.src())); } - LinkResourceRequest.Builder request = DefaultLinkResourceRequest - .builder(intent.id(), linkRequest).addMplsRequest(); - LinkResourceAllocations reqMpls = resourceService - .requestResources(request.build()); - return reqMpls; - } + Map labels = findMplsLabels(linkRequest); + if (labels.isEmpty()) { + return Collections.emptyMap(); + } + + List resources = labels.entrySet().stream() + .map(x -> new ResourcePath(linkKey(x.getKey().src(), x.getKey().src()), x.getValue())) + .collect(Collectors.toList()); + List allocations = + resourceService.allocate(intent.id(), resources); + if (allocations.isEmpty()) { + Collections.emptyMap(); + } - private MplsLabel getMplsLabel(LinkResourceAllocations allocations, Link link) { - for (ResourceAllocation allocation : allocations - .getResourceAllocation(link)) { - if (allocation.type() == ResourceType.MPLS_LABEL) { - return ((MplsLabelResourceAllocation) allocation).mplsLabel(); + return labels; + } + private Map findMplsLabels(Set links) { + Map labels = new HashMap<>(); + for (LinkKey link : links) { + Optional label = findMplsLabel(link); + if (label.isPresent()) { + labels.put(link, label.get()); } } - log.warn("MPLS label was not assigned successfully"); - return null; + + return labels; + } + + private Optional findMplsLabel(LinkKey link) { + return resourceService.getAvailableResources(new ResourcePath(link)).stream() + .filter(x -> x.lastComponent() instanceof MplsLabel) + .map(x -> (MplsLabel) x.lastComponent()) + .findFirst(); + } + + private MplsLabel getMplsLabel(Map labels, LinkKey link) { + return labels.get(link); } private List generateRules(MplsPathIntent intent, - LinkResourceAllocations allocations) { + Map labels) { Iterator links = intent.path().links().iterator(); Link srcLink = links.next(); @@ -149,7 +168,7 @@ public class MplsPathIntentCompiler implements IntentCompiler { // Ingress traffic // Get the new MPLS label - MplsLabel mpls = getMplsLabel(allocations, link); + MplsLabel mpls = getMplsLabel(labels, linkKey(link)); checkNotNull(mpls); MplsLabel prevLabel = mpls; rules.add(ingressFlow(prev.port(), link, intent, mpls)); @@ -163,7 +182,7 @@ public class MplsPathIntentCompiler implements IntentCompiler { if (links.hasNext()) { // Transit traffic // Get the new MPLS label - mpls = getMplsLabel(allocations, link); + mpls = getMplsLabel(labels, linkKey(link)); checkNotNull(mpls); rules.add(transitFlow(prev.port(), link, intent, prevLabel, mpls)); @@ -181,7 +200,8 @@ public class MplsPathIntentCompiler implements IntentCompiler { } private FlowRule ingressFlow(PortNumber inPort, Link link, - MplsPathIntent intent, MplsLabel label) { + MplsPathIntent intent, + MplsLabel label) { TrafficSelector.Builder ingressSelector = DefaultTrafficSelector .builder(intent.selector()); @@ -193,10 +213,10 @@ public class MplsPathIntentCompiler implements IntentCompiler { .matchMplsLabel(intent.ingressLabel().get()); // Swap the MPLS label - treat.setMpls(label.label()); + treat.setMpls(label); } else { // Push and set the MPLS label - treat.pushMpls().setMpls(label.label()); + treat.pushMpls().setMpls(label); } // Add the output action treat.setOutput(link.src().port()); @@ -205,21 +225,21 @@ public class MplsPathIntentCompiler implements IntentCompiler { } private FlowRule transitFlow(PortNumber inPort, Link link, - MplsPathIntent intent, - MplsLabel prevLabel, - MplsLabel outLabel) { + MplsPathIntent intent, + MplsLabel prevLabel, + MplsLabel outLabel) { // Ignore the ingress Traffic Selector and use only the MPLS label // assigned in the previous link TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST) - .matchMplsLabel(prevLabel.label()); + .matchMplsLabel(prevLabel); TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(); // Set the new label only if the label on the packet is // different if (!prevLabel.equals(outLabel)) { - treat.setMpls(outLabel.label()); + treat.setMpls(outLabel); } treat.setOutput(link.src().port()); @@ -227,14 +247,14 @@ public class MplsPathIntentCompiler implements IntentCompiler { } private FlowRule egressFlow(PortNumber inPort, Link link, - MplsPathIntent intent, - MplsLabel prevLabel) { + MplsPathIntent intent, + MplsLabel prevLabel) { // egress point: either set the egress MPLS label or pop the // MPLS label based on the intent annotations TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST) - .matchMplsLabel(prevLabel.label()); + .matchMplsLabel(prevLabel); // apply the intent's treatments TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java index c6eb7c5a..fce8498c 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java @@ -17,6 +17,7 @@ package org.onosproject.net.intent.impl.compiler; import org.apache.commons.lang3.tuple.Pair; import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Property; @@ -70,7 +71,7 @@ import static com.google.common.base.Preconditions.checkArgument; * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}. */ // For now, remove component designation until dependency on the new resource manager is available. -// @Component(immediate = true) +@Component(immediate = true) public class OpticalCircuitIntentCompiler implements IntentCompiler { private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java index eb5b4af8..d6725b7c 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java @@ -16,7 +16,11 @@ package org.onosproject.net.intent.impl.compiler; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; @@ -24,6 +28,7 @@ import org.onlab.util.Frequency; import org.onosproject.net.AnnotationKeys; import org.onosproject.net.ConnectPoint; import org.onosproject.net.DeviceId; +import org.onosproject.net.IndexedLambda; import org.onosproject.net.Link; import org.onosproject.net.OchPort; import org.onosproject.net.OchSignal; @@ -38,32 +43,29 @@ import org.onosproject.net.intent.IntentExtensionService; import org.onosproject.net.intent.OpticalConnectivityIntent; import org.onosproject.net.intent.OpticalPathIntent; import org.onosproject.net.intent.impl.IntentCompilationException; +import org.onosproject.net.newresource.ResourceAllocation; import org.onosproject.net.newresource.ResourcePath; import org.onosproject.net.newresource.ResourceService; -import org.onosproject.net.resource.ResourceType; -import org.onosproject.net.resource.link.DefaultLinkResourceRequest; -import org.onosproject.net.resource.link.LambdaResource; -import org.onosproject.net.resource.link.LambdaResourceAllocation; import org.onosproject.net.resource.link.LinkResourceAllocations; -import org.onosproject.net.resource.link.LinkResourceRequest; -import org.onosproject.net.resource.link.LinkResourceService; import org.onosproject.net.topology.LinkWeight; import org.onosproject.net.topology.Topology; import org.onosproject.net.topology.TopologyService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; +import static org.onosproject.net.LinkKey.linkKey; /** * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}. */ // For now, remove component designation until dependency on the new resource manager is available. -// @Component(immediate = true) +@Component(immediate = true) public class OpticalConnectivityIntentCompiler implements IntentCompiler { protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class); @@ -80,9 +82,6 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler linkAllocs.getResourceAllocation(x).stream()) - .filter(x -> x.type() == ResourceType.LAMBDA) - .findFirst() - .map(x -> (LambdaResourceAllocation) x) - .orElse(null); - } - /** * Request and reserve first available wavelength across path. * * @param path path in WDM topology - * @return first available lambda resource allocation + * @return first available lambda allocated */ - private LinkResourceAllocations assignWavelength(Intent intent, Path path) { - LinkResourceRequest.Builder request = - DefaultLinkResourceRequest.builder(intent.id(), path.links()) - .addLambdaRequest(); - - LinkResourceAllocations allocations = linkResourceService.requestResources(request.build()); - - if (!checkWavelengthContinuity(allocations, path)) { - linkResourceService.releaseResources(allocations); + private IndexedLambda assignWavelength(Intent intent, Path path) { + Set lambdas = findCommonLambdasOverLinks(path.links()); + if (lambdas.isEmpty()) { return null; } - return allocations; - } + IndexedLambda minLambda = findFirstLambda(lambdas); + List lambdaResources = path.links().stream() + .map(x -> new ResourcePath(linkKey(x.src(), x.dst()))) + .map(x -> ResourcePath.child(x, minLambda)) + .collect(Collectors.toList()); - /** - * Checks wavelength continuity constraint across path, i.e., an identical lambda is used on all links. - * @return true if wavelength continuity is met, false otherwise - */ - private boolean checkWavelengthContinuity(LinkResourceAllocations allocations, Path path) { - if (allocations == null) { - return false; + List allocations = resourceService.allocate(intent.id(), lambdaResources); + if (allocations.isEmpty()) { + log.info("Resource allocation for {} failed (resource request: {})", intent, lambdaResources); } - List lambdas = path.links().stream() - .flatMap(x -> allocations.getResourceAllocation(x).stream()) - .filter(x -> x.type() == ResourceType.LAMBDA) - .map(x -> ((LambdaResourceAllocation) x).lambda()) - .collect(Collectors.toList()); + return minLambda; + } - LambdaResource lambda = null; - for (LambdaResource nextLambda: lambdas) { - if (nextLambda == null) { - return false; - } - if (lambda == null) { - lambda = nextLambda; - continue; - } - if (!lambda.equals(nextLambda)) { - return false; - } - } + private Set findCommonLambdasOverLinks(List links) { + return links.stream() + .map(x -> new ResourcePath(linkKey(x.src(), x.dst()))) + .map(resourceService::getAvailableResources) + .map(x -> Iterables.filter(x, r -> r.lastComponent() instanceof IndexedLambda)) + .map(x -> Iterables.transform(x, r -> (IndexedLambda) r.lastComponent())) + .map(x -> (Set) ImmutableSet.copyOf(x)) + .reduce(Sets::intersection) + .orElse(Collections.emptySet()); + } - return true; + private IndexedLambda findFirstLambda(Set lambdas) { + return lambdas.stream() + .findFirst() + .get(); } private ConnectPoint staticPort(ConnectPoint connectPoint) { diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java index 5226967f..10fe75ea 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java @@ -41,7 +41,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * An implementation of ResourceService. */ -@Component(immediate = true, enabled = false) +@Component(immediate = true) @Service @Beta public final class ResourceManager implements ResourceService, ResourceAdminService { @@ -126,6 +126,17 @@ public final class ResourceManager implements ResourceService, ResourceAdminServ .collect(Collectors.toList()); } + @Override + public Collection getAvailableResources(ResourcePath parent) { + checkNotNull(parent); + + Collection children = store.getChildResources(parent); + return children.stream() + // We access store twice in this method, then the store may be updated by others + .filter(x -> !store.getConsumer(x).isPresent()) + .collect(Collectors.toList()); + } + @Override public boolean isAvailable(ResourcePath resource) { checkNotNull(resource); diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java index 8b9952ed..7eb189e5 100644 --- a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java +++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java @@ -31,7 +31,6 @@ import org.onosproject.net.resource.ResourceType; import org.onosproject.net.resource.link.BandwidthResourceAllocation; import org.onosproject.net.resource.link.BandwidthResourceRequest; import org.onosproject.net.resource.link.DefaultLinkResourceAllocations; -import org.onosproject.net.resource.link.LambdaResource; import org.onosproject.net.resource.link.LambdaResourceAllocation; import org.onosproject.net.resource.link.LambdaResourceRequest; import org.onosproject.net.resource.link.LinkResourceAllocations; @@ -46,15 +45,12 @@ import org.onosproject.net.resource.link.MplsLabelResourceAllocation; import org.onosproject.net.resource.link.MplsLabelResourceRequest; import org.slf4j.Logger; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.Map; +import java.util.Optional; import java.util.Set; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; import static org.onosproject.security.AppGuard.checkPermission; import static org.slf4j.LoggerFactory.getLogger; import static org.onosproject.security.AppPermission.Type.*; @@ -86,67 +82,6 @@ public class LinkResourceManager log.info("Stopped"); } - /** - * Returns available lambdas on specified link. - * - * @param link the link - * @return available lambdas on specified link - */ - private Set getAvailableLambdas(Link link) { - checkNotNull(link); - Set resAllocs = store.getFreeResources(link); - if (resAllocs == null) { - return Collections.emptySet(); - } - Set lambdas = new HashSet<>(); - for (ResourceAllocation res : resAllocs) { - if (res.type() == ResourceType.LAMBDA) { - lambdas.add(((LambdaResourceAllocation) res).lambda()); - } - } - return lambdas; - } - - - /** - * Returns available lambdas on specified links. - * - * @param links the links - * @return available lambdas on specified links - */ - private Iterable getAvailableLambdas(Iterable links) { - checkNotNull(links); - Iterator i = links.iterator(); - checkArgument(i.hasNext()); - Set lambdas = new HashSet<>(getAvailableLambdas(i.next())); - while (i.hasNext()) { - lambdas.retainAll(getAvailableLambdas(i.next())); - } - return lambdas; - } - - - /** - * Returns available MPLS label on specified link. - * - * @param link the link - * @return available MPLS labels on specified link - */ - private Iterable getAvailableMplsLabels(Link link) { - Set resAllocs = store.getFreeResources(link); - if (resAllocs == null) { - return Collections.emptySet(); - } - Set mplsLabels = new HashSet<>(); - for (ResourceAllocation res : resAllocs) { - if (res.type() == ResourceType.MPLS_LABEL) { - - mplsLabels.add(((MplsLabelResourceAllocation) res).mplsLabel()); - } - } - - return mplsLabels; - } @Override public LinkResourceAllocations requestResources(LinkResourceRequest req) { @@ -164,26 +99,23 @@ public class LinkResourceManager allocs.add(new BandwidthResourceAllocation(br.bandwidth())); break; case LAMBDA: - Iterator lambdaIterator = - getAvailableLambdas(req.links()).iterator(); - if (lambdaIterator.hasNext()) { - allocs.add(new LambdaResourceAllocation(lambdaIterator.next())); - } else { - log.info("Failed to allocate lambda resource."); - return null; - } + LambdaResourceRequest lr = (LambdaResourceRequest) r; + allocs.add(new LambdaResourceAllocation(lr.lambda())); break; case MPLS_LABEL: for (Link link : req.links()) { if (allocsPerLink.get(link) == null) { allocsPerLink.put(link, new HashSet<>()); } - Iterator mplsIter = getAvailableMplsLabels(link) - .iterator(); - if (mplsIter.hasNext()) { - allocsPerLink.get(link) - .add(new MplsLabelResourceAllocation(mplsIter - .next())); + + Optional label = req.resources(link).stream() + .filter(x -> x.type() == ResourceType.MPLS_LABEL) + .map(x -> (MplsLabelResourceRequest) x) + .map(MplsLabelResourceRequest::mplsLabel) + .findFirst(); + + if (label.isPresent()) { + allocsPerLink.get(link).add(new MplsLabelResourceAllocation(label.get())); } else { log.info("Failed to allocate MPLS resource."); break; @@ -258,10 +190,12 @@ public class LinkResourceManager ((BandwidthResourceAllocation) alloc).bandwidth())); break; case LAMBDA: - result.add(new LambdaResourceRequest()); + result.add(new LambdaResourceRequest( + ((LambdaResourceAllocation) alloc).lambda())); break; case MPLS_LABEL: - result.add(new MplsLabelResourceRequest()); + result.add(new MplsLabelResourceRequest( + ((MplsLabelResourceAllocation) alloc).mplsLabel())); break; default: break; diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java new file mode 100644 index 00000000..06b2c81e --- /dev/null +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MockResourceService.java @@ -0,0 +1,100 @@ +/* + * Copyright 2015 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.net.intent.impl.compiler; + +import com.google.common.collect.ImmutableList; +import org.onlab.packet.MplsLabel; +import org.onosproject.net.newresource.ResourceAllocation; +import org.onosproject.net.newresource.ResourceConsumer; +import org.onosproject.net.newresource.ResourcePath; +import org.onosproject.net.newresource.ResourceService; + +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +class MockResourceService implements ResourceService { + + private final Map assignment = new HashMap<>(); + + @Override + public List allocate(ResourceConsumer consumer, List resources) { + assignment.putAll( + resources.stream().collect(Collectors.toMap(x -> x, x -> consumer)) + ); + + return resources.stream() + .map(x -> new ResourceAllocation(x, consumer)) + .collect(Collectors.toList()); + } + + @Override + public boolean release(List allocations) { + allocations.forEach(x -> assignment.remove(x.resource())); + + return true; + } + + @Override + public boolean release(ResourceConsumer consumer) { + List resources = assignment.entrySet().stream() + .filter(x -> x.getValue().equals(consumer)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + List allocations = resources.stream() + .map(x -> new ResourceAllocation(x, consumer)) + .collect(Collectors.toList()); + + return release(allocations); + } + + @Override + public Optional getResourceAllocation(ResourcePath resource) { + return Optional.ofNullable(assignment.get(resource)) + .map(x -> new ResourceAllocation(resource, x)); + } + + @Override + public Collection getResourceAllocations(ResourcePath parent, Class cls) { + return assignment.entrySet().stream() + .filter(x -> x.getKey().parent().isPresent()) + .filter(x -> x.getKey().parent().get().equals(parent)) + .map(x -> new ResourceAllocation(x.getKey(), x.getValue())) + .collect(Collectors.toList()); + } + + @Override + public Collection getResourceAllocations(ResourceConsumer consumer) { + return assignment.entrySet().stream() + .filter(x -> x.getValue().equals(consumer)) + .map(x -> new ResourceAllocation(x.getKey(), x.getValue())) + .collect(Collectors.toList()); + } + + @Override + public Collection getAvailableResources(ResourcePath parent) { + ResourcePath resource = ResourcePath.child(parent, MplsLabel.mplsLabel(10)); + return ImmutableList.of(resource); + } + + @Override + public boolean isAvailable(ResourcePath resource) { + return true; + } +} diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java index 771a9883..6cceee12 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompilerTest.java @@ -41,10 +41,8 @@ import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.intent.FlowRuleIntent; import org.onosproject.net.intent.Intent; import org.onosproject.net.intent.IntentExtensionService; -import org.onosproject.net.intent.IntentTestsMocks; import org.onosproject.net.intent.MockIdGenerator; import org.onosproject.net.intent.MplsPathIntent; -import org.onosproject.store.trivial.SimpleLinkStore; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; @@ -52,6 +50,7 @@ import static org.easymock.EasyMock.replay; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.onosproject.net.DefaultEdgeLink.createEdgeLink; import static org.onosproject.net.Link.Type.DIRECT; import static org.onosproject.net.NetTestTools.APP_ID; import static org.onosproject.net.NetTestTools.PID; @@ -61,10 +60,12 @@ public class MplsPathIntentCompilerTest { private final ApplicationId appId = new TestApplicationId("test"); + private final ConnectPoint d1pi = connectPoint("s1", 100); private final ConnectPoint d1p1 = connectPoint("s1", 0); private final ConnectPoint d2p0 = connectPoint("s2", 0); private final ConnectPoint d2p1 = connectPoint("s2", 1); private final ConnectPoint d3p1 = connectPoint("s3", 1); + private final ConnectPoint d3pe = connectPoint("s3", 100); private final TrafficSelector selector = DefaultTrafficSelector.builder().build(); private final TrafficTreatment treatment = DefaultTrafficTreatment.builder().build(); @@ -75,8 +76,10 @@ public class MplsPathIntentCompilerTest { Optional.of(MplsLabel.mplsLabel(20)); private final List links = Arrays.asList( + createEdgeLink(d1pi, true), new DefaultLink(PID, d1p1, d2p0, DIRECT), - new DefaultLink(PID, d2p1, d3p1, DIRECT) + new DefaultLink(PID, d2p1, d3p1, DIRECT), + createEdgeLink(d3pe, false) ); private IdGenerator idGenerator = new MockIdGenerator(); @@ -92,8 +95,7 @@ public class MplsPathIntentCompilerTest { expect(coreService.registerApplication("org.onosproject.net.intent")) .andReturn(appId); sut.coreService = coreService; - sut.linkStore = new SimpleLinkStore(); - sut.resourceService = new IntentTestsMocks.MockResourceService(); + sut.resourceService = new MockResourceService(); Intent.bindIdGenerator(idGenerator); @@ -128,7 +130,7 @@ public class MplsPathIntentCompilerTest { assertThat(compiled, hasSize(1)); Collection rules = ((FlowRuleIntent) compiled.get(0)).flowRules(); - assertThat(rules, hasSize(1)); + assertThat(rules, hasSize(3)); FlowRule rule = rules.stream() .filter(x -> x.deviceId().equals(d2p0.deviceId())) @@ -139,4 +141,5 @@ public class MplsPathIntentCompilerTest { sut.deactivate(); } + } diff --git a/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java index 1a160d98..3e806a73 100644 --- a/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java +++ b/framework/src/onos/core/net/src/test/java/org/onosproject/net/proxyarp/impl/ProxyArpManagerTest.java @@ -21,11 +21,19 @@ import org.junit.Before; import org.junit.Test; import org.onlab.packet.ARP; import org.onlab.packet.Ethernet; +import org.onlab.packet.ICMP6; +import org.onlab.packet.IPacket; +import org.onlab.packet.IPv6; import org.onlab.packet.Ip4Address; import org.onlab.packet.Ip4Prefix; +import org.onlab.packet.Ip6Address; +import org.onlab.packet.Ip6Prefix; import org.onlab.packet.IpPrefix; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; +import org.onlab.packet.ndp.NeighborAdvertisement; +import org.onlab.packet.ndp.NeighborDiscoveryOptions; +import org.onlab.packet.ndp.NeighborSolicitation; import org.onosproject.incubator.net.intf.Interface; import org.onosproject.incubator.net.intf.InterfaceService; import org.onosproject.net.ConnectPoint; @@ -66,9 +74,13 @@ import static org.easymock.EasyMock.anyObject; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; +import static org.hamcrest.Matchers.anyOf; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; /** @@ -83,6 +95,8 @@ public class ProxyArpManagerTest { private static final Ip4Address IP1 = Ip4Address.valueOf("192.168.1.1"); private static final Ip4Address IP2 = Ip4Address.valueOf("192.168.1.2"); + private static final Ip6Address IP3 = Ip6Address.valueOf("1000::1"); + private static final Ip6Address IP4 = Ip6Address.valueOf("1000::2"); private static final ProviderId PID = new ProviderId("of", "foo"); @@ -90,8 +104,14 @@ public class ProxyArpManagerTest { private static final VlanId VLAN2 = VlanId.vlanId((short) 2); private static final MacAddress MAC1 = MacAddress.valueOf("00:00:11:00:00:01"); private static final MacAddress MAC2 = MacAddress.valueOf("00:00:22:00:00:02"); + private static final MacAddress MAC3 = MacAddress.valueOf("00:00:33:00:00:03"); + private static final MacAddress MAC4 = MacAddress.valueOf("00:00:44:00:00:04"); + private static final MacAddress SOLICITED_MAC3 = MacAddress.valueOf("33:33:FF:00:00:01"); private static final HostId HID1 = HostId.hostId(MAC1, VLAN1); private static final HostId HID2 = HostId.hostId(MAC2, VLAN1); + private static final HostId HID3 = HostId.hostId(MAC3, VLAN1); + private static final HostId HID4 = HostId.hostId(MAC4, VLAN1); + private static final HostId SOLICITED_HID3 = HostId.hostId(SOLICITED_MAC3, VLAN1); private static final DeviceId DID1 = getDeviceId(1); private static final DeviceId DID2 = getDeviceId(2); @@ -222,21 +242,29 @@ public class ProxyArpManagerTest { for (int i = 1; i <= NUM_ADDRESS_PORTS; i++) { ConnectPoint cp = new ConnectPoint(getDeviceId(i), P1); - Ip4Prefix prefix1 = - Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24"); - Ip4Address addr1 = - Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1"); + + // Interface address for IPv4 + Ip4Prefix prefix1 = Ip4Prefix.valueOf("10.0." + (2 * i - 1) + ".0/24"); + Ip4Address addr1 = Ip4Address.valueOf("10.0." + (2 * i - 1) + ".1"); Ip4Prefix prefix2 = Ip4Prefix.valueOf("10.0." + (2 * i) + ".0/24"); Ip4Address addr2 = Ip4Address.valueOf("10.0." + (2 * i) + ".1"); InterfaceIpAddress ia1 = new InterfaceIpAddress(addr1, prefix1); InterfaceIpAddress ia2 = new InterfaceIpAddress(addr2, prefix2); - Interface intf1 = new Interface(cp, Sets.newHashSet(ia1), + + // Interface address for IPv6 + Ip6Prefix prefix3 = Ip6Prefix.valueOf((2 * i - 1) + "000::0/64"); + Ip6Address addr3 = Ip6Address.valueOf((2 * i - 1) + "000::1"); + Ip6Prefix prefix4 = Ip6Prefix.valueOf((2 * i) + "000::0/64"); + Ip6Address addr4 = Ip6Address.valueOf((2 * i) + "000::1"); + InterfaceIpAddress ia3 = new InterfaceIpAddress(addr3, prefix3); + InterfaceIpAddress ia4 = new InterfaceIpAddress(addr4, prefix4); + + Interface intf1 = new Interface(cp, Sets.newHashSet(ia1, ia3), MacAddress.valueOf(2 * i - 1), VlanId.vlanId((short) 1)); - Interface intf2 = new Interface(cp, Sets.newHashSet(ia2), + Interface intf2 = new Interface(cp, Sets.newHashSet(ia2, ia4), MacAddress.valueOf(2 * i), VlanId.NONE); - interfaces.add(intf1); interfaces.add(intf2); @@ -319,6 +347,41 @@ public class ProxyArpManagerTest { verifyPacketOut(arpReply, getLocation(5), packetService.packets.get(0)); } + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct NDP reply is sent out the correct port. + */ + @Test + public void testReplyKnownIpv6() { + //Set the return value of isEdgePoint from the edgemanager. + isEdgePointReturn = true; + + Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN1, getLocation(4), + Collections.singleton(IP3)); + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.singleton(replyer)); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + proxyArp.reply(ndpRequest, getLocation(5)); + + assertEquals(1, packetService.packets.size()); + Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + MAC3, MAC4, IP3, IP4); + verifyPacketOut(ndpReply, getLocation(5), packetService.packets.get(0)); + } + /** * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the * destination host is not known. @@ -337,7 +400,6 @@ public class ProxyArpManagerTest { .andReturn(Collections.emptySet()); expect(hostService.getHost(HID2)).andReturn(requestor); - replay(hostService); replay(interfaceService); @@ -353,6 +415,41 @@ public class ProxyArpManagerTest { verifyFlood(arpRequest); } + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the NDP request is flooded out the correct edge ports. + */ + @Test + public void testReplyUnknownIpv6() { + isEdgePointReturn = true; + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(IP4)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //Setup the set of edge ports to be used in the reply method + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.reply(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + /** * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the * destination host is known for that IP address, but is not on the same @@ -388,6 +485,46 @@ public class ProxyArpManagerTest { verifyFlood(arpRequest); } + /** + * Tests {@link ProxyArpManager#reply(Ethernet, ConnectPoint)} in the case where the + * destination host is known for that IP address, but is not on the same + * VLAN as the source host. + * Verifies the NDP request is flooded out the correct edge ports. + */ + @Test + public void testReplyDifferentVlanIpv6() { + + Host replyer = new DefaultHost(PID, HID3, MAC3, VLAN2, getLocation(4), + Collections.singleton(IP3)); + + Host requestor = new DefaultHost(PID, HID4, MAC4, VLAN1, getLocation(5), + Collections.singleton(IP4)); + + expect(hostService.getHostsByIp(IP3)) + .andReturn(Collections.singleton(replyer)); + expect(interfaceService.getInterfacesByIp(IP4)) + .andReturn(Collections.emptySet()); + expect(hostService.getHost(HID4)).andReturn(requestor); + + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //Setup for flood test + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + proxyArp.reply(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + + /** + * Test ARP request from external network to an internal host. + */ @Test public void testReplyToRequestForUs() { Ip4Address theirIp = Ip4Address.valueOf("10.0.1.254"); @@ -422,6 +559,63 @@ public class ProxyArpManagerTest { verifyPacketOut(arpReply, LOC1, packetService.packets.get(0)); } + /** + * Test NDP request from external network to an internal host. + */ + @Test + public void testReplyToRequestForUsIpv6() { + Ip6Address theirIp = Ip6Address.valueOf("1000::ffff"); + Ip6Address ourFirstIp = Ip6Address.valueOf("1000::1"); + Ip6Address ourSecondIp = Ip6Address.valueOf("2000::1"); + MacAddress firstMac = MacAddress.valueOf(1L); + MacAddress secondMac = MacAddress.valueOf(2L); + + Host requestor = new DefaultHost(PID, HID2, MAC2, VLAN1, LOC1, + Collections.singleton(theirIp)); + + expect(hostService.getHost(HID2)).andReturn(requestor); + expect(hostService.getHostsByIp(ourFirstIp)) + .andReturn(Collections.singleton(requestor)); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC2, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + ourFirstIp); + isEdgePointReturn = true; + proxyArp.reply(ndpRequest, LOC1); + assertEquals(1, packetService.packets.size()); + + Ethernet ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + firstMac, + MAC2, + ourFirstIp, + theirIp); + verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0)); + + // Test a request for the second address on that port + packetService.packets.clear(); + ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC2, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + ourSecondIp); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(1, packetService.packets.size()); + + ndpReply = buildNDP(ICMP6.NEIGHBOR_ADVERTISEMENT, + secondMac, + MAC2, + ourSecondIp, + theirIp); + verifyPacketOut(ndpReply, LOC1, packetService.packets.get(0)); + } + + /** + * Request for a valid external IPv4 address but coming in the wrong port. + */ @Test public void testReplyExternalPortBadRequest() { replay(hostService); // no further host service expectations @@ -442,6 +636,38 @@ public class ProxyArpManagerTest { assertEquals(0, packetService.packets.size()); } + /** + * Request for a valid external IPv6 address but coming in the wrong port. + */ + @Test + public void testReplyExternalPortBadRequestIpv6() { + replay(hostService); // no further host service expectations + replay(interfaceService); + + Ip6Address theirIp = Ip6Address.valueOf("1000::ffff"); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC1, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + Ip6Address.valueOf("3000::1")); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + + // Request for a valid internal IP address but coming in an external port + packetService.packets.clear(); + ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC1, + MacAddress.valueOf("33:33:ff:00:00:01"), + theirIp, + IP3); + proxyArp.reply(ndpRequest, LOC1); + assertEquals(0, packetService.packets.size()); + } + + /** + * Test ARP request from internal network to an external host. + */ @Test public void testReplyToRequestFromUs() { Ip4Address ourIp = Ip4Address.valueOf("10.0.1.1"); @@ -473,6 +699,48 @@ public class ProxyArpManagerTest { assertEquals(0, packetService.packets.size()); } + /** + * Test NDP request from internal network to an external host. + */ + @Test + public void testReplyToRequestFromUsIpv6() { + Ip6Address ourIp = Ip6Address.valueOf("1000::1"); + MacAddress ourMac = MacAddress.valueOf(1L); + Ip6Address theirIp = Ip6Address.valueOf("1000::100"); + + expect(hostService.getHostsByIp(theirIp)).andReturn(Collections.emptySet()); + expect(interfaceService.getInterfacesByIp(ourIp)) + .andReturn(Collections.singleton(new Interface(getLocation(1), + Collections.singleton(new InterfaceIpAddress( + ourIp, + IpPrefix.valueOf("1000::1/64"))), + ourMac, + VLAN1))); + expect(hostService.getHost(HostId.hostId(ourMac, VLAN1))).andReturn(null); + replay(hostService); + replay(interfaceService); + + // This is a request from something inside our network (like a BGP + // daemon) to an external host. + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + ourMac, + MacAddress.valueOf("33:33:ff:00:00:01"), + ourIp, + theirIp); + + //Ensure the packet is allowed through (it is not to an internal port) + isEdgePointReturn = true; + + proxyArp.reply(ndpRequest, getLocation(5)); + assertEquals(1, packetService.packets.size()); + verifyPacketOut(ndpRequest, getLocation(1), packetService.packets.get(0)); + + // The same request from a random external port should fail + packetService.packets.clear(); + proxyArp.reply(ndpRequest, getLocation(2)); + assertEquals(0, packetService.packets.size()); + } + /** * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the * destination host is known. @@ -500,6 +768,35 @@ public class ProxyArpManagerTest { verifyPacketOut(arpRequest, LOC1, packet); } + /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is known. + * Verifies the correct ARP request is sent out the correct port. + */ + @Test + public void testForwardToHostIpv6() { + Host host1 = new DefaultHost(PID, HID3, MAC3, VLAN1, LOC1, + Collections.singleton(IP3)); + Host host2 = new DefaultHost(PID, HID4, MAC4, VLAN1, LOC2, + Collections.singleton(IP4)); + + expect(hostService.getHost(SOLICITED_HID3)).andReturn(host1); + expect(hostService.getHost(HID4)).andReturn(host2); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + proxyArp.forward(ndpRequest, LOC2); + + assertEquals(1, packetService.packets.size()); + OutboundPacket packet = packetService.packets.get(0); + + verifyPacketOut(ndpRequest, LOC1, packet); + } + /** * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the * destination host is not known. @@ -525,6 +822,33 @@ public class ProxyArpManagerTest { verifyFlood(arpRequest); } + /** + * Tests {@link ProxyArpManager#forward(Ethernet, ConnectPoint)} in the case where the + * destination host is not known. + * Verifies the correct NDP request is flooded out the correct edge ports. + */ + @Test + public void testForwardFloodIpv6() { + expect(hostService.getHost(SOLICITED_HID3)).andReturn(null); + replay(hostService); + replay(interfaceService); + + Ethernet ndpRequest = buildNDP(ICMP6.NEIGHBOR_SOLICITATION, + MAC4, SOLICITED_MAC3, + IP4, IP3); + + //populate the list of edges when so that when forward hits flood in the manager it contains the values + //that should continue on + getEdgePointsNoArg = Lists.newLinkedList(); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("3"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("5"), PortNumber.portNumber(1))); + getEdgePointsNoArg.add(new ConnectPoint(DeviceId.deviceId("4"), PortNumber.portNumber(1))); + + proxyArp.forward(ndpRequest, getLocation(6)); + + verifyFlood(ndpRequest); + } + /** * Verifies that the given packet was flooded out all available edge ports, * except for the input port. @@ -625,6 +949,61 @@ public class ProxyArpManagerTest { return eth; } + /** + * Builds an NDP packet with the given parameters. + * + * @param type NeighborSolicitation or NeighborAdvertisement + * @param srcMac source MAC address + * @param dstMac destination MAC address, or null if this is a request + * @param srcIp source IP address + * @param dstIp destination IP address + * @return the NDP packet + */ + private Ethernet buildNDP(byte type, MacAddress srcMac, MacAddress dstMac, + Ip6Address srcIp, Ip6Address dstIp) { + assertThat(type, anyOf( + is(ICMP6.NEIGHBOR_SOLICITATION), + is(ICMP6.NEIGHBOR_ADVERTISEMENT) + )); + assertNotNull(srcMac); + assertNotNull(dstMac); + assertNotNull(srcIp); + assertNotNull(dstIp); + + IPacket ndp; + if (type == ICMP6.NEIGHBOR_SOLICITATION) { + ndp = new NeighborSolicitation().setTargetAddress(dstIp.toOctets()); + } else { + ndp = new NeighborAdvertisement() + .setSolicitedFlag((byte) 1) + .setOverrideFlag((byte) 1) + .setTargetAddress(srcIp.toOctets()) + .addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS, + srcMac.toBytes()); + } + + ICMP6 icmp6 = new ICMP6(); + icmp6.setIcmpType(type); + icmp6.setIcmpCode((byte) 0); + icmp6.setPayload(ndp); + + IPv6 ipv6 = new IPv6(); + ipv6.setDestinationAddress(dstIp.toOctets()); + ipv6.setSourceAddress(srcIp.toOctets()); + ipv6.setNextHeader(IPv6.PROTOCOL_ICMP6); + ipv6.setHopLimit((byte) 255); + ipv6.setPayload(icmp6); + + Ethernet eth = new Ethernet(); + eth.setDestinationMACAddress(dstMac); + eth.setSourceMACAddress(srcMac); + eth.setEtherType(Ethernet.TYPE_IPV6); + eth.setVlanID(VLAN1.toShort()); + eth.setPayload(ipv6); + + return eth; + } + /** * Test PacketService implementation that simply stores OutboundPackets * passed to {@link #emit(OutboundPacket)} for later verification. -- cgit 1.2.3-korg