[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
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/aa11765b Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/aa11765b Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/aa11765b Branch: refs/heads/master Commit: aa11765bee8e5e7f2d062ba8123c0dced822f071 Parents: 0bc935d Author: Andrea Turli <[email protected]> Authored: Thu Jul 6 18:18:44 2017 +0200 Committer: Andrea Turli <[email protected]> Committed: Mon Jul 17 11:32:24 2017 +0200 ---------------------------------------------------------------------- .../nova/v2_0/compute/NovaComputeService.java | 10 +- .../v2_0/compute/NovaComputeServiceAdapter.java | 82 ++++++----- .../config/NovaComputeServiceContextModule.java | 131 +++++++++++------ .../AllocateAndAddFloatingIpToNode.java | 8 +- .../compute/functions/CleanupResources.java | 106 ++++++++++++++ .../v2_0/compute/functions/CleanupServer.java | 123 ---------------- .../compute/loaders/CreateUniqueKeyPair.java | 76 ---------- .../compute/options/NovaTemplateOptions.java | 60 +------- ...desWithGroupEncodedIntoNameThenAddToSet.java | 140 ++++++++++++------- .../nova/v2_0/options/CreateServerOptions.java | 3 - .../nova/v2_0/predicates/ServerPredicates.java | 118 ---------------- .../NovaComputeServiceAdapterExpectTest.java | 26 ++-- .../compute/NovaComputeServiceExpectTest.java | 71 ++++++---- .../loaders/CreateUniqueKeyPairTest.java | 108 -------------- .../options/NovaTemplateOptionsTest.java | 50 ------- .../nova/v2_0/features/ServerApiLiveTest.java | 23 ++- .../nova/v2_0/parse/ParseServerTest.java | 24 ++-- .../predicates/ServerPredicatesMockTest.java | 105 -------------- .../src/test/resources/logback-test.xml | 42 ++++++ .../src/test/resources/server_details.json | 2 +- .../resources/server_details_suspended.json | 87 ++++++++++++ 21 files changed, 549 insertions(+), 846 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java index df3f1ca..584c95e 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeService.java @@ -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); } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java index 9864d17..1149e94 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapter.java @@ -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 http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java index cead92b..700a39b 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/config/NovaComputeServiceContextModule.java @@ -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 @@ -165,6 +166,23 @@ public class NovaComputeServiceContextModule extends } @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") protected final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> instanceToFloatingIps( @@ -196,13 +214,6 @@ public class NovaComputeServiceContextModule extends @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( @Memoized Supplier<Set<? extends Location>> locations) { return Suppliers.compose(new Function<Set<? extends Location>, Map<String, Location>>() { @@ -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() { http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java index 7689b53..ec7aaf8 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/AllocateAndAddFloatingIpToNode.java @@ -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()); http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java new file mode 100644 index 0000000..9030395 --- /dev/null +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupResources.java @@ -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(); + } + +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupServer.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupServer.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupServer.java deleted file mode 100644 index 5a922e8..0000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/functions/CleanupServer.java +++ /dev/null @@ -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); - } -} http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java deleted file mode 100644 index 9d521c4..0000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/loaders/CreateUniqueKeyPair.java +++ /dev/null @@ -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; - } - -} http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java index d8939ad..2811e05 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/options/NovaTemplateOptions.java @@ -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() */ @@ -178,31 +176,9 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable { } /** - * - * @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 /** http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java index ca02357..673781c 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/compute/strategy/ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.java @@ -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); } + 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 keyPairExtensionPresent = novaApi.getKeyPairApi(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()) { - 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); + 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()); } - boolean securityGroupExtensionPresent = novaApi.getSecurityGroupApi(region).isPresent(); - List<Integer> inboundPorts = Ints.asList(templateOptions.getInboundPorts()); + 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); + } + } http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java index f9f5362..7dd3472 100644 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java +++ b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/options/CreateServerOptions.java @@ -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"); http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java b/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java deleted file mode 100644 index 00da77f..0000000 --- a/apis/openstack-nova/src/main/java/org/jclouds/openstack/nova/v2_0/predicates/ServerPredicates.java +++ /dev/null @@ -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()); - } - } -} http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java index 5cb02a7..5528349 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceAdapterExpectTest.java @@ -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); http://git-wip-us.apache.org/repos/asf/jclouds/blob/aa11765b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceExpectTest.java ---------------------------------------------------------------------- diff --git a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceExpectTest.java b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceExpectTest.java index a163f02..49cc969 100644 --- a/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceExpectTest.java +++ b/apis/openstack-nova/src/test/java/org/jclouds/openstack/nova/v2_0/compute/NovaComputeServiceExpectTest.java @@ -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()); } }
