mirror of https://github.com/apache/jclouds.git
JCLOUDS-607: ComputeService.createNodesInGroup throws NPE on FloatingIPApi.create()
This commit is contained in:
parent
f52a264b87
commit
7641bd61cd
|
@ -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
|
||||||
|
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue