Sho SHIMIZU
Committed by Brian O'Connor

Add sub-types to distinguish type of resources

Change-Id: Ia43cbf4a13937c9bd9dbc97221062ef5fa3e578f
......@@ -19,13 +19,14 @@ import com.google.common.annotations.Beta;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
/**
* An object that is used to locate a resource in a network.
......@@ -33,25 +34,45 @@ import static com.google.common.base.Preconditions.checkNotNull;
* of elementary resources that are not globally identifiable. A ResourcePath can be a globally
* unique resource identifier.
*
* Two types of resource are considered. One is discrete type and the other is continuous type.
* Discrete type resource is a resource whose amount is measured as a discrete unit. VLAN ID and
* MPLS label are examples of discrete type resource. Continuous type resource is a resource whose
* amount is measured as a continuous value. Bandwidth is an example of continuous type resource.
* A double value is associated with a continuous type value.
*
* Users of this class must keep the semantics of resources regarding the hierarchical structure.
* For example, resource path, Link:1/VLAN ID:100, is valid, but resource path, VLAN ID:100/Link:1
* is not valid because a link is not a sub-component of a VLAN ID.
*/
@Beta
public final class ResourcePath {
public abstract class ResourcePath {
private final ResourcePath parent;
private final Discrete parent;
private final Object last;
public static final ResourcePath ROOT = new ResourcePath(ImmutableList.of());
public static final Discrete ROOT = new Discrete();
/**
* Creates an resource path from the specified components.
* Creates an resource path which represents a discrete-type resource from the specified components.
*
* @param components components of the path. The order represents hierarchical structure of the resource.
*/
public static ResourcePath discrete(Object... components) {
if (components.length == 0) {
return ROOT;
} else {
return new Discrete(ImmutableList.copyOf(components));
}
}
/**
* Creates an resource path which represents a continuous-type resource from the specified components.
*
* @param value amount of the resource
* @param components components of the path. The order represents hierarchical structure of the resource.
*/
public ResourcePath(Object... components) {
this(Arrays.asList(components));
public static ResourcePath continuous(double value, Object... components) {
return new Continuous(ImmutableList.copyOf(components), value);
}
/**
......@@ -59,17 +80,17 @@ public final class ResourcePath {
*
* @param components components of the path. The order represents hierarchical structure of the resource.
*/
public ResourcePath(List<Object> components) {
ResourcePath(List<Object> components) {
checkNotNull(components);
if (components.isEmpty()) {
this.parent = null;
this.last = null;
return;
}
checkArgument(!components.isEmpty());
LinkedList<Object> children = new LinkedList<>(components);
this.last = children.pollLast();
this.parent = new ResourcePath(children);
if (children.isEmpty()) {
this.parent = ROOT;
} else {
this.parent = new Discrete(children);
}
}
/**
......@@ -78,9 +99,12 @@ public final class ResourcePath {
* @param parent the parent of this resource
* @param last a child of the parent
*/
public ResourcePath(ResourcePath parent, Object last) {
this.parent = checkNotNull(parent);
this.last = checkNotNull(last);
ResourcePath(Discrete parent, Object last) {
checkNotNull(parent);
checkNotNull(last);
this.parent = parent;
this.last = last;
}
// for serialization
......@@ -97,10 +121,10 @@ public final class ResourcePath {
public List<Object> components() {
LinkedList<Object> components = new LinkedList<>();
ResourcePath parentPath = parent;
while (parentPath != null) {
Optional<Discrete> parentPath = Optional.ofNullable(parent);
while (parentPath.isPresent()) {
components.addFirst(last);
parentPath = parent.parent;
parentPath = parent.parent();
}
return components;
......@@ -113,12 +137,20 @@ public final class ResourcePath {
* @return the parent resource path of this instance.
* If there is no parent, empty instance will be returned.
*/
public Optional<ResourcePath> parent() {
public Optional<Discrete> parent() {
return Optional.ofNullable(parent);
}
public ResourcePath child(Object child) {
return new ResourcePath(this, child);
checkState(this instanceof Discrete);
return new Discrete((Discrete) this, child);
}
public ResourcePath child(Object child, double value) {
checkState(this instanceof Discrete);
return new Continuous((Discrete) this, child, value);
}
/**
......@@ -156,4 +188,57 @@ public final class ResourcePath {
.add("last", last)
.toString();
}
/**
* Represents a resource path which specifies a resource which can be measured
* as a discrete unit. A VLAN ID and a MPLS label of a link are examples of the resource.
* <p>
* Note: This class is exposed to the public, but intended to be used in the resource API
* implementation only. It is not for resource API user.
* </p>
*/
public static final class Discrete extends ResourcePath {
private Discrete() {
super();
}
private Discrete(List<Object> components) {
super(components);
}
private Discrete(Discrete parent, Object last) {
super(parent, last);
}
}
/**
* Represents a resource path which specifies a resource which can be measured
* as continuous value. Bandwidth of a link is an example of the resource.
* <p>
* Note: This class is exposed to the public, but intended to be used in the resource API
* implementation only. It is not for resource API user.
*/
public static final class Continuous extends ResourcePath {
// Note: value is not taken into account for equality
private final double value;
private Continuous(List<Object> components, double value) {
super(components);
this.value = value;
}
public Continuous(Discrete parent, Object last, double value) {
super(parent, last);
this.value = value;
}
/**
* Returns the value of the resource amount.
*
* @return the value of the resource amount
*/
public double value() {
return value;
}
}
}
......
......@@ -38,9 +38,9 @@ public class ResourceAllocationTest {
@Test
public void testEquals() {
ResourceAllocation alloc1 = new ResourceAllocation(new ResourcePath(LK1, VLAN1), IID1);
ResourceAllocation sameAsAlloc1 = new ResourceAllocation(new ResourcePath(LK1, VLAN1), IID1);
ResourceAllocation alloc2 = new ResourceAllocation(new ResourcePath(LK2, VLAN1), IID1);
ResourceAllocation alloc1 = new ResourceAllocation(ResourcePath.discrete(LK1, VLAN1), IID1);
ResourceAllocation sameAsAlloc1 = new ResourceAllocation(ResourcePath.discrete(LK1, VLAN1), IID1);
ResourceAllocation alloc2 = new ResourceAllocation(ResourcePath.discrete(LK2, VLAN1), IID1);
new EqualsTester()
.addEqualityGroup(alloc1, sameAsAlloc1)
......
......@@ -18,6 +18,7 @@ package org.onosproject.net.newresource;
import com.google.common.testing.EqualsTester;
import org.junit.Test;
import org.onlab.packet.VlanId;
import org.onlab.util.Bandwidth;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.LinkKey;
......@@ -36,37 +37,42 @@ public class ResourcePathTest {
private static final ConnectPoint CP1_1 = new ConnectPoint(D1, P1);
private static final ConnectPoint CP2_1 = new ConnectPoint(D2, P1);
private static final VlanId VLAN1 = VlanId.vlanId((short) 100);
private static final Bandwidth BW1 = Bandwidth.gbps(2);
private static final Bandwidth BW2 = Bandwidth.gbps(1);
@Test
public void testEquals() {
ResourcePath resource1 = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath sameAsResource1 = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath resource2 = new ResourcePath(LinkKey.linkKey(CP2_1, CP1_1), VLAN1);
ResourcePath resource1 = ResourcePath.discrete(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath sameAsResource1 = ResourcePath.discrete(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath resource2 = ResourcePath.discrete(LinkKey.linkKey(CP2_1, CP1_1), VLAN1);
ResourcePath resource3 = ResourcePath.continuous(BW1.bps(), LinkKey.linkKey(CP1_1, CP2_1), BW1);
ResourcePath sameAsResource3 = ResourcePath.continuous(BW2.bps(), LinkKey.linkKey(CP1_1, CP2_1), BW1);
new EqualsTester()
.addEqualityGroup(resource1, sameAsResource1)
.addEqualityGroup(resource2)
.addEqualityGroup(resource3, sameAsResource3) // this is intentional
.testEquals();
}
@Test
public void testCreateWithZeroComponent() {
ResourcePath path = new ResourcePath();
ResourcePath path = ResourcePath.discrete();
assertThat(path, is(ResourcePath.ROOT));
}
@Test
public void testThereIsParent() {
ResourcePath path = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath parent = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1));
ResourcePath path = ResourcePath.discrete(LinkKey.linkKey(CP1_1, CP2_1), VLAN1);
ResourcePath parent = ResourcePath.discrete(LinkKey.linkKey(CP1_1, CP2_1));
assertThat(path.parent(), is(Optional.of(parent)));
}
@Test
public void testNoParent() {
ResourcePath path = new ResourcePath(LinkKey.linkKey(CP1_1, CP2_1));
ResourcePath path = ResourcePath.discrete(LinkKey.linkKey(CP1_1, CP2_1));
assertThat(path.parent(), is(Optional.of(ResourcePath.ROOT)));
}
......@@ -74,7 +80,7 @@ public class ResourcePathTest {
@Test
public void testBase() {
LinkKey linkKey = LinkKey.linkKey(CP1_1, CP2_1);
ResourcePath path = new ResourcePath(linkKey);
ResourcePath path = ResourcePath.discrete(linkKey);
LinkKey child = (LinkKey) path.last();
assertThat(child, is(linkKey));
......
......@@ -121,7 +121,7 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
}
List<ResourcePath> resources = labels.entrySet().stream()
.map(x -> new ResourcePath(linkKey(x.getKey().src(), x.getKey().src()), x.getValue()))
.map(x -> ResourcePath.discrete(linkKey(x.getKey().src(), x.getKey().src()), x.getValue()))
.collect(Collectors.toList());
List<org.onosproject.net.newresource.ResourceAllocation> allocations =
resourceService.allocate(intent.id(), resources);
......@@ -145,7 +145,7 @@ public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
}
private Optional<MplsLabel> findMplsLabel(LinkKey link) {
return resourceService.getAvailableResources(new ResourcePath(link)).stream()
return resourceService.getAvailableResources(ResourcePath.discrete(link)).stream()
.filter(x -> x.last() instanceof MplsLabel)
.map(x -> (MplsLabel) x.last())
.findFirst();
......
......@@ -160,8 +160,8 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
log.debug("Compiling optical circuit intent between {} and {}", src, dst);
// Reserve OduClt ports
ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
ResourcePath srcPortPath = ResourcePath.discrete(src.deviceId(), src.port());
ResourcePath dstPortPath = ResourcePath.discrete(dst.deviceId(), dst.port());
List<ResourceAllocation> allocation = resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
if (allocation.isEmpty()) {
throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
......@@ -312,7 +312,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
if (ochCP != null) {
OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
Optional<IntentId> intentId =
resourceService.getResourceAllocation(new ResourcePath(ochCP.deviceId(), ochCP.port()))
resourceService.getResourceAllocation(ResourcePath.discrete(ochCP.deviceId(), ochCP.port()))
.map(ResourceAllocation::consumer)
.filter(x -> x instanceof IntentId)
.map(x -> (IntentId) x);
......@@ -331,7 +331,7 @@ public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircu
}
Optional<IntentId> intentId =
resourceService.getResourceAllocation(new ResourcePath(oduPort.deviceId(), port.number()))
resourceService.getResourceAllocation(ResourcePath.discrete(oduPort.deviceId(), port.number()))
.map(ResourceAllocation::consumer)
.filter(x -> x instanceof IntentId)
.map(x -> (IntentId) x);
......
......@@ -107,8 +107,8 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
// Reserve OCh ports
ResourcePath srcPortPath = new ResourcePath(src.deviceId(), src.port());
ResourcePath dstPortPath = new ResourcePath(dst.deviceId(), dst.port());
ResourcePath srcPortPath = ResourcePath.discrete(src.deviceId(), src.port());
ResourcePath dstPortPath = ResourcePath.discrete(dst.deviceId(), dst.port());
List<org.onosproject.net.newresource.ResourceAllocation> allocation =
resourceService.allocate(intent.id(), srcPortPath, dstPortPath);
if (allocation.isEmpty()) {
......@@ -182,7 +182,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
IndexedLambda minLambda = findFirstLambda(lambdas);
List<ResourcePath> lambdaResources = path.links().stream()
.map(x -> new ResourcePath(linkKey(x.src(), x.dst())))
.map(x -> ResourcePath.discrete(linkKey(x.src(), x.dst())))
.map(x -> x.child(minLambda))
.collect(Collectors.toList());
......@@ -196,7 +196,7 @@ public class OpticalConnectivityIntentCompiler implements IntentCompiler<Optical
private Set<IndexedLambda> findCommonLambdasOverLinks(List<Link> links) {
return links.stream()
.map(x -> new ResourcePath(linkKey(x.src(), x.dst())))
.map(x -> ResourcePath.discrete(linkKey(x.src(), x.dst())))
.map(resourceService::getAvailableResources)
.map(x -> Iterables.filter(x, r -> r.last() instanceof IndexedLambda))
.map(x -> Iterables.transform(x, r -> (IndexedLambda) r.last()))
......
......@@ -75,12 +75,12 @@ final class ResourceDeviceListener implements DeviceListener {
}
private void registerPortResource(Device device, Port port) {
ResourcePath parent = new ResourcePath(device.id());
ResourcePath parent = ResourcePath.discrete(device.id());
executor.submit(() -> adminService.registerResources(parent, port.number()));
}
private void unregisterPortResource(Device device, Port port) {
ResourcePath parent = new ResourcePath(device.id());
ResourcePath parent = ResourcePath.discrete(device.id());
executor.submit(() -> adminService.unregisterResources(parent, port.number()));
}
}
......
......@@ -87,7 +87,7 @@ final class ResourceLinkListener implements LinkListener {
LinkKey linkKey = LinkKey.linkKey(link);
adminService.registerResources(ResourcePath.ROOT, linkKey);
ResourcePath linkPath = new ResourcePath(linkKey);
ResourcePath linkPath = ResourcePath.discrete(linkKey);
// register VLAN IDs against the link
if (isEnabled(link, this::isVlanEnabled)) {
adminService.registerResources(linkPath, ENTIRE_VLAN_IDS);
......
......@@ -231,7 +231,7 @@ public class ObjectiveTrackerTest {
@Test
public void testResourceEvent() throws Exception {
ResourceEvent event = new ResourceEvent(RESOURCE_ADDED,
new ResourcePath(linkKey(link("a", 1, "b", 1))));
ResourcePath.discrete(linkKey(link("a", 1, "b", 1))));
resourceListener.event(event);
assertThat(
......
......@@ -416,6 +416,8 @@ public final class KryoNamespaces {
BandwidthResourceAllocation.class,
LambdaResourceAllocation.class,
ResourcePath.class,
ResourcePath.Discrete.class,
ResourcePath.Continuous.class,
ResourceAllocation.class,
// Constraints
LambdaConstraint.class,
......
......@@ -373,13 +373,13 @@ public class KryoSerializerTest {
@Test
public void testResourcePath() {
testSerializedEquals(new ResourcePath(LinkKey.linkKey(CP1, CP2), VLAN1));
testSerializedEquals(ResourcePath.discrete(LinkKey.linkKey(CP1, CP2), VLAN1));
}
@Test
public void testResourceAllocation() {
testSerializedEquals(new org.onosproject.net.newresource.ResourceAllocation(
new ResourcePath(LinkKey.linkKey(CP1, CP2), VLAN1),
ResourcePath.discrete(LinkKey.linkKey(CP1, CP2), VLAN1),
IntentId.valueOf(30)));
}
......