mirror of https://github.com/apache/jclouds.git
[JCLOUDS-1318] fix based on nodeTerminatePredicate
- wait for serer deletion, before deleting the security group - rename cleanupServer to cleanupResources - remove keyPairCache - better usage of tags to remove securityGroups created by jclouds - remove keyPair after the creation of a group - remove test for create unique keypair - openstack nova re-adding support for existing keypair - fix securityGroupApi check - fix other unit tests - remove ServerPredicates as it is now duplicated - remove TemplateOptions.securityGroupNames as deprecated - address commits for ApplyNovaTemplateOptionsCreatesNodesWithGroupEncodedIntoNameAndAddToSet - fix testCreateNodeWhileUserSpecifiesKeyPairAndUserSpecifiedGroups
This commit is contained in:
parent
0bc935dd57
commit
aa11765bee
|
@ -54,7 +54,7 @@ import org.jclouds.compute.strategy.ResumeNodeStrategy;
|
|||
import org.jclouds.compute.strategy.SuspendNodeStrategy;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupResources;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
|
||||
|
@ -65,7 +65,7 @@ import com.google.common.util.concurrent.ListeningExecutorService;
|
|||
|
||||
@Singleton
|
||||
public class NovaComputeService extends BaseComputeService {
|
||||
protected final CleanupServer cleanupServer;
|
||||
protected final CleanupResources cleanupResources;
|
||||
|
||||
@Inject
|
||||
protected NovaComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
|
||||
|
@ -83,7 +83,7 @@ public class NovaComputeService extends BaseComputeService {
|
|||
RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
||||
CleanupServer cleanupServer,
|
||||
CleanupResources cleanupResources,
|
||||
Optional<ImageExtension> imageExtension,
|
||||
Optional<SecurityGroupExtension> securityGroupExtension) {
|
||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
|
||||
|
@ -91,14 +91,14 @@ public class NovaComputeService extends BaseComputeService {
|
|||
startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
|
||||
nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
|
||||
persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
this.cleanupResources = checkNotNull(cleanupResources, "cleanupResources");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
|
||||
for (NodeMetadata deadNode : deadNodes) {
|
||||
cleanupServer.apply(deadNode.getId());
|
||||
cleanupResources.apply(deadNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,11 @@ import static com.google.common.collect.Iterables.contains;
|
|||
import static com.google.common.collect.Iterables.filter;
|
||||
import static com.google.common.collect.Iterables.transform;
|
||||
import static java.lang.String.format;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
|
||||
import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -39,32 +41,30 @@ import org.jclouds.domain.LoginCredentials;
|
|||
import org.jclouds.location.Region;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupResources;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.RemoveFloatingIpFromNodeAndDeallocate;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Flavor;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Image;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.RebootType;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.ServerCreated;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.FlavorInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.ImageInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.ServerInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.predicates.ImagePredicates;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* The adapter used by the NovaComputeServiceContextModule to interface the nova-specific domain
|
||||
|
@ -80,20 +80,23 @@ public class NovaComputeServiceAdapter implements
|
|||
protected final NovaApi novaApi;
|
||||
protected final Supplier<Set<String>> regionIds;
|
||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
protected final CleanupServer cleanupServer;
|
||||
|
||||
private final Predicate<RegionAndId> serverRunningPredicate;
|
||||
private final Predicate<RegionAndId> serverTerminatedPredicate;
|
||||
private final CleanupResources cleanupResources;
|
||||
|
||||
@Inject
|
||||
public NovaComputeServiceAdapter(NovaApi novaApi, @Region Supplier<Set<String>> regionIds,
|
||||
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache, CleanupServer cleanupServer) {
|
||||
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
@Named(TIMEOUT_NODE_RUNNING) Predicate<RegionAndId> serverRunningPredicate,
|
||||
@Named(TIMEOUT_NODE_TERMINATED) Predicate<RegionAndId> serverTerminatedPredicate,
|
||||
CleanupResources cleanupResources) {
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.regionIds = checkNotNull(regionIds, "regionIds");
|
||||
this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate,
|
||||
"removeFloatingIpFromNodeAndDeallocate");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
this.serverRunningPredicate = serverRunningPredicate;
|
||||
this.serverTerminatedPredicate = serverTerminatedPredicate;
|
||||
this.cleanupResources = cleanupResources;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,18 +107,20 @@ public class NovaComputeServiceAdapter implements
|
|||
@Override
|
||||
public NodeAndInitialCredentials<ServerInRegion> createNodeWithGroupEncodedIntoName(String group, String name,
|
||||
Template template) {
|
||||
|
||||
LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
|
||||
final String regionId = template.getLocation().getId();
|
||||
String imageId = template.getImage().getProviderId();
|
||||
String flavorId = template.getHardware().getProviderId();
|
||||
NovaTemplateOptions templateOptions = template.getOptions().as(NovaTemplateOptions.class);
|
||||
|
||||
CreateServerOptions options = new CreateServerOptions();
|
||||
options.metadata(metadataAndTagsAsCommaDelimitedValue(template.getOptions()));
|
||||
if (!templateOptions.getGroups().isEmpty())
|
||||
options.securityGroupNames(templateOptions.getGroups());
|
||||
Map<String, String> metadataAndTagsAsCommaDelimitedValue = metadataAndTagsAsCommaDelimitedValue(template.getOptions());
|
||||
options.metadata(metadataAndTagsAsCommaDelimitedValue);
|
||||
if (!templateOptions.getGroups().isEmpty()) options.securityGroupNames(templateOptions.getGroups());
|
||||
options.userData(templateOptions.getUserData());
|
||||
options.diskConfig(templateOptions.getDiskConfig());
|
||||
options.configDrive(templateOptions.getConfigDrive());
|
||||
options.availabilityZone(templateOptions.getAvailabilityZone());
|
||||
|
||||
if (templateOptions.getNovaNetworks() != null) {
|
||||
options.novaNetworks(templateOptions.getNovaNetworks());
|
||||
}
|
||||
|
@ -123,41 +128,32 @@ public class NovaComputeServiceAdapter implements
|
|||
options.networks(templateOptions.getNetworks());
|
||||
}
|
||||
|
||||
Optional<String> privateKey = Optional.absent();
|
||||
if (templateOptions.getKeyPairName() != null) {
|
||||
options.keyPairName(templateOptions.getKeyPairName());
|
||||
KeyPair keyPair = keyPairCache.getIfPresent(RegionAndName.fromRegionAndName(template.getLocation().getId(), templateOptions.getKeyPairName()));
|
||||
if (keyPair != null && keyPair.getPrivateKey() != null) {
|
||||
privateKey = Optional.of(keyPair.getPrivateKey());
|
||||
credentialsBuilder.privateKey(privateKey.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final String regionId = template.getLocation().getId();
|
||||
String imageId = template.getImage().getProviderId();
|
||||
String flavorId = template.getHardware().getProviderId();
|
||||
|
||||
|
||||
logger.debug(">> creating new server region(%s) name(%s) image(%s) flavor(%s) options(%s)", regionId, name, imageId, flavorId, options);
|
||||
final ServerCreated lightweightServer = novaApi.getServerApi(regionId).create(name, imageId, flavorId, options);
|
||||
if (!retry(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(String serverId) {
|
||||
Server server = novaApi.getServerApi(regionId).get(serverId);
|
||||
return server != null && server.getAddresses() != null && !server.getAddresses().isEmpty();
|
||||
}
|
||||
}, 30 * 60, 1, SECONDS).apply(lightweightServer.getId())) {
|
||||
final String message = format("Server %s was not created within %sms so it will be destroyed.", name, "30 * 60");
|
||||
if (!serverRunningPredicate.apply(RegionAndId.fromRegionAndId(regionId, lightweightServer.getId()))) {
|
||||
final String message = format("Server %s was not created within %sms. The resources created for it will be destroyed", name, "30 * 60");
|
||||
logger.warn(message);
|
||||
destroyNode(RegionAndId.fromRegionAndId(regionId, lightweightServer.getId()).slashEncode());
|
||||
String tagString = metadataAndTagsAsCommaDelimitedValue.get("jclouds_tags");
|
||||
Set<String> tags = Sets.newHashSet(Splitter.on(',').split(tagString));
|
||||
cleanupResources.removeSecurityGroupCreatedByJcloudsAndInvalidateCache(regionId, tags);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
logger.trace("<< server(%s)", lightweightServer.getId());
|
||||
|
||||
Server server = novaApi.getServerApi(regionId).get(lightweightServer.getId());
|
||||
ServerInRegion serverInRegion = new ServerInRegion(server, regionId);
|
||||
if (!privateKey.isPresent() && lightweightServer.getAdminPass().isPresent())
|
||||
|
||||
LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
|
||||
if (templateOptions.getLoginPrivateKey() != null) {
|
||||
credentialsBuilder.privateKey(templateOptions.getLoginPrivateKey());
|
||||
}
|
||||
if (lightweightServer.getAdminPass().isPresent()) {
|
||||
credentialsBuilder.password(lightweightServer.getAdminPass().get());
|
||||
}
|
||||
return new NodeAndInitialCredentials<ServerInRegion>(serverInRegion, serverInRegion.slashEncode(), credentialsBuilder
|
||||
.build());
|
||||
}
|
||||
|
@ -266,7 +262,9 @@ public class NovaComputeServiceAdapter implements
|
|||
|
||||
@Override
|
||||
public void destroyNode(String id) {
|
||||
checkState(cleanupServer.apply(id), "server(%s) still there after deleting!?", id);
|
||||
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
|
||||
novaApi.getServerApi(regionAndId.getRegion()).delete(regionAndId.getId());
|
||||
checkState(serverTerminatedPredicate.apply(regionAndId), "server was not destroyed in the configured timeout");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
*/
|
||||
package org.jclouds.openstack.nova.v2_0.compute.config;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
|
||||
import static org.jclouds.openstack.nova.v2_0.config.NovaProperties.AUTO_ALLOCATE_FLOATING_IPS;
|
||||
import static org.jclouds.openstack.nova.v2_0.config.NovaProperties.AUTO_GENERATE_KEYPAIRS;
|
||||
import static org.jclouds.openstack.nova.v2_0.config.NovaProperties.TIMEOUT_SECURITYGROUP_PRESENT;
|
||||
|
@ -42,15 +45,17 @@ import org.jclouds.compute.domain.SecurityGroup;
|
|||
import org.jclouds.compute.extensions.ImageExtension;
|
||||
import org.jclouds.compute.extensions.SecurityGroupExtension;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.compute.strategy.impl.CreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.functions.IdentityFunction;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.NovaComputeService;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.NovaComputeServiceAdapter;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaImageExtension;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaSecurityGroupExtension;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupResources;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CreateSecurityGroupIfNeeded;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.FlavorInRegionToHardware;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.ImageInRegionToImage;
|
||||
|
@ -58,14 +63,13 @@ import org.jclouds.openstack.nova.v2_0.compute.functions.ImageToOperatingSystem;
|
|||
import org.jclouds.openstack.nova.v2_0.compute.functions.NovaSecurityGroupInRegionToSecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.OrphanedGroupsByRegionId;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.ServerInRegionToNodeMetadata;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.loaders.CreateUniqueKeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.loaders.FindSecurityGroupOrCreate;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.loaders.LoadFloatingIpsForInstance;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.FloatingIP;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server.Status;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.FlavorInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.ImageInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
|
||||
|
@ -142,17 +146,14 @@ public class NovaComputeServiceContextModule extends
|
|||
bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
|
||||
ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
|
||||
|
||||
bind(new TypeLiteral<CacheLoader<RegionAndName, KeyPair>>() {
|
||||
}).to(CreateUniqueKeyPair.class);
|
||||
|
||||
bind(new TypeLiteral<ImageExtension>() {
|
||||
}).to(NovaImageExtension.class);
|
||||
|
||||
bind(new TypeLiteral<SecurityGroupExtension>() {
|
||||
}).to(NovaSecurityGroupExtension.class);
|
||||
|
||||
bind(new TypeLiteral<Function<String, Boolean>>() {
|
||||
}).to(CleanupServer.class);
|
||||
bind(new TypeLiteral<Function<NodeMetadata, Boolean>>() {
|
||||
}).to(CleanupResources.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -164,6 +165,23 @@ public class NovaComputeServiceContextModule extends
|
|||
Key.get(boolean.class, Names.named(AUTO_GENERATE_KEYPAIRS))));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@com.google.inject.name.Named(TIMEOUT_NODE_RUNNING)
|
||||
protected Predicate<RegionAndId> provideServerRunningPredicate(final NovaApi api,
|
||||
ComputeServiceConstants.Timeouts timeouts,
|
||||
ComputeServiceConstants.PollPeriod pollPeriod) {
|
||||
return retry(new ServerInStatusPredicate(api, Status.ACTIVE), timeouts.nodeRunning,
|
||||
pollPeriod.pollInitialPeriod, pollPeriod.pollMaxPeriod);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@com.google.inject.name.Named(TIMEOUT_NODE_TERMINATED)
|
||||
protected Predicate<RegionAndId> provideServerTerminatedPredicate(final NovaApi api, ComputeServiceConstants.Timeouts timeouts,
|
||||
ComputeServiceConstants.PollPeriod pollPeriod) {
|
||||
return retry(new ServerTerminatedPredicate(api), timeouts.nodeTerminated, pollPeriod.pollInitialPeriod,
|
||||
pollPeriod.pollMaxPeriod);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("FLOATINGIP")
|
||||
|
@ -194,13 +212,6 @@ public class NovaComputeServiceContextModule extends
|
|||
return retry(in, msDelay, 100L, MILLISECONDS);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairMap(
|
||||
CacheLoader<RegionAndName, KeyPair> in) {
|
||||
return CacheBuilder.newBuilder().build(in);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
protected final Supplier<Map<String, Location>> createLocationIndexedById(
|
||||
|
@ -226,35 +237,35 @@ public class NovaComputeServiceContextModule extends
|
|||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static final Map<Server.Status, NodeMetadata.Status> toPortableNodeStatus = ImmutableMap
|
||||
.<Server.Status, NodeMetadata.Status> builder()
|
||||
.put(Server.Status.ACTIVE, NodeMetadata.Status.RUNNING)
|
||||
.put(Server.Status.BUILD, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.DELETED, NodeMetadata.Status.TERMINATED)
|
||||
.put(Server.Status.ERROR, NodeMetadata.Status.ERROR)
|
||||
.put(Server.Status.HARD_REBOOT, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.MIGRATING, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.PASSWORD, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.PAUSED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.REBOOT, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.REBUILD, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.RESCUE, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.RESIZE, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.REVERT_RESIZE, NodeMetadata.Status.PENDING)
|
||||
.put(Server.Status.SHELVED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.SHELVED_OFFLOADED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.SHUTOFF, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.SOFT_DELETED, NodeMetadata.Status.TERMINATED)
|
||||
.put(Server.Status.STOPPED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.SUSPENDED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Server.Status.UNKNOWN, NodeMetadata.Status.UNRECOGNIZED)
|
||||
.put(Server.Status.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED)
|
||||
.put(Server.Status.VERIFY_RESIZE, NodeMetadata.Status.PENDING)
|
||||
public static final Map<Status, NodeMetadata.Status> toPortableNodeStatus = ImmutableMap
|
||||
.<Status, NodeMetadata.Status> builder()
|
||||
.put(Status.ACTIVE, NodeMetadata.Status.RUNNING)
|
||||
.put(Status.BUILD, NodeMetadata.Status.PENDING)
|
||||
.put(Status.DELETED, NodeMetadata.Status.TERMINATED)
|
||||
.put(Status.ERROR, NodeMetadata.Status.ERROR)
|
||||
.put(Status.HARD_REBOOT, NodeMetadata.Status.PENDING)
|
||||
.put(Status.MIGRATING, NodeMetadata.Status.PENDING)
|
||||
.put(Status.PASSWORD, NodeMetadata.Status.PENDING)
|
||||
.put(Status.PAUSED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.REBOOT, NodeMetadata.Status.PENDING)
|
||||
.put(Status.REBUILD, NodeMetadata.Status.PENDING)
|
||||
.put(Status.RESCUE, NodeMetadata.Status.PENDING)
|
||||
.put(Status.RESIZE, NodeMetadata.Status.PENDING)
|
||||
.put(Status.REVERT_RESIZE, NodeMetadata.Status.PENDING)
|
||||
.put(Status.SHELVED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.SHELVED_OFFLOADED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.SHUTOFF, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.SOFT_DELETED, NodeMetadata.Status.TERMINATED)
|
||||
.put(Status.STOPPED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.SUSPENDED, NodeMetadata.Status.SUSPENDED)
|
||||
.put(Status.UNKNOWN, NodeMetadata.Status.UNRECOGNIZED)
|
||||
.put(Status.UNRECOGNIZED, NodeMetadata.Status.UNRECOGNIZED)
|
||||
.put(Status.VERIFY_RESIZE, NodeMetadata.Status.PENDING)
|
||||
.build();
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
protected final Map<Server.Status, NodeMetadata.Status> toPortableNodeStatus() {
|
||||
protected final Map<Status, NodeMetadata.Status> toPortableNodeStatus() {
|
||||
return toPortableNodeStatus;
|
||||
}
|
||||
|
||||
|
@ -268,6 +279,46 @@ public class NovaComputeServiceContextModule extends
|
|||
.put(org.jclouds.openstack.nova.v2_0.domain.Image.Status.UNKNOWN, Image.Status.UNRECOGNIZED)
|
||||
.put(org.jclouds.openstack.nova.v2_0.domain.Image.Status.UNRECOGNIZED, Image.Status.UNRECOGNIZED).build();
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
static class ServerInStatusPredicate implements Predicate<RegionAndId> {
|
||||
|
||||
private final NovaApi api;
|
||||
private final Status status;
|
||||
|
||||
public ServerInStatusPredicate(NovaApi api, Status status) {
|
||||
this.api = checkNotNull(api, "api must not be null");
|
||||
this.status = checkNotNull(status, "status must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(RegionAndId regionAndId) {
|
||||
checkNotNull(regionAndId, "regionAndId");
|
||||
Server server = api.getServerApi(regionAndId.getRegion()).get(regionAndId.getId());
|
||||
if (server == null) {
|
||||
throw new IllegalStateException(String.format("Server %s not found.", regionAndId.getId()));
|
||||
}
|
||||
return status.equals(server.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static class ServerTerminatedPredicate implements Predicate<RegionAndId> {
|
||||
|
||||
private final NovaApi api;
|
||||
|
||||
public ServerTerminatedPredicate(NovaApi api) {
|
||||
this.api = checkNotNull(api, "api must not be null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(RegionAndId regionAndId) {
|
||||
checkNotNull(regionAndId, "regionAndId");
|
||||
Server server = api.getServerApi(regionAndId.getRegion()).get(regionAndId.getId());
|
||||
return server == null;
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
protected final Map<org.jclouds.openstack.nova.v2_0.domain.Image.Status, Image.Status> toPortableImageStatus() {
|
||||
|
|
|
@ -64,15 +64,15 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
private final Predicate<AtomicReference<NodeMetadata>> nodeRunning;
|
||||
private final NovaApi novaApi;
|
||||
private final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache;
|
||||
private final CleanupServer cleanupServer;
|
||||
private final CleanupResources cleanupResources;
|
||||
|
||||
@Inject
|
||||
public AllocateAndAddFloatingIpToNode(@Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
|
||||
NovaApi novaApi, @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache, CleanupServer cleanupServer) {
|
||||
NovaApi novaApi, @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache, CleanupResources cleanupResources) {
|
||||
this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning");
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.floatingIpCache = checkNotNull(floatingIpCache, "floatingIpCache");
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
this.cleanupResources = checkNotNull(cleanupResources, "cleanupResources");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -86,7 +86,7 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
|
||||
Optional<FloatingIP> ip = allocateFloatingIPForNode(floatingIpApi, poolNames, node.getId());
|
||||
if (!ip.isPresent()) {
|
||||
cleanupServer.apply(node.getId());
|
||||
cleanupResources.apply(node);
|
||||
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());
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.JCLOUDS_SG_PREFIX;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
|
||||
@Singleton
|
||||
public class CleanupResources implements Function<NodeMetadata, Boolean> {
|
||||
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
protected final NovaApi novaApi;
|
||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap;
|
||||
|
||||
@Inject
|
||||
public CleanupResources(NovaApi novaApi, RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap) {
|
||||
|
||||
this.novaApi = novaApi;
|
||||
this.removeFloatingIpFromNodeAndDeallocate = removeFloatingIpFromNodeAndDeallocate;
|
||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean apply(NodeMetadata node) {
|
||||
final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(node.getId());
|
||||
removeFloatingIpFromNodeifAny(regionAndId);
|
||||
return removeSecurityGroupCreatedByJcloudsAndInvalidateCache(regionAndId.getRegion(), node.getTags());
|
||||
}
|
||||
|
||||
public boolean removeSecurityGroupCreatedByJcloudsAndInvalidateCache(String regionId, Set<String> tags) {
|
||||
String securityGroupIdCreatedByJclouds = getSecurityGroupIdCreatedByJclouds(tags);
|
||||
if (securityGroupIdCreatedByJclouds != null) {
|
||||
SecurityGroup securityGroup = novaApi.getSecurityGroupApi(regionId).get().get(securityGroupIdCreatedByJclouds);
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionId, securityGroup.getName());
|
||||
logger.debug(">> deleting securityGroup(%s)", regionAndName);
|
||||
novaApi.getSecurityGroupApi(regionId).get().delete(securityGroupIdCreatedByJclouds);
|
||||
securityGroupMap.invalidate(regionAndName);
|
||||
logger.debug("<< deleted securityGroup(%s)", regionAndName);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void removeFloatingIpFromNodeifAny(RegionAndId regionAndId) {
|
||||
try {
|
||||
removeFloatingIpFromNodeAndDeallocate.apply(regionAndId);
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn(e, "<< error removing and deallocating ip from node(%s): %s", regionAndId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String getSecurityGroupIdCreatedByJclouds(Set<String> tags) {
|
||||
return FluentIterable.from(tags).filter(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean apply(String input) {
|
||||
return input.startsWith(JCLOUDS_SG_PREFIX);
|
||||
}
|
||||
}).transform(new Function<String, String>() {
|
||||
@Override
|
||||
public String apply(String input) {
|
||||
return input.substring(JCLOUDS_SG_PREFIX.length() + 1);
|
||||
}
|
||||
}).first().orNull();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,123 +0,0 @@
|
|||
/*
|
||||
* 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.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.JCLOUDS_KP;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.JCLOUDS_SG;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.ServerWithSecurityGroups;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
@Singleton
|
||||
public class CleanupServer implements Function<String, Boolean> {
|
||||
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
protected final NovaApi novaApi;
|
||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
|
||||
@Inject
|
||||
public CleanupServer(NovaApi novaApi, RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache) {
|
||||
this.novaApi = novaApi;
|
||||
this.removeFloatingIpFromNodeAndDeallocate = removeFloatingIpFromNodeAndDeallocate;
|
||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean apply(String id) {
|
||||
final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
|
||||
boolean secGroupsPresent = novaApi.getServerWithSecurityGroupsApi(regionAndId.getRegion()).isPresent();
|
||||
Server server;
|
||||
|
||||
if (secGroupsPresent) {
|
||||
server = novaApi.getServerWithSecurityGroupsApi(regionAndId.getRegion()).get()
|
||||
.get(regionAndId.getId());
|
||||
} else {
|
||||
server = novaApi.getServerApi(regionAndId.getRegion()).get(regionAndId.getId());
|
||||
}
|
||||
|
||||
if (novaApi.getFloatingIPApi(regionAndId.getRegion()).isPresent()) {
|
||||
try {
|
||||
removeFloatingIpFromNodeAndDeallocate.apply(regionAndId);
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn(e, "<< error removing and deallocating ip from node(%s): %s", id, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (containsMetadata(server, JCLOUDS_KP)) {
|
||||
if (novaApi.getKeyPairApi(regionAndId.getRegion()).isPresent()) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionAndId.getRegion(), server.getKeyName());
|
||||
logger.debug(">> deleting keypair(%s)", regionAndName);
|
||||
novaApi.getKeyPairApi(regionAndId.getRegion()).get().delete(server.getKeyName());
|
||||
// TODO: test this clear happens
|
||||
keyPairCache.invalidate(regionAndName);
|
||||
logger.debug("<< deleted keypair(%s)", regionAndName);
|
||||
}
|
||||
}
|
||||
|
||||
boolean serverDeleted = novaApi.getServerApi(regionAndId.getRegion()).delete(regionAndId.getId());
|
||||
|
||||
if (containsMetadata(server, JCLOUDS_SG) && secGroupsPresent) {
|
||||
for (final String securityGroupName : ((ServerWithSecurityGroups)server).getSecurityGroupNames()) {
|
||||
for (SecurityGroup securityGroup : novaApi.getSecurityGroupApi(regionAndId.getRegion()).get().list().toList()) {
|
||||
if (securityGroup.getName().equalsIgnoreCase(securityGroupName)) {
|
||||
if (novaApi.getSecurityGroupApi(regionAndId.getRegion()).isPresent()) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionAndId.getRegion(), securityGroup.getName());
|
||||
logger.debug(">> deleting securityGroup(%s)", regionAndName);
|
||||
novaApi.getSecurityGroupApi(regionAndId.getRegion()).get().delete(securityGroup.getId());
|
||||
// TODO: test this clear happens
|
||||
securityGroupMap.invalidate(regionAndName);
|
||||
logger.debug("<< deleted securityGroup(%s)", regionAndName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverDeleted;
|
||||
}
|
||||
|
||||
private boolean containsMetadata(Server server, String key) {
|
||||
if (server == null || server.getMetadata() == null || server.getMetadata().get("jclouds_tags") == null)
|
||||
return false;
|
||||
return server.getMetadata().get("jclouds_tags").contains(key);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* 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.loaders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
@Singleton
|
||||
public class CreateUniqueKeyPair extends CacheLoader<RegionAndName, KeyPair> {
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
protected final NovaApi novaApi;
|
||||
protected final GroupNamingConvention.Factory namingConvention;
|
||||
|
||||
@Inject
|
||||
public CreateUniqueKeyPair(NovaApi novaApi, GroupNamingConvention.Factory namingConvention) {
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair load(RegionAndName regionAndName) {
|
||||
String regionId = checkNotNull(regionAndName, "regionAndName").getRegion();
|
||||
String prefix = regionAndName.getName();
|
||||
|
||||
Optional<? extends KeyPairApi> api = novaApi.getKeyPairApi(regionId);
|
||||
checkArgument(api.isPresent(), "Key pairs are required, but the extension is not available in region %s!",
|
||||
regionId);
|
||||
|
||||
logger.debug(">> creating keyPair region(%s) prefix(%s)", regionId, prefix);
|
||||
|
||||
KeyPair keyPair = null;
|
||||
while (keyPair == null) {
|
||||
try {
|
||||
keyPair = api.get().create(namingConvention.createWithoutPrefix().uniqueNameForGroup(prefix));
|
||||
} catch (IllegalStateException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("<< created keyPair(%s)", keyPair.getName());
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,9 @@ import java.util.Set;
|
|||
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
import org.jclouds.domain.LoginCredentials;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Network;
|
||||
import org.jclouds.openstack.nova.v2_0.options.CreateServerOptions;
|
||||
import org.jclouds.scriptbuilder.domain.Statement;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
@ -65,8 +67,6 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
eTo.autoAssignFloatingIp(shouldAutoAssignFloatingIp());
|
||||
if (getFloatingIpPoolNames().isPresent())
|
||||
eTo.floatingIpPoolNames(getFloatingIpPoolNames().get());
|
||||
if (getSecurityGroupNames().isPresent())
|
||||
eTo.securityGroupNames(getSecurityGroupNames().get());
|
||||
eTo.generateKeyPair(shouldGenerateKeyPair());
|
||||
eTo.keyPairName(getKeyPairName());
|
||||
if (getUserData() != null) {
|
||||
|
@ -134,8 +134,6 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
return toString;
|
||||
}
|
||||
|
||||
public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
|
||||
|
||||
/**
|
||||
* @see #getFloatingIpPoolNames()
|
||||
*/
|
||||
|
@ -177,32 +175,10 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getSecurityGroupNames
|
||||
* @deprecated Use @link {@link TemplateOptions#securityGroups(String...)} instead. To be removed in jclouds 2.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public NovaTemplateOptions securityGroupNames(String... securityGroupNames) {
|
||||
return securityGroupNames(ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getSecurityGroupNames
|
||||
* @deprecated Use {@link TemplateOptions#securityGroups(Iterable)} instead. To be removed in jclouds 2.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public NovaTemplateOptions securityGroupNames(Iterable<String> securityGroupNames) {
|
||||
for (String groupName : checkNotNull(securityGroupNames, "securityGroupNames"))
|
||||
checkNotNull(emptyToNull(groupName), "all security groups must be non-empty");
|
||||
securityGroups(securityGroupNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* <h3>Note</h3>
|
||||
*
|
||||
* This requires that {@link NovaApi#getExtensionForRegion(String)} to return
|
||||
* This requires that {@link NovaApi#getExtensionApi(String)} to return
|
||||
* {@link Optional#isPresent present}
|
||||
*
|
||||
* @return true if auto assignment of a floating ip to each vm is enabled
|
||||
|
@ -242,7 +218,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
/**
|
||||
* <h3>Note</h3>
|
||||
*
|
||||
* This requires that {@link NovaApi#getKeyPairExtensionApi(String)} to return
|
||||
* This requires that {@link NovaApi#getKeyPairApi(String)} to return
|
||||
* {@link Optional#isPresent present}
|
||||
*
|
||||
* @return true if auto generation of keypairs is enabled
|
||||
|
@ -251,18 +227,6 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
return generateKeyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* if unset, generate a default group prefixed with {@link jclouds#} according
|
||||
* to {@link #getInboundPorts()}
|
||||
*
|
||||
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getSecurityGroupNames
|
||||
* @deprecated Use {@link TemplateOptions#getGroups()} instead. To be removed in jclouds 2.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public Optional<Set<String>> getSecurityGroupNames() {
|
||||
return getGroups().isEmpty() ? Optional.<Set<String>>absent() : Optional.of(getGroups());
|
||||
}
|
||||
|
||||
public byte[] getUserData() {
|
||||
return userData;
|
||||
}
|
||||
|
@ -334,22 +298,6 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
|||
return new NovaTemplateOptions().keyPairName(keyPairName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getSecurityGroupNames
|
||||
*/
|
||||
public static NovaTemplateOptions securityGroupNames(String... groupNames) {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
return NovaTemplateOptions.class.cast(options.securityGroupNames(groupNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.jclouds.openstack.nova.v2_0.options.CreateServerOptions#getSecurityGroupNames
|
||||
*/
|
||||
public static NovaTemplateOptions securityGroupNames(Iterable<String> groupNames) {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
return NovaTemplateOptions.class.cast(options.securityGroupNames(groupNames));
|
||||
}
|
||||
|
||||
// methods that only facilitate returning the correct object type
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.jclouds.openstack.nova.v2_0.compute.strategy;
|
|||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.ssh.SshKeys.fingerprintPrivateKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -35,7 +34,6 @@ import org.jclouds.compute.config.CustomizationResponse;
|
|||
import org.jclouds.compute.domain.NodeMetadata;
|
||||
import org.jclouds.compute.domain.Template;
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.compute.strategy.CreateNodeWithGroupEncodedIntoName;
|
||||
import org.jclouds.compute.strategy.CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap;
|
||||
import org.jclouds.compute.strategy.ListNodesStrategy;
|
||||
|
@ -50,12 +48,14 @@ import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionSecurityGroupNa
|
|||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
@ -64,12 +64,10 @@ import com.google.common.util.concurrent.ListeningExecutorService;
|
|||
public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends
|
||||
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
|
||||
|
||||
public static final String JCLOUDS_SG = "jclouds_securityGroup";
|
||||
public static final String JCLOUDS_KP = "jclouds_keyPair";
|
||||
public static final String JCLOUDS_SG_PREFIX = "jclouds_sg";
|
||||
|
||||
private final AllocateAndAddFloatingIpToNode createAndAddFloatingIpToNode;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupCache;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
protected final NovaApi novaApi;
|
||||
|
||||
@Inject
|
||||
|
@ -80,12 +78,10 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
CustomizeNodeAndAddToGoodMapOrPutExceptionIntoBadMap.Factory customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
||||
AllocateAndAddFloatingIpToNode createAndAddFloatingIpToNode,
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupCache,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache, NovaApi novaApi) {
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupCache, NovaApi novaApi) {
|
||||
super(addNodeWithTagStrategy, listNodesStrategy, namingConvention, userExecutor,
|
||||
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
|
||||
this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.createAndAddFloatingIpToNode = checkNotNull(createAndAddFloatingIpToNode,
|
||||
"createAndAddFloatingIpToNode");
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
|
@ -95,63 +91,78 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
public Map<?, ListenableFuture<Void>> execute(String group, int count, Template template, Set<NodeMetadata> goodNodes,
|
||||
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
|
||||
|
||||
Template mutableTemplate = template.clone();
|
||||
|
||||
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions());
|
||||
|
||||
assert template.getOptions().equals(templateOptions) : "options didn't clone properly";
|
||||
|
||||
String region = mutableTemplate.getLocation().getId();
|
||||
ImmutableList.Builder<String> tagsBuilder = ImmutableList.builder();
|
||||
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(template.getOptions());
|
||||
String region = template.getLocation().getId();
|
||||
|
||||
if (templateOptions.shouldAutoAssignFloatingIp()) {
|
||||
checkArgument(novaApi.getFloatingIPApi(region).isPresent(),
|
||||
"Floating IPs are required by options, but the extension is not available! options: %s",
|
||||
templateOptions);
|
||||
}
|
||||
|
||||
boolean keyPairExtensionPresent = novaApi.getKeyPairApi(region).isPresent();
|
||||
if (templateOptions.shouldGenerateKeyPair()) {
|
||||
checkArgument(keyPairExtensionPresent,
|
||||
"Key Pairs are required by options, but the extension is not available! options: %s", templateOptions);
|
||||
KeyPair keyPair = keyPairCache.getUnchecked(RegionAndName.fromRegionAndName(region, namingConvention.create()
|
||||
.sharedNameForGroup(group)));
|
||||
keyPairCache.asMap().put(RegionAndName.fromRegionAndName(region, keyPair.getName()), keyPair);
|
||||
templateOptions.keyPairName(keyPair.getName());
|
||||
tagsBuilder.add(JCLOUDS_KP);
|
||||
} else if (templateOptions.getKeyPairName() != null) {
|
||||
checkArgument(keyPairExtensionPresent,
|
||||
"Key Pairs are required by options, but the extension is not available! options: %s", templateOptions);
|
||||
if (templateOptions.getLoginPrivateKey() != null) {
|
||||
String pem = templateOptions.getLoginPrivateKey();
|
||||
KeyPair keyPair = KeyPair.builder().name(templateOptions.getKeyPairName())
|
||||
.fingerprint(fingerprintPrivateKey(pem)).privateKey(pem).build();
|
||||
keyPairCache.asMap().put(RegionAndName.fromRegionAndName(region, keyPair.getName()), keyPair);
|
||||
}
|
||||
if (templateOptions.shouldGenerateKeyPair() || templateOptions.getKeyPairName() != null) {
|
||||
checkArgument(novaApi.getKeyPairApi(region).isPresent(),
|
||||
"Key Pairs are required by options, but the extension is not available! options: %s", templateOptions);
|
||||
}
|
||||
|
||||
boolean securityGroupExtensionPresent = novaApi.getSecurityGroupApi(region).isPresent();
|
||||
List<Integer> inboundPorts = Ints.asList(templateOptions.getInboundPorts());
|
||||
if (!templateOptions.getGroups().isEmpty() || !inboundPorts.isEmpty()) {
|
||||
checkArgument(novaApi.getSecurityGroupApi(region).isPresent(),
|
||||
"Security groups are required by options, but the extension is not available! options: %s",
|
||||
templateOptions);
|
||||
}
|
||||
|
||||
KeyPair keyPair = null;
|
||||
if (templateOptions.shouldGenerateKeyPair()) {
|
||||
keyPair = generateKeyPair(region, namingConvention.create().sharedNameForGroup(group));
|
||||
// If a private key has not been explicitly set, configure the auto-generated one
|
||||
if (Strings.isNullOrEmpty(templateOptions.getLoginPrivateKey())) {
|
||||
templateOptions.overrideLoginPrivateKey(keyPair.getPrivateKey());
|
||||
}
|
||||
} else if (templateOptions.getKeyPairName() != null) {
|
||||
keyPair = checkNotNull(novaApi.getKeyPairApi(region).get().get(templateOptions.getKeyPairName()),
|
||||
"keypair %s doesn't exist", templateOptions.getKeyPairName());
|
||||
}
|
||||
if (keyPair != null) {
|
||||
templateOptions.keyPairName(keyPair.getName());
|
||||
}
|
||||
|
||||
ImmutableList.Builder<String> tagsBuilder = ImmutableList.builder();
|
||||
|
||||
if (!templateOptions.getGroups().isEmpty()) {
|
||||
checkArgument(securityGroupExtensionPresent,
|
||||
"Security groups are required by options, but the extension is not available! options: %s",
|
||||
templateOptions);
|
||||
} else if (securityGroupExtensionPresent) {
|
||||
if (templateOptions.getGroups().isEmpty() && !inboundPorts.isEmpty()) {
|
||||
String securityGroupName = namingConvention.create().sharedNameForGroup(group);
|
||||
try {
|
||||
securityGroupCache.get(new RegionSecurityGroupNameAndPorts(region, securityGroupName, inboundPorts));
|
||||
} catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e.getCause());
|
||||
}
|
||||
templateOptions.securityGroups(securityGroupName);
|
||||
tagsBuilder.add(JCLOUDS_SG);
|
||||
for (String securityGroupName : templateOptions.getGroups()) {
|
||||
checkNotNull(novaApi.getSecurityGroupApi(region).get().get(securityGroupName), "security group %s doesn't exist", securityGroupName);
|
||||
}
|
||||
}
|
||||
templateOptions.userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
|
||||
else if (!inboundPorts.isEmpty()) {
|
||||
SecurityGroupInRegion securityGroupInRegion;
|
||||
String securityGroupName = namingConvention.create().sharedNameForGroup(group);
|
||||
try {
|
||||
securityGroupInRegion = securityGroupCache.get(new RegionSecurityGroupNameAndPorts(region, securityGroupName, inboundPorts));
|
||||
} catch (ExecutionException e) {
|
||||
throw Throwables.propagate(e.getCause());
|
||||
}
|
||||
templateOptions.securityGroups(securityGroupName);
|
||||
tagsBuilder.add(String.format("%s-%s", JCLOUDS_SG_PREFIX, securityGroupInRegion.getSecurityGroup().getId()));
|
||||
}
|
||||
templateOptions.tags(tagsBuilder.build());
|
||||
|
||||
return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses);
|
||||
Map<?, ListenableFuture<Void>> responses = super.execute(group, count, template, goodNodes, badNodes,
|
||||
customizationResponses);
|
||||
|
||||
// Key pairs in Openstack are only required to create the Server. They aren't used anymore so it is better
|
||||
// to delete the auto-generated key pairs at this point where we know exactly which ones have been
|
||||
// auto-generated by jclouds.
|
||||
if (templateOptions.shouldGenerateKeyPair() && keyPair != null) {
|
||||
registerAutoGeneratedKeyPairCleanupCallbacks(responses, region, keyPair.getName());
|
||||
}
|
||||
return responses;
|
||||
}
|
||||
|
||||
private KeyPair generateKeyPair(String region, String prefix) {
|
||||
logger.debug(">> creating default keypair for node...");
|
||||
KeyPair keyPair = novaApi.getKeyPairApi(region).get().create(namingConvention.createWithoutPrefix().uniqueNameForGroup(prefix));
|
||||
logger.debug(">> keypair created! %s", keyPair.getName());
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -177,4 +188,35 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
}
|
||||
}
|
||||
|
||||
private void registerAutoGeneratedKeyPairCleanupCallbacks(final Map<?, ListenableFuture<Void>> responses,
|
||||
final String region, final String generatedKeyPairName) {
|
||||
// The Futures.allAsList fails immediately if some of the futures fail. The Futures.successfulAsList, however,
|
||||
// returns a list containing the results or 'null' for those futures that failed. We want to wait for all them
|
||||
// (even if they fail), so better use the latter form.
|
||||
ListenableFuture<List<Void>> aggregatedResponses = Futures.successfulAsList(responses.values());
|
||||
|
||||
// Key pairs must be cleaned up after all futures completed (even if some failed).
|
||||
Futures.addCallback(aggregatedResponses, new FutureCallback<List<Void>>() {
|
||||
@Override
|
||||
public void onSuccess(List<Void> result) {
|
||||
cleanupAutoGeneratedKeyPair(generatedKeyPairName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
cleanupAutoGeneratedKeyPair(generatedKeyPairName);
|
||||
}
|
||||
|
||||
private void cleanupAutoGeneratedKeyPair(String keyPairName) {
|
||||
logger.debug(">> cleaning up auto-generated key pairs...");
|
||||
try {
|
||||
novaApi.getKeyPairApi(region).get().delete(keyPairName);
|
||||
} catch (Exception ex) {
|
||||
logger.warn(">> could not delete key pair %s: %s", keyPairName, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}, userExecutor);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -367,9 +367,6 @@ public class CreateServerOptions implements MapBinder {
|
|||
return securityGroupNames(ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #getSecurityGroupNames()
|
||||
*/
|
||||
public CreateServerOptions securityGroupNames(Iterable<String> securityGroupNames) {
|
||||
for (String groupName : checkNotNull(securityGroupNames, "securityGroupNames"))
|
||||
checkNotNull(emptyToNull(groupName), "all security groups must be non-empty");
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* 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.predicates;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.SHUTOFF;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server.Status;
|
||||
import org.jclouds.openstack.nova.v2_0.features.ServerApi;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
|
||||
/**
|
||||
* This class tests to see if a Server or ServerCreated has reached a desired status. This class is most useful when
|
||||
* paired with a RetryablePredicate as in the code below. Together these classes can be used to block execution until
|
||||
* the Server or ServerCreated has reached that desired status. This is useful when your Server needs to be 100% ready
|
||||
* before you can continue with execution.
|
||||
* <p/>
|
||||
* For example, you can use the factory methods like so.
|
||||
* <p/>
|
||||
* <pre>
|
||||
* {@code
|
||||
* ServerCreated serverCreated = serverApi.create("my-server", image.getId(), flavor.getId());
|
||||
*
|
||||
* if (!ServerPredicates.awaitActive(serverApi).apply(serverCreated.getId())) {
|
||||
* throw new TimeoutException("Timeout on server: " + serverCreated);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* if (!ServerPredicates.awaitStatus(serverApi, ACTIVE, 300, 2).apply(server.getId())) {
|
||||
* throw new TimeoutException("Timeout on server: " + serverCreated);
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class ServerPredicates {
|
||||
private static final int THIRTY_MINUTES = 600 * 3;
|
||||
private static final int FIVE_SECONDS = 5;
|
||||
|
||||
/**
|
||||
* Waits until a Server is ACTIVE.
|
||||
*
|
||||
* @param serverApi The ServerApi in the region where your Server resides.
|
||||
* @return Predicate that will check the status every 5 seconds for a maximum of 10 minutes.
|
||||
*/
|
||||
public static Predicate<String> awaitActive(ServerApi serverApi) {
|
||||
return awaitStatus(serverApi, ACTIVE, THIRTY_MINUTES, FIVE_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a Server is SHUTOFF.
|
||||
*
|
||||
* @param serverApi The ServerApi in the region where your Server resides.
|
||||
* @return Predicate that will check the status every 5 seconds for a maximum of 10 minutes.
|
||||
*/
|
||||
public static Predicate<String> awaitShutoff(ServerApi serverApi) {
|
||||
return awaitStatus(serverApi, SHUTOFF, THIRTY_MINUTES, FIVE_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until a Server reaches Status.
|
||||
*
|
||||
* @param serverApi The ServerApi in the region where your Server resides.
|
||||
* @return Predicate that will check the status every periodInSec seconds for a maximum of maxWaitInSec minutes.
|
||||
*/
|
||||
public static Predicate<String> awaitStatus(
|
||||
ServerApi serverApi, Status status, long maxWaitInSec, long periodInSec) {
|
||||
ServerStatusPredicate statusPredicate = new ServerStatusPredicate(serverApi, status);
|
||||
|
||||
return retry(statusPredicate, maxWaitInSec, periodInSec, periodInSec, SECONDS);
|
||||
}
|
||||
|
||||
public static class ServerStatusPredicate implements Predicate<String> {
|
||||
private final ServerApi serverApi;
|
||||
private final Status status;
|
||||
|
||||
public ServerStatusPredicate(ServerApi serverApi, Status status) {
|
||||
this.serverApi = checkNotNull(serverApi, "serverApi must be defined");
|
||||
this.status = checkNotNull(status, "status must be defined");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean Return true when the Server reaches the Status, false otherwise
|
||||
* @throws IllegalStateException if the Server associated with serverId does not exist
|
||||
*/
|
||||
@Override
|
||||
public boolean apply(String serverId) {
|
||||
checkNotNull(serverId, "server must be defined");
|
||||
|
||||
Server server = serverApi.get(serverId);
|
||||
|
||||
if (server == null) {
|
||||
throw new IllegalStateException(String.format("Server %s not found.", serverId));
|
||||
}
|
||||
|
||||
return status.equals(server.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,20 +32,15 @@ import org.jclouds.domain.LoginCredentials;
|
|||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
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.Network;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.ServerInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.internal.BaseNovaComputeServiceContextExpectTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Key;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
/**
|
||||
* Tests the compute service abstraction of the nova api.
|
||||
|
@ -239,7 +234,7 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC
|
|||
Injector forSecurityGroups = requestsSendResponses(requestResponseMap);
|
||||
|
||||
Template template = forSecurityGroups.getInstance(TemplateBuilder.class).build();
|
||||
template.getOptions().as(NovaTemplateOptions.class).securityGroupNames("group1", "group2");
|
||||
template.getOptions().as(NovaTemplateOptions.class).securityGroups("group1", "group2");
|
||||
|
||||
NovaComputeServiceAdapter adapter = forSecurityGroups.getInstance(NovaComputeServiceAdapter.class);
|
||||
|
||||
|
@ -281,17 +276,10 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC
|
|||
Injector forSecurityGroups = requestsSendResponses(requestResponseMap);
|
||||
|
||||
Template template = forSecurityGroups.getInstance(TemplateBuilder.class).build();
|
||||
template.getOptions().as(NovaTemplateOptions.class).keyPairName("foo");
|
||||
template.getOptions().as(NovaTemplateOptions.class).keyPairName("foo").overrideLoginPrivateKey("privateKey");
|
||||
|
||||
NovaComputeServiceAdapter adapter = forSecurityGroups.getInstance(NovaComputeServiceAdapter.class);
|
||||
|
||||
// we expect to have already an entry in the cache for the key
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache = forSecurityGroups.getInstance(Key
|
||||
.get(new TypeLiteral<LoadingCache<RegionAndName, KeyPair>>() {
|
||||
}));
|
||||
keyPairCache.put(RegionAndName.fromRegionAndName("az-1.region-a.geo-1", "foo"), KeyPair.builder().name("foo")
|
||||
.privateKey("privateKey").build());
|
||||
|
||||
NodeAndInitialCredentials<ServerInRegion> server = adapter.createNodeWithGroupEncodedIntoName("test", "test-e92",
|
||||
template);
|
||||
assertNotNull(server);
|
||||
|
@ -359,13 +347,16 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC
|
|||
.statusCode(202)
|
||||
.build();
|
||||
|
||||
HttpResponse serverDetailSuspendedResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/server_details_suspended.json")).build();
|
||||
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess)
|
||||
.put(extensionsOfNovaRequest, extensionsOfNovaResponse)
|
||||
.put(listDetail, listDetailResponse)
|
||||
.put(listFlavorsDetail, listFlavorsDetailResponse)
|
||||
.put(suspendServer, suspendServerResponse)
|
||||
.put(serverDetail, serverDetailResponse).build();
|
||||
.put(serverDetail, serverDetailSuspendedResponse).build();
|
||||
|
||||
Injector forAdminExtension = requestsSendResponses(requestResponseMap);
|
||||
|
||||
|
@ -394,13 +385,16 @@ public class NovaComputeServiceAdapterExpectTest extends BaseNovaComputeServiceC
|
|||
.statusCode(202)
|
||||
.build();
|
||||
|
||||
HttpResponse serverDetailSuspendedResponse = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/server_details_suspended.json")).build();
|
||||
|
||||
Map<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.put(keystoneAuthWithUsernameAndPasswordAndTenantName, responseWithKeystoneAccess)
|
||||
.put(extensionsOfNovaRequest, unmatchedExtensionsOfNovaResponse)
|
||||
.put(listDetail, listDetailResponse)
|
||||
.put(listFlavorsDetail, listFlavorsDetailResponse)
|
||||
.put(suspendServer, suspendServerResponse)
|
||||
.put(serverDetail, serverDetailResponse).build();
|
||||
.put(serverDetail, serverDetailSuspendedResponse).build();
|
||||
|
||||
Injector forAdminExtension = requestsSendResponses(requestResponseMap);
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import static org.jclouds.compute.util.ComputeServiceUtils.getCores;
|
|||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.blockUntilRunning;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.keyPairName;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
|
@ -174,7 +173,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
HttpResponse securityGroupWithPort22 = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/securitygroup_details_port22.json")).build();
|
||||
|
||||
HttpRequest create = HttpRequest
|
||||
HttpRequest createKeyPair = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
.endpoint("https://nova-api.openstack.org:9774/v2/3456/os-keypairs")
|
||||
|
@ -188,6 +187,16 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
HttpResponse keyPairWithPrivateKey = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/keypair_created_computeservice.json")).build();
|
||||
|
||||
HttpRequest getKeyPair = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint("https://nova-api.openstack.org:9774/v2/3456/os-keypairs/fooPair")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("X-Auth-Token", authToken).build();
|
||||
|
||||
HttpResponse keyPairDetails = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/keypair_details.json")).build();
|
||||
|
||||
HttpRequest serverDetail = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
|
@ -202,18 +211,12 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
public void testCreateNodeWithGeneratedKeyPair() throws Exception {
|
||||
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.putAll(defaultTemplateOpenStack);
|
||||
requestResponseMap.put(createKeyPair, keyPairWithPrivateKey);
|
||||
requestResponseMap.put(list, notFound);
|
||||
|
||||
requestResponseMap.put(createWithPrefixOnGroup, securityGroupCreated);
|
||||
|
||||
requestResponseMap.put(createRuleForDefaultPort22, securityGroupRuleCreated);
|
||||
|
||||
requestResponseMap.put(getSecurityGroup, securityGroupWithPort22);
|
||||
|
||||
requestResponseMap.put(create, keyPairWithPrivateKey);
|
||||
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
|
||||
|
||||
HttpRequest createServerWithGeneratedKeyPair = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
|
@ -222,13 +225,14 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.addHeader("X-Auth-Token", authToken)
|
||||
.payload(
|
||||
payloadFromStringWithContentType(
|
||||
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds-group\":\"test\",\"jclouds_tags\":\"jclouds_keyPair,jclouds_securityGroup\"},\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds_tags\":\"jclouds_sg-2769\"},\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"application/json")).build();
|
||||
|
||||
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
|
||||
.payload(payloadFromResourceWithContentType("/new_server.json", "application/json; charset=UTF-8")).build();
|
||||
|
||||
requestResponseMap.put(createServerWithGeneratedKeyPair, createdServer);
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
|
||||
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build(), new AbstractModule() {
|
||||
|
||||
|
@ -260,14 +264,14 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.putAll(defaultTemplateOpenStack);
|
||||
requestResponseMap.put(list, notFound);
|
||||
|
||||
requestResponseMap.put(getKeyPair, keyPairDetails);
|
||||
|
||||
requestResponseMap.put(createWithPrefixOnGroup, securityGroupCreated);
|
||||
|
||||
requestResponseMap.put(createRuleForDefaultPort22, securityGroupRuleCreated);
|
||||
|
||||
requestResponseMap.put(getSecurityGroup, securityGroupWithPort22);
|
||||
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
|
||||
HttpRequest createServerWithSuppliedKeyPair = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
|
@ -276,19 +280,20 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.addHeader("X-Auth-Token", authToken)
|
||||
.payload(
|
||||
payloadFromStringWithContentType(
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds-group\":\"test\",\"jclouds_tags\":\"jclouds_securityGroup\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds_tags\":\"jclouds_sg-2769\"},\"key_name\":\"testkeypair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"application/json")).build();
|
||||
|
||||
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
|
||||
.payload(payloadFromResourceWithContentType("/new_server.json", "application/json; charset=UTF-8")).build();
|
||||
|
||||
requestResponseMap.put(createServerWithSuppliedKeyPair, createdServer);
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
|
||||
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build(), new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
// predicatable node names
|
||||
// predictable node names
|
||||
final AtomicInteger suffix = new AtomicInteger();
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(new Supplier<String>() {
|
||||
|
@ -304,20 +309,37 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
});
|
||||
|
||||
NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1,
|
||||
keyPairName("fooPair").blockUntilRunning(false)));
|
||||
keyPairName("fooPair").overrideLoginPrivateKey("privateKey").blockUntilRunning(false)));
|
||||
// we don't have access to this private key
|
||||
assertFalse(node.getCredentials().getOptionalPrivateKey().isPresent());
|
||||
assertTrue(node.getCredentials().getOptionalPrivateKey().isPresent());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateNodeWhileUserSpecifiesKeyPairAndUserSpecifiedGroups() throws Exception {
|
||||
Builder<HttpRequest, HttpResponse> requestResponseMap = ImmutableMap.<HttpRequest, HttpResponse> builder()
|
||||
.putAll(defaultTemplateOpenStack);
|
||||
requestResponseMap.put(list, notFound);
|
||||
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
requestResponseMap.put(getKeyPair, keyPairDetails);
|
||||
|
||||
requestResponseMap.put(createWithPrefixOnGroup, securityGroupCreated);
|
||||
|
||||
requestResponseMap.put(createRuleForDefaultPort22, securityGroupRuleCreated);
|
||||
|
||||
requestResponseMap.put(getSecurityGroup, securityGroupWithPort22);
|
||||
|
||||
HttpRequest getSecurityGroup = HttpRequest
|
||||
.builder()
|
||||
.method("GET")
|
||||
.endpoint("https://nova-api.openstack.org:9774/v2/3456/os-security-groups/mygroup")
|
||||
.addHeader("Accept", "application/json")
|
||||
.addHeader("X-Auth-Token", authToken).build();
|
||||
|
||||
HttpResponse securityGroupWDetails = HttpResponse.builder().statusCode(200)
|
||||
.payload(payloadFromResource("/securitygroup_details_port22.json")).build();
|
||||
|
||||
requestResponseMap.put(getSecurityGroup, securityGroupWDetails);
|
||||
|
||||
HttpRequest createServerWithSuppliedKeyPairAndGroup = HttpRequest
|
||||
.builder()
|
||||
.method("POST")
|
||||
|
@ -326,20 +348,21 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.addHeader("X-Auth-Token", authToken)
|
||||
.payload(
|
||||
payloadFromStringWithContentType(
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
|
||||
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"mygroup\"}]}}",
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"key_name\":\"testkeypair\",\"security_groups\":[{\"name\":\"mygroup\"}]}}",
|
||||
"application/json")).build();
|
||||
|
||||
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
|
||||
.payload(payloadFromResourceWithContentType("/new_server.json", "application/json; charset=UTF-8")).build();
|
||||
|
||||
requestResponseMap.put(createServerWithSuppliedKeyPairAndGroup, createdServer);
|
||||
|
||||
requestResponseMap.put(serverDetail, serverDetailResponse);
|
||||
|
||||
ComputeService apiThatCreatesNode = requestsSendResponses(requestResponseMap.build(), new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
// predicatable node names
|
||||
// predictable node names
|
||||
final AtomicInteger suffix = new AtomicInteger();
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(new Supplier<String>() {
|
||||
|
@ -355,9 +378,9 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
});
|
||||
|
||||
NodeMetadata node = Iterables.getOnlyElement(apiThatCreatesNode.createNodesInGroup("test", 1,
|
||||
keyPairName("fooPair").securityGroupNames("mygroup").blockUntilRunning(false)));
|
||||
keyPairName("fooPair").securityGroups("mygroup").blockUntilRunning(false)));
|
||||
// we don't have access to this private key
|
||||
assertFalse(node.getCredentials().getOptionalPrivateKey().isPresent());
|
||||
assertTrue(!node.getCredentials().getOptionalPrivateKey().isPresent());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* 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.loaders;
|
||||
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.TypeLiteral;
|
||||
|
||||
@Test(groups = "unit", testName = "CreateUniqueKeyPairTest")
|
||||
public class CreateUniqueKeyPairTest {
|
||||
|
||||
@Test
|
||||
public void testApply() throws UnknownHostException {
|
||||
final NovaApi api = createMock(NovaApi.class);
|
||||
KeyPairApi keyApi = createMock(KeyPairApi.class);
|
||||
|
||||
KeyPair pair = createMock(KeyPair.class);
|
||||
|
||||
Optional optKeyApi = Optional.of(keyApi);
|
||||
|
||||
expect(api.getKeyPairApi("region")).andReturn(optKeyApi).atLeastOnce();
|
||||
|
||||
expect(keyApi.create("group-1")).andReturn(pair);
|
||||
|
||||
replay(api, keyApi);
|
||||
|
||||
CreateUniqueKeyPair parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(Suppliers.ofInstance("1"));
|
||||
bind(NovaApi.class).toInstance(api);
|
||||
}
|
||||
|
||||
}).getInstance(CreateUniqueKeyPair.class);
|
||||
|
||||
assertEquals(parser.load(RegionAndName.fromRegionAndName("region", "group")), pair);
|
||||
|
||||
verify(api, keyApi);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyWithIllegalStateException() throws UnknownHostException {
|
||||
final NovaApi api = createMock(NovaApi.class);
|
||||
KeyPairApi keyApi = createMock(KeyPairApi.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Supplier<String> uniqueIdSupplier = createMock(Supplier.class);
|
||||
|
||||
KeyPair pair = createMock(KeyPair.class);
|
||||
|
||||
expect(api.getKeyPairApi("region")).andReturn((Optional) Optional.of(keyApi)).atLeastOnce();
|
||||
|
||||
expect(uniqueIdSupplier.get()).andReturn("1");
|
||||
expect(keyApi.create("group-1")).andThrow(new IllegalStateException());
|
||||
expect(uniqueIdSupplier.get()).andReturn("2");
|
||||
expect(keyApi.create("group-2")).andReturn(pair);
|
||||
|
||||
replay(api, keyApi, uniqueIdSupplier);
|
||||
|
||||
CreateUniqueKeyPair parser = Guice.createInjector(new AbstractModule() {
|
||||
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(new TypeLiteral<Supplier<String>>() {
|
||||
}).toInstance(uniqueIdSupplier);
|
||||
bind(NovaApi.class).toInstance(api);
|
||||
}
|
||||
|
||||
}).getInstance(CreateUniqueKeyPair.class);
|
||||
|
||||
assertEquals(parser.load(RegionAndName.fromRegionAndName("region", "group")), pair);
|
||||
|
||||
verify(api, keyApi, uniqueIdSupplier);
|
||||
}
|
||||
|
||||
}
|
|
@ -22,7 +22,6 @@ import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOption
|
|||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.generateKeyPair;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.inboundPorts;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.installPrivateKey;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions.Builder.securityGroupNames;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -31,9 +30,6 @@ import org.jclouds.compute.options.TemplateOptions;
|
|||
import org.jclouds.openstack.nova.v2_0.domain.Server;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Tests possible uses of NovaTemplateOptions and NovaTemplateOptions.Builder.*
|
||||
*/
|
||||
|
@ -45,52 +41,6 @@ public class NovaTemplateOptionsTest {
|
|||
assertEquals(options.as(NovaTemplateOptions.class), options);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "all security groups must be non-empty")
|
||||
public void testsecurityGroupNamesIterableBadFormat() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
options.securityGroupNames(ImmutableSet.of("group1", ""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testsecurityGroupNamesIterable() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
options.securityGroupNames(ImmutableSet.of("group1", "group2"));
|
||||
assertEquals(options.getSecurityGroupNames(), Optional.of(ImmutableSet.of("group1", "group2")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testsecurityGroupNamesIterableStatic() {
|
||||
NovaTemplateOptions options = securityGroupNames(ImmutableSet.of("group1", "group2"));
|
||||
assertEquals(options.getSecurityGroupNames(), Optional.of(ImmutableSet.of("group1", "group2")));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = NullPointerException.class, expectedExceptionsMessageRegExp = "all security groups must be non-empty")
|
||||
public void testsecurityGroupNamesVarArgsBadFormat() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
options.securityGroupNames("mygroup", "");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testsecurityGroupNamesVarArgs() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
options.securityGroupNames("group1", "group2");
|
||||
assertEquals(options.getSecurityGroupNames(), Optional.of(ImmutableSet.of("group1", "group2")));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultGroupsVarArgsEmpty() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
assertEquals(options.getSecurityGroupNames(), Optional.absent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testsecurityGroupNamesVarArgsStatic() {
|
||||
NovaTemplateOptions options = securityGroupNames("group1", "group2");
|
||||
assertEquals(options.getSecurityGroupNames(), Optional.of(ImmutableSet.of("group1", "group2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testautoAssignFloatingIpDefault() {
|
||||
NovaTemplateOptions options = new NovaTemplateOptions();
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
*/
|
||||
package org.jclouds.openstack.nova.v2_0.features;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.BlockDeviceMapping;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.Network;
|
||||
|
@ -34,11 +36,9 @@ import org.jclouds.openstack.v2_0.features.ExtensionApi;
|
|||
import org.jclouds.openstack.v2_0.predicates.LinkPredicates;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
|
||||
import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitActive;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
|
||||
/**
|
||||
* Tests behavior of {@link ServerApi}
|
||||
|
@ -120,8 +120,6 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
|
|||
ServerCreated server = serverApi.create(hostName, imageId(regionId), "1", options);
|
||||
serverId = server.getId();
|
||||
|
||||
awaitActive(serverApi).apply(server.getId());
|
||||
|
||||
Server serverCheck = serverApi.get(serverId);
|
||||
assertEquals(serverCheck.getStatus(), ACTIVE);
|
||||
} finally {
|
||||
|
@ -162,9 +160,6 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
|
|||
|
||||
ServerCreated server = serverApi.create(hostName, "", flavorRef, options);
|
||||
serverId = server.getId();
|
||||
|
||||
awaitActive(serverApi).apply(server.getId());
|
||||
|
||||
Server serverCheck = serverApi.get(serverId);
|
||||
assertEquals(serverCheck.getStatus(), ACTIVE);
|
||||
} finally {
|
||||
|
@ -241,8 +236,6 @@ public class ServerApiLiveTest extends BaseNovaApiLiveTest {
|
|||
|
||||
ServerCreated server = serverApi.create(hostName, imageId(regionId), flavorId(regionId), options);
|
||||
|
||||
awaitActive(serverApi).apply(server.getId());
|
||||
|
||||
return serverApi.get(server.getId());
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,14 @@
|
|||
*/
|
||||
package org.jclouds.openstack.nova.v2_0.parse;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Address.createV4;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Address.createV6;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||
import org.jclouds.json.BaseItemParserTest;
|
||||
import org.jclouds.json.config.GsonModule;
|
||||
|
@ -33,12 +37,10 @@ import org.jclouds.openstack.v2_0.domain.Resource;
|
|||
import org.jclouds.rest.annotations.SelectJson;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.net.URI;
|
||||
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Address.createV4;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Address.createV6;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableMultimap;
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
@Test(groups = "unit", testName = "ParseServerTest")
|
||||
public class ParseServerTest extends BaseItemParserTest<Server> {
|
||||
|
@ -63,7 +65,7 @@ public class ParseServerTest extends BaseItemParserTest<Server> {
|
|||
.hostId("e4d909c290d0fb1ca068ffaddf22cbd0")
|
||||
.accessIPv4("67.23.10.132")
|
||||
.accessIPv6("::babe:67.23.10.132")
|
||||
.status(Status.BUILD)
|
||||
.status(Status.ACTIVE)
|
||||
.diskConfig(Server.DISK_CONFIG_AUTO)
|
||||
.availabilityZone("nova")
|
||||
.image(
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
* 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.predicates;
|
||||
|
||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.features.ServerApi;
|
||||
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.ACTIVE;
|
||||
import static org.jclouds.openstack.nova.v2_0.domain.Server.Status.SHUTOFF;
|
||||
import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitActive;
|
||||
import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitShutoff;
|
||||
import static org.jclouds.openstack.nova.v2_0.predicates.ServerPredicates.awaitStatus;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
@Test(groups = "unit", testName = "ServerPredicatesMockTest")
|
||||
public class ServerPredicatesMockTest extends BaseOpenStackMockTest<NovaApi> {
|
||||
public void testAwaitActive() throws Exception {
|
||||
MockWebServer server = mockOpenStackServer();
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
String serverDetailsActive = stringFromResource("/server_details.json").replace("BUILD(scheduling)", ACTIVE.value());
|
||||
server.enqueue(new MockResponse().setBody(serverDetailsActive));
|
||||
|
||||
try {
|
||||
NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
|
||||
ServerApi serverApi = novaApi.getServerApi("RegionOne");
|
||||
|
||||
boolean result = awaitActive(serverApi).apply("71752");
|
||||
|
||||
assertTrue(result);
|
||||
assertEquals(server.getRequestCount(), 5);
|
||||
assertAuthentication(server);
|
||||
} finally {
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testAwaitShutoff() throws Exception {
|
||||
MockWebServer server = mockOpenStackServer();
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
String serverDetailsShutoff = stringFromResource("/server_details.json").replace("BUILD(scheduling)", SHUTOFF.value());
|
||||
server.enqueue(new MockResponse().setBody(serverDetailsShutoff));
|
||||
|
||||
try {
|
||||
NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
|
||||
ServerApi serverApi = novaApi.getServerApi("RegionOne");
|
||||
|
||||
boolean result = awaitShutoff(serverApi).apply("71752");
|
||||
|
||||
assertTrue(result);
|
||||
assertEquals(server.getRequestCount(), 7);
|
||||
assertAuthentication(server);
|
||||
} finally {
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testAwaitTimeout() throws Exception {
|
||||
MockWebServer server = mockOpenStackServer();
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/access.json")));
|
||||
|
||||
for (int i = 0; i < 20; i++) {
|
||||
server.enqueue(new MockResponse().setBody(stringFromResource("/server_details.json")));
|
||||
}
|
||||
|
||||
try {
|
||||
NovaApi novaApi = api(server.getUrl("/").toString(), "openstack-nova");
|
||||
ServerApi serverApi = novaApi.getServerApi("RegionOne");
|
||||
|
||||
boolean result = awaitStatus(serverApi, ACTIVE, 3, 1).apply("71752");
|
||||
|
||||
assertFalse(result);
|
||||
assertAuthentication(server);
|
||||
} finally {
|
||||
server.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?xml version="1.0"?>
|
||||
<configuration scan="false">
|
||||
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="WIREFILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/test-data/jclouds-wire.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
<appender name="COMPUTEFILE" class="ch.qos.logback.core.FileAppender">
|
||||
<file>target/jclouds-compute.log</file>
|
||||
<encoder>
|
||||
<Pattern>%d %-5p [%c] [%thread] %m%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.jclouds">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="FILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.compute">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="COMPUTEFILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.wire">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
<logger name="jclouds.headers">
|
||||
<level value="DEBUG" />
|
||||
<appender-ref ref="WIREFILE" />
|
||||
</logger>
|
||||
|
||||
<root>
|
||||
<level value="INFO" />
|
||||
</root>
|
||||
</configuration>
|
|
@ -9,7 +9,7 @@
|
|||
"hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
|
||||
"accessIPv4": "67.23.10.132",
|
||||
"accessIPv6": "::babe:67.23.10.132",
|
||||
"status": "BUILD(scheduling)",
|
||||
"status": "ACTIVE",
|
||||
"progress": 60,
|
||||
"OS-DCF:diskConfig": "AUTO",
|
||||
"OS-EXT-AZ:availability_zone": "nova",
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"server": {
|
||||
"id": "71752",
|
||||
"tenant_id": "1234",
|
||||
"user_id": "5678",
|
||||
"name": "sample-f352",
|
||||
"updated": "2010-10-10T12:00:00Z",
|
||||
"created": "2010-08-10T12:00:00Z",
|
||||
"hostId": "e4d909c290d0fb1ca068ffaddf22cbd0",
|
||||
"accessIPv4": "67.23.10.132",
|
||||
"accessIPv6": "::babe:67.23.10.132",
|
||||
"status": "SUSPENDED",
|
||||
"progress": 60,
|
||||
"OS-DCF:diskConfig": "AUTO",
|
||||
"OS-EXT-AZ:availability_zone": "nova",
|
||||
"image": {
|
||||
"id": "52415800-8b69-11e0-9b19-734f6f006e54",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://servers.api.openstack.org/v2/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
|
||||
},
|
||||
{
|
||||
"rel": "bookmark",
|
||||
"href": "http://servers.api.openstack.org/1234/images/52415800-8b69-11e0-9b19-734f6f006e54"
|
||||
}
|
||||
]
|
||||
},
|
||||
"flavor": {
|
||||
"id": "52415800-8b69-11e0-9b19-734f216543fd",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://servers.api.openstack.org/v2/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
|
||||
},
|
||||
{
|
||||
"rel": "bookmark",
|
||||
"href": "http://servers.api.openstack.org/1234/flavors/52415800-8b69-11e0-9b19-734f216543fd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"addresses": {
|
||||
"public": [
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "67.23.10.132"
|
||||
},
|
||||
{
|
||||
"version": 6,
|
||||
"addr": "::babe:67.23.10.132"
|
||||
},
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "67.23.10.131"
|
||||
},
|
||||
{
|
||||
"version": 6,
|
||||
"addr": "::babe:4317:0A83"
|
||||
}
|
||||
],
|
||||
"private": [
|
||||
{
|
||||
"version": 4,
|
||||
"addr": "10.176.42.16"
|
||||
},
|
||||
{
|
||||
"version": 6,
|
||||
"addr": "::babe:10.176.42.16"
|
||||
}
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"Server Label": "Web Head 1",
|
||||
"Image Version": "2.1"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://servers.api.openstack.org/v2/1234/servers/71752"
|
||||
},
|
||||
{
|
||||
"rel": "bookmark",
|
||||
"href": "http://servers.api.openstack.org/1234/servers/71752"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue