JCLOUDS-607: ComputeService.createNodesInGroup throws NPE on FloatingIPApi.create()

This commit is contained in:
Christopher Dancy 2014-06-30 16:01:43 -04:00 committed by Ignasi Barrera
parent f52a264b87
commit 7641bd61cd
6 changed files with 250 additions and 41 deletions

View File

@ -19,8 +19,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING; import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -32,6 +33,7 @@ import org.jclouds.compute.domain.NodeMetadataBuilder;
import org.jclouds.compute.reference.ComputeServiceConstants; import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.openstack.nova.v2_0.NovaApi; import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.compute.options.NodeAndNovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.domain.FloatingIP; import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneAndId; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneAndId;
import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi; import org.jclouds.openstack.nova.v2_0.extensions.FloatingIPApi;
@ -39,6 +41,7 @@ import org.jclouds.rest.InsufficientResourcesException;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -51,7 +54,7 @@ import com.google.common.collect.Lists;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AllocateAndAddFloatingIpToNode implements public class AllocateAndAddFloatingIpToNode implements
Function<AtomicReference<NodeMetadata>, AtomicReference<NodeMetadata>> { Function<AtomicReference<NodeAndNovaTemplateOptions>, AtomicReference<NodeMetadata>> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
@ -70,39 +73,77 @@ public class AllocateAndAddFloatingIpToNode implements
} }
@Override @Override
public AtomicReference<NodeMetadata> apply(AtomicReference<NodeMetadata> input) { public AtomicReference<NodeMetadata> apply(AtomicReference<NodeAndNovaTemplateOptions> input) {
checkState(nodeRunning.apply(input), "node never achieved state running %s", input.get()); checkState(nodeRunning.apply(input.get().getNodeMetadata()), "node never achieved state running %s", input.get().getNodeMetadata());
NodeMetadata node = input.get(); NodeMetadata node = input.get().getNodeMetadata().get();
// node's location is a host // node's location is a host
String zoneId = node.getLocation().getParent().getId(); String zoneId = node.getLocation().getParent().getId();
FloatingIPApi floatingIpApi = novaApi.getFloatingIPExtensionForZone(zoneId).get(); FloatingIPApi floatingIpApi = novaApi.getFloatingIPExtensionForZone(zoneId).get();
Optional<Set<String>> poolNames = input.get().getNovaTemplateOptions().get().getFloatingIpPoolNames();
Optional<FloatingIP> ip = allocateFloatingIPForNode(floatingIpApi, poolNames, node.getId());
if (!ip.isPresent()) {
throw new InsufficientResourcesException("Failed to allocate a FloatingIP for node(" + node.getId() + ")");
}
logger.debug(">> adding floatingIp(%s) to node(%s)", ip.get().getIp(), node.getId());
floatingIpApi.addToServer(ip.get().getIp(), node.getProviderId());
input.get().getNodeMetadata().set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.get().getIp())).build());
floatingIpCache.invalidate(ZoneAndId.fromSlashEncoded(node.getId()));
return input.get().getNodeMetadata();
}
/**
* Allocates a FloatingIP for a given Node
*
* @param floatingIpApi FloatingIPApi to create or query for a valid FloatingIP
* @param poolNames optional set of pool names from which we will attempt to allocate an IP from. Most cases this is null
* @param nodeID optional id of the Node we are trying to allocate a FloatingIP for. Used here only for logging purposes
* @return Optional<FloatingIP>
*/
private Optional<FloatingIP> allocateFloatingIPForNode(FloatingIPApi floatingIpApi, Optional<Set<String>> poolNames, String nodeID) {
FloatingIP ip = null; FloatingIP ip = null;
try {
logger.debug(">> allocating or reassigning floating ip for node(%s)", node.getId());
ip = floatingIpApi.create();
} catch (InsufficientResourcesException e) {
logger.trace("<< [%s] allocating a new floating ip for node(%s)", e.getMessage(), node.getId());
logger.trace(">> searching for existing, unassigned floating ip for node(%s)", node.getId());
ArrayList<FloatingIP> unassignedIps = Lists.newArrayList(Iterables.filter(floatingIpApi.list(),
new Predicate<FloatingIP>() {
@Override // 1.) Attempt to allocate from optionally passed poolNames
public boolean apply(FloatingIP arg0) { if (poolNames.isPresent()) {
return arg0.getFixedIp() == null; for (String poolName : poolNames.get()){
} try {
logger.debug(">> allocating floating IP from pool %s for node(%s)", poolName, nodeID);
})); ip = floatingIpApi.allocateFromPool(poolName);
// try to prevent multiple parallel launches from choosing the same ip. if (ip != null)
Collections.shuffle(unassignedIps); return Optional.of(ip);
ip = Iterables.getLast(unassignedIps); } catch (InsufficientResourcesException ire){
logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ire.getMessage(), poolName, nodeID);
}
}
} }
logger.debug(">> adding floatingIp(%s) to node(%s)", ip.getIp(), node.getId());
floatingIpApi.addToServer(ip.getIp(), node.getProviderId()); // 2.) Attempt to allocate, if necessary, via 'create()' call
input.set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.getIp())).build()); try {
floatingIpCache.invalidate(ZoneAndId.fromSlashEncoded(node.getId())); logger.debug(">> creating floating IP for node(%s)", nodeID);
return input; ip = floatingIpApi.create();
if(ip != null)
return Optional.of(ip);
} catch (InsufficientResourcesException ire){
logger.trace("<< [%s] failed to create floating IP for node(%s)", ire.getMessage(), nodeID);
}
// 3.) If no IP was found make final attempt by searching through list of available IP's
logger.trace(">> searching for existing, unassigned floating IP for node(%s)", nodeID);
List<FloatingIP> unassignedIps = Lists.newArrayList(Iterables.filter(floatingIpApi.list(),
new Predicate<FloatingIP>() {
@Override
public boolean apply(FloatingIP arg0) {
return arg0.getFixedIp() == null;
}
}));
// try to prevent multiple parallel launches from choosing the same ip.
Collections.shuffle(unassignedIps);
ip = Iterables.getLast(unassignedIps);
return Optional.fromNullable(ip);
} }
@Override @Override

View File

@ -0,0 +1,54 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.jclouds.openstack.nova.v2_0.compute.options;
import java.util.concurrent.atomic.AtomicReference;
import org.jclouds.compute.domain.NodeMetadata;
import com.google.common.util.concurrent.Atomics;
/**
* Simple data-structure for holding a NodeMetadata object along with a
* corresponding NovaTemplateOptions object.
*/
public class NodeAndNovaTemplateOptions {
private final AtomicReference<NodeMetadata> nodeMetadata;
private final AtomicReference<NovaTemplateOptions> novaTemplateOptions;
protected NodeAndNovaTemplateOptions(AtomicReference<NodeMetadata> nodeMetadata, AtomicReference<NovaTemplateOptions> novaTemplateOptions){
this.nodeMetadata = nodeMetadata;
this.novaTemplateOptions = novaTemplateOptions;
}
public AtomicReference<NodeMetadata> getNodeMetadata() {
return nodeMetadata;
}
public AtomicReference<NovaTemplateOptions> getNovaTemplateOptions() {
return novaTemplateOptions;
}
public static NodeAndNovaTemplateOptions newReference(AtomicReference<NodeMetadata> node, AtomicReference<NovaTemplateOptions> options){
return new NodeAndNovaTemplateOptions(node, options);
}
public static AtomicReference<NodeAndNovaTemplateOptions> newAtomicReference(AtomicReference<NodeMetadata> node, AtomicReference<NovaTemplateOptions> options){
return Atomics.newReference(NodeAndNovaTemplateOptions.newReference(node, options));
}
}

View File

@ -66,6 +66,8 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
if (to instanceof NovaTemplateOptions) { if (to instanceof NovaTemplateOptions) {
NovaTemplateOptions eTo = NovaTemplateOptions.class.cast(to); NovaTemplateOptions eTo = NovaTemplateOptions.class.cast(to);
eTo.autoAssignFloatingIp(shouldAutoAssignFloatingIp()); eTo.autoAssignFloatingIp(shouldAutoAssignFloatingIp());
if (getFloatingIpPoolNames().isPresent())
eTo.floatingIpPoolNames(getFloatingIpPoolNames().get());
if (getSecurityGroupNames().isPresent()) if (getSecurityGroupNames().isPresent())
eTo.securityGroupNames(getSecurityGroupNames().get()); eTo.securityGroupNames(getSecurityGroupNames().get());
eTo.generateKeyPair(shouldGenerateKeyPair()); eTo.generateKeyPair(shouldGenerateKeyPair());
@ -83,6 +85,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
} }
protected boolean autoAssignFloatingIp = false; protected boolean autoAssignFloatingIp = false;
protected Optional<Set<String>> floatingIpPoolNames = Optional.absent();
protected Optional<Set<String>> securityGroupNames = Optional.absent(); protected Optional<Set<String>> securityGroupNames = Optional.absent();
protected boolean generateKeyPair = false; protected boolean generateKeyPair = false;
protected String keyPairName; protected String keyPairName;
@ -99,6 +102,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
return false; return false;
NovaTemplateOptions that = NovaTemplateOptions.class.cast(o); NovaTemplateOptions that = NovaTemplateOptions.class.cast(o);
return super.equals(that) && equal(this.autoAssignFloatingIp, that.autoAssignFloatingIp) return super.equals(that) && equal(this.autoAssignFloatingIp, that.autoAssignFloatingIp)
&& equal(this.floatingIpPoolNames, that.floatingIpPoolNames)
&& equal(this.securityGroupNames, that.securityGroupNames) && equal(this.securityGroupNames, that.securityGroupNames)
&& equal(this.generateKeyPair, that.generateKeyPair) && equal(this.generateKeyPair, that.generateKeyPair)
&& equal(this.keyPairName, that.keyPairName) && equal(this.keyPairName, that.keyPairName)
@ -110,7 +114,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, securityGroupNames, generateKeyPair, keyPairName, userData, diskConfig, configDrive, novaNetworks); return Objects.hashCode(super.hashCode(), autoAssignFloatingIp, floatingIpPoolNames, securityGroupNames, generateKeyPair, keyPairName, userData, diskConfig, configDrive, novaNetworks);
} }
@Override @Override
@ -118,6 +122,8 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
ToStringHelper toString = super.string(); ToStringHelper toString = super.string();
if (!autoAssignFloatingIp) if (!autoAssignFloatingIp)
toString.add("autoAssignFloatingIp", autoAssignFloatingIp); toString.add("autoAssignFloatingIp", autoAssignFloatingIp);
if (floatingIpPoolNames.isPresent())
toString.add("floatingIpPoolNames", floatingIpPoolNames.get());
if (securityGroupNames.isPresent()) if (securityGroupNames.isPresent())
toString.add("securityGroupNames", securityGroupNames.get()); toString.add("securityGroupNames", securityGroupNames.get());
if (generateKeyPair) if (generateKeyPair)
@ -133,13 +139,30 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
public static final NovaTemplateOptions NONE = new NovaTemplateOptions(); public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
/** /**
* @see #shouldAutoAssignFloatingIp() * @see #getFloatingIpPoolNames()
*/ */
public NovaTemplateOptions autoAssignFloatingIp(boolean enable) { public NovaTemplateOptions autoAssignFloatingIp(boolean enable) {
this.autoAssignFloatingIp = enable; this.autoAssignFloatingIp = enable;
return this; return this;
} }
/**
* @see #getFloatingIpPoolNames()
*/
public NovaTemplateOptions floatingIpPoolNames(String... floatingIpPoolNames) {
return floatingIpPoolNames(ImmutableSet.copyOf(checkNotNull(floatingIpPoolNames, "floatingIpPoolNames")));
}
/**
* @see #getFloatingIpPoolNames()
*/
public NovaTemplateOptions floatingIpPoolNames(Iterable<String> floatingIpPoolNames) {
for (String groupName : checkNotNull(floatingIpPoolNames, "floatingIpPoolNames"))
checkNotNull(emptyToNull(groupName), "all floating-ip-pool-names must be non-empty");
this.floatingIpPoolNames = Optional.<Set<String>> of(ImmutableSet.copyOf(floatingIpPoolNames));
return this;
}
/** /**
* @see #shouldGenerateKeyPair() * @see #shouldGenerateKeyPair()
*/ */
@ -186,6 +209,18 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
return autoAssignFloatingIp; return autoAssignFloatingIp;
} }
/**
* The floating IP pool name(s) to use when allocating a FloatingIP. Applicable
* only if #shouldAutoAssignFloatingIp() returns true. If not set will attempt to
* use whatever FloatingIP(s) can be found regardless of which pool they originated
* from
*
* @return floating-ip-pool names to use
*/
public Optional<Set<String>> getFloatingIpPoolNames() {
return floatingIpPoolNames;
}
/** /**
* Specifies the keypair used to run instances with * Specifies the keypair used to run instances with
* @return the keypair to be used * @return the keypair to be used
@ -250,6 +285,22 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
return new NovaTemplateOptions().autoAssignFloatingIp(enable); return new NovaTemplateOptions().autoAssignFloatingIp(enable);
} }
/**
* @see #getFloatingIpPoolNames()
*/
public NovaTemplateOptions floatingIpPoolNames(String... floatingIpPoolNames) {
NovaTemplateOptions options = new NovaTemplateOptions();
return NovaTemplateOptions.class.cast(options.floatingIpPoolNames(floatingIpPoolNames));
}
/**
* @see #getFloatingIpPoolNames()
*/
public NovaTemplateOptions floatingIpPoolNames(Iterable<String> floatingIpPoolNames) {
NovaTemplateOptions options = new NovaTemplateOptions();
return NovaTemplateOptions.class.cast(options.floatingIpPoolNames(floatingIpPoolNames));
}
/** /**
* @see NovaTemplateOptions#shouldGenerateKeyPair() * @see NovaTemplateOptions#shouldGenerateKeyPair()
*/ */

View File

@ -42,16 +42,19 @@ import org.jclouds.compute.strategy.ListNodesStrategy;
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet; import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
import org.jclouds.openstack.nova.v2_0.NovaApi; import org.jclouds.openstack.nova.v2_0.NovaApi;
import org.jclouds.openstack.nova.v2_0.compute.functions.AllocateAndAddFloatingIpToNode; import org.jclouds.openstack.nova.v2_0.compute.functions.AllocateAndAddFloatingIpToNode;
import org.jclouds.openstack.nova.v2_0.compute.options.NodeAndNovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions; import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.domain.KeyPair; import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
import org.jclouds.openstack.nova.v2_0.domain.zonescoped.SecurityGroupInZone; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.SecurityGroupInZone;
import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneAndName; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneAndName;
import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneSecurityGroupNameAndPorts; import org.jclouds.openstack.nova.v2_0.domain.zonescoped.ZoneSecurityGroupNameAndPorts;
import com.google.common.base.Function;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.Atomics;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
@ -153,13 +156,21 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
final String name, Template template) { final String name, Template template) {
ListenableFuture<AtomicReference<NodeMetadata>> future = super.createNodeInGroupWithNameAndTemplate(group, name, template); ListenableFuture<AtomicReference<NodeMetadata>> future = super.createNodeInGroupWithNameAndTemplate(group, name, template);
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(template.getOptions()); final NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(template.getOptions());
if (templateOptions.shouldAutoAssignFloatingIp()) { if (templateOptions.shouldAutoAssignFloatingIp()) {
return Futures.transform(future, createAndAddFloatingIpToNode, userExecutor);
ListenableFuture<AtomicReference<NodeAndNovaTemplateOptions>> nodeAndNovaTemplateOptions = Futures.transform(future,
new Function<AtomicReference<NodeMetadata>, AtomicReference<NodeAndNovaTemplateOptions>>(){
@Override
public AtomicReference<NodeAndNovaTemplateOptions> apply(AtomicReference<NodeMetadata> input) {
return NodeAndNovaTemplateOptions.newAtomicReference(input, Atomics.newReference(templateOptions));
}
}
);
return Futures.transform(nodeAndNovaTemplateOptions, createAndAddFloatingIpToNode, userExecutor);
} else { } else {
return future; return future;
} }
} }
} }

View File

@ -31,6 +31,8 @@ import org.jclouds.domain.LocationScope;
import org.jclouds.domain.LoginCredentials; import org.jclouds.domain.LoginCredentials;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.openstack.nova.v2_0.compute.options.NodeAndNovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaComputeServiceExpectTest; import org.jclouds.openstack.nova.v2_0.internal.BaseNovaComputeServiceExpectTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -55,6 +57,7 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
final NodeMetadata node = new NodeMetadataBuilder().id("az-1.region-a.geo-1/71592").providerId("71592").location( final NodeMetadata node = new NodeMetadataBuilder().id("az-1.region-a.geo-1/71592").providerId("71592").location(
host).name("Server 71592").status(Status.RUNNING).privateAddresses(ImmutableSet.of("10.4.27.237")) host).name("Server 71592").status(Status.RUNNING).privateAddresses(ImmutableSet.of("10.4.27.237"))
.credentials(LoginCredentials.builder().password("foo").build()).build(); .credentials(LoginCredentials.builder().password("foo").build()).build();
final NovaTemplateOptions options = NovaTemplateOptions.Builder.autoAssignFloatingIp(false);
HttpRequest createFloatingIP = HttpRequest.builder().method("POST").endpoint( HttpRequest createFloatingIP = HttpRequest.builder().method("POST").endpoint(
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers( URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers(
@ -77,9 +80,13 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
.getInstance(AllocateAndAddFloatingIpToNode.class); .getInstance(AllocateAndAddFloatingIpToNode.class);
AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node); AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
fn.apply(nodeRef); AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);
AtomicReference<NodeAndNovaTemplateOptions> nodeNovaRef = NodeAndNovaTemplateOptions.newAtomicReference(nodeRef, optionsRef);
fn.apply(nodeNovaRef);
NodeMetadata node1 = nodeRef.get(); NodeMetadata node1 = nodeRef.get();
assertNotNull(node1); assertNotNull(node1);
assertNotNull(optionsRef.get());
assertEquals(node1.getPublicAddresses(), ImmutableSet.of("10.0.0.3")); assertEquals(node1.getPublicAddresses(), ImmutableSet.of("10.0.0.3"));
assertEquals(node1.getCredentials(), node.getCredentials()); assertEquals(node1.getCredentials(), node.getCredentials());
@ -96,7 +103,7 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
return addFloatingIPRequest; return addFloatingIPRequest;
} }
public void testAllocateWhenAllocationFailsLookupUnusedIpAddToServerAndUpdatesNodeMetadata() throws Exception { public void testAllocateWhenAllocationFailsOn400LookupUnusedIpAddToServerAndUpdatesNodeMetadata() throws Exception {
HttpResponse createFloatingIPResponse = HttpResponse HttpResponse createFloatingIPResponse = HttpResponse
.builder() .builder()
.statusCode(400) .statusCode(400)
@ -124,10 +131,51 @@ public class AllocateAndAddFloatingIpToNodeExpectTest extends BaseNovaComputeSer
.getInstance(AllocateAndAddFloatingIpToNode.class); .getInstance(AllocateAndAddFloatingIpToNode.class);
AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node); AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
fn.apply(nodeRef); AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);
AtomicReference<NodeAndNovaTemplateOptions> nodeNovaRef = NodeAndNovaTemplateOptions.newAtomicReference(nodeRef, optionsRef);
fn.apply(nodeNovaRef);
NodeMetadata node1 = nodeRef.get(); NodeMetadata node1 = nodeRef.get();
assertNotNull(node1); assertNotNull(node1);
assertNotNull(optionsRef.get());
assertEquals(node1.getPublicAddresses(), ImmutableSet.of("10.0.0.5")); assertEquals(node1.getPublicAddresses(), ImmutableSet.of("10.0.0.5"));
}
public void testAllocateWhenAllocationFailsOn404LookupUnusedIpAddToServerAndUpdatesNodeMetadata() throws Exception {
HttpResponse createFloatingIPResponse = HttpResponse
.builder()
.statusCode(404)
.payload(
payloadFromStringWithContentType(
"{\"badRequest\": {\"message\": \"AddressLimitExceeded: Address quota exceeded. You cannot create any more addresses\", \"code\": 404}}",
"application/json")).build();
HttpRequest list = HttpRequest.builder().method("GET").endpoint(
URI.create("https://az-1.region-a.geo-1.compute.hpcloudsvc.com/v1.1/3456/os-floating-ips")).headers(
ImmutableMultimap.<String, String> builder().put("Accept", "application/json").put("X-Auth-Token",
authToken).build()).build();
HttpResponse listResponseForUnassigned = HttpResponse.builder().statusCode(200).payload(
payloadFromResource("/floatingip_list.json")).build();
HttpRequest addFloatingIPRequest = addFloatingIPForAddress("10.0.0.5");
AllocateAndAddFloatingIpToNode fn = requestsSendResponses(
ImmutableMap.<HttpRequest, HttpResponse> builder().put(keystoneAuthWithUsernameAndPasswordAndTenantName,
responseWithKeystoneAccess).put(extensionsOfNovaRequest, extensionsOfNovaResponse).put(
createFloatingIP, createFloatingIPResponse)
.put(addFloatingIPRequest, addFloatingIPResponse).put(list,
listResponseForUnassigned).build()).getContext().utils().injector()
.getInstance(AllocateAndAddFloatingIpToNode.class);
AtomicReference<NodeMetadata> nodeRef = Atomics.newReference(node);
AtomicReference<NovaTemplateOptions> optionsRef = Atomics.newReference(options);
AtomicReference<NodeAndNovaTemplateOptions> nodeNovaRef = NodeAndNovaTemplateOptions.newAtomicReference(nodeRef, optionsRef);
fn.apply(nodeNovaRef);
NodeMetadata node1 = nodeRef.get();
assertNotNull(node1);
assertNotNull(optionsRef.get());
assertEquals(node1.getPublicAddresses(), ImmutableSet.of("10.0.0.5"));
} }
} }

View File

@ -29,6 +29,10 @@ public class InsufficientResourcesException extends RuntimeException {
super(); super();
} }
public InsufficientResourcesException(String arg0) {
super(arg0);
}
public InsufficientResourcesException(String arg0, Throwable arg1) { public InsufficientResourcesException(String arg0, Throwable arg1) {
super(arg0, arg1); super(arg0, arg1);
} }