Ray Milkey

ONOS-423 - Throw a specific exception when resources are exhausted

Create an exception to throw when no resources are available
Simple, Hazelcast and Distributed link resource stores throw ResourceAllocationException
Unit tests for successful and unsuccessful bandwidth and lambda allocations

Change-Id: If062d10d2233935dd59efabfa5f37a446e275a5b
/*
* 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.resource;
/**
* Exception thrown for resource allocation errors.
*/
public class ResourceAllocationException extends ResourceException {
public ResourceAllocationException() {
super();
}
public ResourceAllocationException(String message) {
super(message);
}
public ResourceAllocationException(String message, Throwable cause) {
super(message, cause);
}
}
/*
* 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.resource;
/**
* Represents a resource related error.
*/
public class ResourceException extends RuntimeException {
/**
* Constructs an exception with no message and no underlying cause.
*/
public ResourceException() {
}
/**
* Constructs an exception with the specified message.
*
* @param message the message describing the specific nature of the error
*/
public ResourceException(String message) {
super(message);
}
/**
* Constructs an exception with the specified message and the underlying cause.
*
* @param message the message describing the specific nature of the error
* @param cause the underlying cause of this error
*/
public ResourceException(String message, Throwable cause) {
super(message, cause);
}
}
/*
* Copyright 2014 Open Networking Laboratory
* Copyright 2014-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.
......@@ -29,6 +29,7 @@ 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.util.PositionalParameterStringFormatter;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
......@@ -42,6 +43,7 @@ import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceEvent;
import org.onosproject.net.resource.LinkResourceStore;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceAllocationException;
import org.onosproject.net.resource.ResourceType;
import org.onosproject.store.serializers.KryoSerializer;
import org.onosproject.store.serializers.StoreSerializer;
......@@ -384,19 +386,26 @@ public class DistributedLinkResourceStore implements LinkResourceStore {
BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
double bwLeft = bw.bandwidth().toDouble();
bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
if (bwLeft < 0) {
checkState(bwLeft >= 0,
"There's no Bandwidth left on %s. %s",
link, bwLeft);
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate bandwidth for link {} "
+ " requested amount is {} current allocation is {}",
link,
bwReq.bandwidth().toDouble(),
bw));
}
} else if (req instanceof LambdaResourceAllocation) {
final LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
// check if allocation should be accepted
if (!avail.contains(req)) {
// requested lambda was not available
checkState(avail.contains(req),
"Allocating %s on %s failed",
req, link);
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate lambda for link {} lamdba is {}",
link,
lambdaAllocation.lambda().toInt()));
}
}
}
......
/*
* Copyright 2014 Open Networking Laboratory
* Copyright 2014-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.
......@@ -30,6 +30,7 @@ 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.util.PositionalParameterStringFormatter;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
......@@ -43,6 +44,7 @@ import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceEvent;
import org.onosproject.net.resource.LinkResourceStore;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceAllocationException;
import org.onosproject.net.resource.ResourceType;
import org.onosproject.store.StoreDelegate;
import org.onosproject.store.hz.AbstractHazelcastStore;
......@@ -330,20 +332,27 @@ public class HazelcastLinkResourceStore
}
BandwidthResourceAllocation bw = (BandwidthResourceAllocation) avail.iterator().next();
double bwLeft = bw.bandwidth().toDouble();
bwLeft -= ((BandwidthResourceAllocation) req).bandwidth().toDouble();
BandwidthResourceAllocation bwReq = ((BandwidthResourceAllocation) req);
bwLeft -= bwReq.bandwidth().toDouble();
if (bwLeft < 0) {
checkState(bwLeft >= 0,
"There's no Bandwidth left on %s. %s",
link, bwLeft);
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate bandwidth for link {} "
+ " requested amount is {} current allocation is {}",
link,
bwReq.bandwidth().toDouble(),
bw));
}
} else if (req instanceof LambdaResourceAllocation) {
LambdaResourceAllocation lambdaAllocation = (LambdaResourceAllocation) req;
// check if allocation should be accepted
if (!avail.contains(req)) {
// requested lambda was not available
checkState(avail.contains(req),
"Allocating %s on %s failed",
req, link);
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate lambda for link {} lamdba is {}",
link,
lambdaAllocation.lambda().toInt()));
}
}
}
......
/*
* Copyright 2014 Open Networking Laboratory
* Copyright 2014-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.
......@@ -27,17 +27,25 @@ import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.Link;
import org.onosproject.net.intent.IntentId;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.resource.Bandwidth;
import org.onosproject.net.resource.BandwidthResourceAllocation;
import org.onosproject.net.resource.DefaultLinkResourceAllocations;
import org.onosproject.net.resource.DefaultLinkResourceRequest;
import org.onosproject.net.resource.Lambda;
import org.onosproject.net.resource.LambdaResourceAllocation;
import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceRequest;
import org.onosproject.net.resource.LinkResourceStore;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceAllocationException;
import org.onosproject.net.resource.ResourceType;
import org.onosproject.store.hz.StoreService;
import org.onosproject.store.hz.TestStoreManager;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
......@@ -191,4 +199,103 @@ public class HazelcastLinkResourceStoreTest {
}
}
/**
* Tests a successful bandwidth allocation.
*/
@Test
public void testSuccessfulBandwidthAllocation() {
final Link link = newLink("of:1", 1, "of:2", 2);
final LinkResourceRequest request =
DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
ImmutableSet.of(link))
.build();
final ResourceAllocation allocation =
new BandwidthResourceAllocation(Bandwidth.valueOf(900000.0));
final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
final LinkResourceAllocations allocations =
new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
store.allocateResources(allocations);
}
/**
* Tests a unsuccessful bandwidth allocation.
*/
@Test
public void testUnsuccessfulBandwidthAllocation() {
final Link link = newLink("of:1", 1, "of:2", 2);
final LinkResourceRequest request =
DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
ImmutableSet.of(link))
.build();
final ResourceAllocation allocation =
new BandwidthResourceAllocation(Bandwidth.valueOf(9000000.0));
final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
final LinkResourceAllocations allocations =
new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
boolean gotException = false;
try {
store.allocateResources(allocations);
} catch (ResourceAllocationException rae) {
assertEquals(true, rae.getMessage().contains("Unable to allocate bandwidth for link"));
gotException = true;
}
assertEquals(true, gotException);
}
/**
* Tests a successful bandwidth allocation.
*/
@Test
public void testSuccessfulLambdaAllocation() {
final Link link = newLink("of:1", 1, "of:2", 2);
final LinkResourceRequest request =
DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
ImmutableSet.of(link))
.build();
final ResourceAllocation allocation =
new BandwidthResourceAllocation(Bandwidth.valueOf(900000.0));
final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
final LinkResourceAllocations allocations =
new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
store.allocateResources(allocations);
}
/**
* Tests a unsuccessful bandwidth allocation.
*/
@Test
public void testUnsuccessfulLambdaAllocation() {
final Link link = newLink("of:1", 1, "of:2", 2);
final LinkResourceRequest request =
DefaultLinkResourceRequest.builder(IntentId.valueOf(1),
ImmutableSet.of(link))
.build();
final ResourceAllocation allocation =
new LambdaResourceAllocation(Lambda.valueOf(33));
final Set<ResourceAllocation> allocationSet = ImmutableSet.of(allocation);
final LinkResourceAllocations allocations =
new DefaultLinkResourceAllocations(request, ImmutableMap.of(link, allocationSet));
store.allocateResources(allocations);
boolean gotException = false;
try {
store.allocateResources(allocations);
} catch (ResourceAllocationException rae) {
assertEquals(true, rae.getMessage().contains("Unable to allocate lambda for link"));
gotException = true;
}
assertEquals(true, gotException);
}
}
......
/*
* Copyright 2014 Open Networking Laboratory
* Copyright 2014-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.
......@@ -26,6 +26,7 @@ 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.Service;
import org.onlab.util.PositionalParameterStringFormatter;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.Annotations;
import org.onosproject.net.Link;
......@@ -38,6 +39,7 @@ import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceEvent;
import org.onosproject.net.resource.LinkResourceStore;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceAllocationException;
import org.onosproject.net.resource.ResourceType;
import org.slf4j.Logger;
......@@ -143,13 +145,30 @@ public class SimpleLinkResourceStore implements LinkResourceStore {
double requestedBandwidth =
((BandwidthResourceAllocation) res).bandwidth().toDouble();
double newBandwidth = ba.bandwidth().toDouble() - requestedBandwidth;
checkState(newBandwidth >= 0.0);
if (newBandwidth < 0.0) {
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate bandwidth for link {} "
+ "requested amount is {} current allocation is {}",
link,
requestedBandwidth,
ba));
}
freeRes.remove(ba);
freeRes.add(new BandwidthResourceAllocation(
Bandwidth.valueOf(newBandwidth)));
break;
case LAMBDA:
checkState(freeRes.remove(res));
final boolean lambdaAvailable = freeRes.remove(res);
if (!lambdaAvailable) {
int requestedLambda =
((LambdaResourceAllocation) res).lambda().toInt();
throw new ResourceAllocationException(
PositionalParameterStringFormatter.format(
"Unable to allocate lambda for link {} lambda is {}",
link,
requestedLambda));
}
break;
default:
break;
......
/*
* Copyright 2014 Open Networking Laboratory
* Copyright 2014-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.
......@@ -15,6 +15,7 @@
*/
package org.onosproject.store.trivial.impl;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
......@@ -27,15 +28,21 @@ import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.Link;
import org.onosproject.net.intent.IntentId;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.resource.Bandwidth;
import org.onosproject.net.resource.BandwidthResourceAllocation;
import org.onosproject.net.resource.Lambda;
import org.onosproject.net.resource.LambdaResourceAllocation;
import org.onosproject.net.resource.LinkResourceAllocations;
import org.onosproject.net.resource.LinkResourceStore;
import org.onosproject.net.resource.ResourceAllocation;
import org.onosproject.net.resource.ResourceAllocationException;
import org.onosproject.net.resource.ResourceRequest;
import org.onosproject.net.resource.ResourceType;
import com.google.common.collect.ImmutableSet;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
......@@ -63,7 +70,7 @@ public class SimpleLinkResourceStoreTest {
* @param port2 destination port
* @return created {@link Link} object
*/
private Link newLink(String dev1, int port1, String dev2, int port2) {
private static Link newLink(String dev1, int port1, String dev2, int port2) {
Annotations annotations = DefaultAnnotations.builder()
.set(AnnotationKeys.OPTICAL_WAVES, "80")
.set(AnnotationKeys.BANDWIDTH, "1000000")
......@@ -167,4 +174,133 @@ public class SimpleLinkResourceStoreTest {
assertNotNull(res);
assertEquals(80, res.size());
}
public static class MockLinkResourceBandwidthAllocations implements LinkResourceAllocations {
final double allocationAmount;
MockLinkResourceBandwidthAllocations(Double allocationAmount) {
this.allocationAmount = allocationAmount;
}
@Override
public Set<ResourceAllocation> getResourceAllocation(Link link) {
final ResourceAllocation allocation =
new BandwidthResourceAllocation(Bandwidth.valueOf(allocationAmount));
final Set<ResourceAllocation> allocations = new HashSet<>();
allocations.add(allocation);
return allocations;
}
@Override
public IntentId intendId() {
return null;
}
@Override
public Collection<Link> links() {
return ImmutableSet.of(newLink("of:1", 1, "of:2", 2));
}
@Override
public Set<ResourceRequest> resources() {
return null;
}
@Override
public ResourceType type() {
return null;
}
}
public static class MockLinkResourceLambdaAllocations implements LinkResourceAllocations {
final int allocatedLambda;
MockLinkResourceLambdaAllocations(int allocatedLambda) {
this.allocatedLambda = allocatedLambda;
}
@Override
public Set<ResourceAllocation> getResourceAllocation(Link link) {
final ResourceAllocation allocation =
new LambdaResourceAllocation(Lambda.valueOf(allocatedLambda));
final Set<ResourceAllocation> allocations = new HashSet<>();
allocations.add(allocation);
return allocations;
}
@Override
public IntentId intendId() {
return null;
}
@Override
public Collection<Link> links() {
return ImmutableSet.of(newLink("of:1", 1, "of:2", 2));
}
@Override
public Set<ResourceRequest> resources() {
return null;
}
@Override
public ResourceType type() {
return null;
}
}
/**
* Tests a successful bandwidth allocation.
*/
@Test
public void testSuccessfulBandwidthAllocation() {
final LinkResourceAllocations allocations =
new MockLinkResourceBandwidthAllocations(900.0);
store.allocateResources(allocations);
}
/**
* Tests an unsuccessful bandwidth allocation.
*/
@Test
public void testUnsuccessfulBandwidthAllocation() {
final LinkResourceAllocations allocations =
new MockLinkResourceBandwidthAllocations(2000000000.0);
boolean gotException = false;
try {
store.allocateResources(allocations);
} catch (ResourceAllocationException rae) {
assertEquals(true, rae.getMessage().contains("Unable to allocate bandwidth for link"));
gotException = true;
}
assertEquals(true, gotException);
}
/**
* Tests a successful lambda allocation.
*/
@Test
public void testSuccessfulLambdaAllocation() {
final LinkResourceAllocations allocations =
new MockLinkResourceLambdaAllocations(1);
store.allocateResources(allocations);
}
/**
* Tests an unsuccessful lambda allocation.
*/
@Test
public void testUnsuccessfulLambdaAllocation() {
final LinkResourceAllocations allocations =
new MockLinkResourceLambdaAllocations(1);
store.allocateResources(allocations);
boolean gotException = false;
try {
store.allocateResources(allocations);
} catch (ResourceAllocationException rae) {
assertEquals(true, rae.getMessage().contains("Unable to allocate lambda for link"));
gotException = true;
}
assertEquals(true, gotException);
}
}
......
/*
* 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.onlab.util;
/**
* Allows slf4j style formatting of parameters into a string.
*/
public final class PositionalParameterStringFormatter {
/**
* Hide default constructor.
*/
private PositionalParameterStringFormatter() {
}
/**
* Formats a string using slf4j style positional parameter replacement.
* Instances of "{}" in the source string are replaced in order by the
* specified parameter values as strings.
*
* @param source original string to format
* @param parameters list of parameters that will be substituted
* @return formatted string
*/
public static String format(String source, Object... parameters) {
String current = source;
for (Object parameter : parameters) {
if (!current.contains("{}")) {
return current;
}
current = current.replaceFirst("\\{\\}", parameter.toString());
}
return current;
}
}