mirror of https://github.com/apache/jclouds.git
openstack-nova: adding logic for implicit keypair option
This commit is contained in:
parent
e81ac99daa
commit
9045fa2463
|
@ -54,10 +54,13 @@ import org.jclouds.domain.Credentials;
|
||||||
import org.jclouds.domain.Location;
|
import org.jclouds.domain.Location;
|
||||||
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup;
|
import org.jclouds.openstack.nova.v1_1.domain.SecurityGroup;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
|
||||||
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient;
|
import org.jclouds.openstack.nova.v1_1.extensions.SecurityGroupClient;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.predicates.KeyPairPredicates;
|
||||||
import org.jclouds.openstack.nova.v1_1.predicates.SecurityGroupPredicates;
|
import org.jclouds.openstack.nova.v1_1.predicates.SecurityGroupPredicates;
|
||||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||||
|
|
||||||
|
@ -77,6 +80,7 @@ import com.google.common.collect.Multimap;
|
||||||
public class NovaComputeService extends BaseComputeService {
|
public class NovaComputeService extends BaseComputeService {
|
||||||
private final NovaClient novaClient;
|
private final NovaClient novaClient;
|
||||||
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap;
|
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap;
|
||||||
|
private final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
|
||||||
private final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId;
|
private final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -96,6 +100,7 @@ public class NovaComputeService extends BaseComputeService {
|
||||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, NovaClient novaClient,
|
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, NovaClient novaClient,
|
||||||
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap,
|
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap,
|
||||||
|
LoadingCache<ZoneAndName, KeyPair> keyPairCache,
|
||||||
Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId) {
|
Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId) {
|
||||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
|
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
|
||||||
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy,
|
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy,
|
||||||
|
@ -104,6 +109,7 @@ public class NovaComputeService extends BaseComputeService {
|
||||||
timeouts, executor);
|
timeouts, executor);
|
||||||
this.novaClient = checkNotNull(novaClient, "novaClient");
|
this.novaClient = checkNotNull(novaClient, "novaClient");
|
||||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||||
|
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||||
this.orphanedGroupsByZoneId = checkNotNull(orphanedGroupsByZoneId, "orphanedGroupsByZoneId");
|
this.orphanedGroupsByZoneId = checkNotNull(orphanedGroupsByZoneId, "orphanedGroupsByZoneId");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +123,7 @@ public class NovaComputeService extends BaseComputeService {
|
||||||
|
|
||||||
protected void cleanOrphanedGroupsInZone(Set<String> groups, String zoneId) {
|
protected void cleanOrphanedGroupsInZone(Set<String> groups, String zoneId) {
|
||||||
cleanupOrphanedSecurityGroupsInZone(groups, zoneId);
|
cleanupOrphanedSecurityGroupsInZone(groups, zoneId);
|
||||||
|
cleanupOrphanedKeyPairsInZone(groups, zoneId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cleanupOrphanedSecurityGroupsInZone(Set<String> groups, String zoneId) {
|
private void cleanupOrphanedSecurityGroupsInZone(Set<String> groups, String zoneId) {
|
||||||
|
@ -136,6 +143,24 @@ public class NovaComputeService extends BaseComputeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanupOrphanedKeyPairsInZone(Set<String> groups, String zoneId) {
|
||||||
|
Optional<KeyPairClient> keyPairClient = novaClient.getKeyPairExtensionForZone(zoneId);
|
||||||
|
if (keyPairClient.isPresent()) {
|
||||||
|
for (String group : groups) {
|
||||||
|
for (Map<String, KeyPair> wrapper : keyPairClient.get().listKeyPairs()) {
|
||||||
|
for (KeyPair pair : Iterables.filter(wrapper.values(), KeyPairPredicates.nameStartsWith("jclouds#" + group + "#"))) {
|
||||||
|
ZoneAndName zoneAndName = ZoneAndName.fromZoneAndName(zoneId, pair.getName());
|
||||||
|
logger.debug(">> deleting keypair(%s)", zoneAndName);
|
||||||
|
keyPairClient.get().deleteKeyPair(pair.getName());
|
||||||
|
// TODO: test this clear happens
|
||||||
|
keyPairCache.invalidate(zoneAndName);
|
||||||
|
logger.debug("<< deleted keypair(%s)", zoneAndName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns template options, except of type {@link NovaTemplateOptions}.
|
* returns template options, except of type {@link NovaTemplateOptions}.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -26,6 +26,7 @@ import javax.annotation.Resource;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import org.jclouds.compute.ComputeServiceAdapter;
|
import org.jclouds.compute.ComputeServiceAdapter;
|
||||||
import org.jclouds.compute.domain.Template;
|
import org.jclouds.compute.domain.Template;
|
||||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
|
@ -39,12 +40,14 @@ import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
|
import org.jclouds.openstack.nova.v1_1.domain.Flavor;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.Image;
|
import org.jclouds.openstack.nova.v1_1.domain.Image;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
|
import org.jclouds.openstack.nova.v1_1.domain.RebootType;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.Server;
|
import org.jclouds.openstack.nova.v1_1.domain.Server;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ServerInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ServerInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndId;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
||||||
import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
|
import org.jclouds.openstack.nova.v1_1.options.CreateServerOptions;
|
||||||
import org.jclouds.openstack.nova.v1_1.predicates.ImagePredicates;
|
import org.jclouds.openstack.nova.v1_1.predicates.ImagePredicates;
|
||||||
|
|
||||||
|
@ -70,14 +73,17 @@ public class NovaComputeServiceAdapter implements
|
||||||
protected final NovaClient novaClient;
|
protected final NovaClient novaClient;
|
||||||
protected final Supplier<Set<String>> zoneIds;
|
protected final Supplier<Set<String>> zoneIds;
|
||||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||||
|
protected final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public NovaComputeServiceAdapter(NovaClient novaClient, @Zone Supplier<Set<String>> zoneIds,
|
public NovaComputeServiceAdapter(NovaClient novaClient, @Zone Supplier<Set<String>> zoneIds,
|
||||||
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate) {
|
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||||
|
LoadingCache<ZoneAndName, KeyPair> keyPairCache) {
|
||||||
this.novaClient = checkNotNull(novaClient, "novaClient");
|
this.novaClient = checkNotNull(novaClient, "novaClient");
|
||||||
this.zoneIds = checkNotNull(zoneIds, "zoneIds");
|
this.zoneIds = checkNotNull(zoneIds, "zoneIds");
|
||||||
this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate,
|
this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate,
|
||||||
"removeFloatingIpFromNodeAndDeallocate");
|
"removeFloatingIpFromNodeAndDeallocate");
|
||||||
|
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,15 +95,26 @@ public class NovaComputeServiceAdapter implements
|
||||||
public NodeAndInitialCredentials<ServerInZone> createNodeWithGroupEncodedIntoName(String group, String name,
|
public NodeAndInitialCredentials<ServerInZone> createNodeWithGroupEncodedIntoName(String group, String name,
|
||||||
Template template) {
|
Template template) {
|
||||||
|
|
||||||
|
LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
|
||||||
|
NovaTemplateOptions templateOptions = template.getOptions().as(NovaTemplateOptions.class);
|
||||||
CreateServerOptions options = new CreateServerOptions();
|
CreateServerOptions options = new CreateServerOptions();
|
||||||
options.securityGroupNames(template.getOptions().as(NovaTemplateOptions.class).getSecurityGroupNames());
|
options.securityGroupNames(templateOptions.getSecurityGroupNames());
|
||||||
|
|
||||||
|
String keyName = templateOptions.getKeyPairName();
|
||||||
|
if (keyName != null) {
|
||||||
|
options.withKeyName(keyName);
|
||||||
|
KeyPair keyPair = keyPairCache.getIfPresent(ZoneAndName.fromZoneAndName(template.getLocation().getId(), keyName));
|
||||||
|
if (keyPair != null) {
|
||||||
|
credentialsBuilder.privateKey(keyPair.getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String zoneId = template.getLocation().getId();
|
String zoneId = template.getLocation().getId();
|
||||||
Server server = novaClient.getServerClientForZone(zoneId).createServer(name, template.getImage().getProviderId(),
|
Server server = novaClient.getServerClientForZone(zoneId).createServer(name, template.getImage().getProviderId(),
|
||||||
template.getHardware().getProviderId(), options);
|
template.getHardware().getProviderId(), options);
|
||||||
ServerInZone serverInZone = new ServerInZone(server, zoneId);
|
ServerInZone serverInZone = new ServerInZone(server, zoneId);
|
||||||
return new NodeAndInitialCredentials<ServerInZone>(serverInZone, serverInZone.slashEncode(), LoginCredentials
|
return new NodeAndInitialCredentials<ServerInZone>(serverInZone, serverInZone.slashEncode(), credentialsBuilder
|
||||||
.builder().password(server.getAdminPass()).build());
|
.password(server.getAdminPass()).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jclouds.openstack.nova.v1_1.compute.config;
|
package org.jclouds.openstack.nova.v1_1.compute.config;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -42,16 +43,13 @@ import org.jclouds.openstack.nova.v1_1.NovaAsyncClient;
|
||||||
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.NovaComputeService;
|
import org.jclouds.openstack.nova.v1_1.compute.NovaComputeService;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.NovaComputeServiceAdapter;
|
import org.jclouds.openstack.nova.v1_1.compute.NovaComputeServiceAdapter;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.CreateSecurityGroupIfNeeded;
|
import org.jclouds.openstack.nova.v1_1.compute.functions.*;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.FlavorInZoneToHardware;
|
import org.jclouds.openstack.nova.v1_1.compute.loaders.FindKeyPairOrCreate;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.ImageInZoneToImage;
|
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.NovaImageToOperatingSystem;
|
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.OrphanedGroupsByZoneId;
|
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.ServerInZoneToNodeMetadata;
|
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.loaders.FindSecurityGroupOrCreate;
|
import org.jclouds.openstack.nova.v1_1.compute.loaders.FindSecurityGroupOrCreate;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.loaders.LoadFloatingIpsForInstance;
|
import org.jclouds.openstack.nova.v1_1.compute.loaders.LoadFloatingIpsForInstance;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
import org.jclouds.openstack.nova.v1_1.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.FlavorInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
||||||
|
@ -131,6 +129,11 @@ public class NovaComputeServiceContextModule
|
||||||
bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
|
bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
|
||||||
ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
|
ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
|
||||||
|
|
||||||
|
bind(new TypeLiteral<Function<ZoneAndName, KeyPair>>() {
|
||||||
|
}).to(CreateUniqueKeyPair.class);
|
||||||
|
|
||||||
|
bind(new TypeLiteral<CacheLoader<ZoneAndName, KeyPair>>() {
|
||||||
|
}).to(FindKeyPairOrCreate.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -166,6 +169,27 @@ public class NovaComputeServiceContextModule
|
||||||
return new RetryablePredicate<AtomicReference<ZoneAndName>>(in, msDelay, 100l, TimeUnit.MILLISECONDS);
|
return new RetryablePredicate<AtomicReference<ZoneAndName>>(in, msDelay, 100l, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
protected LoadingCache<ZoneAndName, KeyPair> keyPairMap(
|
||||||
|
CacheLoader<ZoneAndName, KeyPair> in) {
|
||||||
|
return CacheBuilder.newBuilder().build(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
Supplier<String> provideSuffix() {
|
||||||
|
return new Supplier<String>() {
|
||||||
|
final SecureRandom random = new SecureRandom();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return random.nextInt(100) + "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
protected Supplier<Map<String, Location>> createLocationIndexedById(
|
protected Supplier<Map<String, Location>> createLocationIndexedById(
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds 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.v1_1.compute.functions;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||||
|
import org.jclouds.logging.Logger;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adam Lowe
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
public class CreateUniqueKeyPair implements Function<ZoneAndName, KeyPair> {
|
||||||
|
@Resource
|
||||||
|
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||||
|
protected Logger logger = Logger.NULL;
|
||||||
|
protected final NovaClient novaClient;
|
||||||
|
protected final Supplier<String> randomSuffix;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CreateUniqueKeyPair(NovaClient novaClient, Supplier<String> randomSuffix) {
|
||||||
|
this.novaClient = checkNotNull(novaClient, "novaClient");
|
||||||
|
this.randomSuffix = randomSuffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair apply(ZoneAndName zoneAndName) {
|
||||||
|
checkNotNull(zoneAndName, "zoneAndName");
|
||||||
|
return createNewKeyPairInZone(zoneAndName.getZone(), zoneAndName.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
KeyPair createNewKeyPairInZone(String zone, String group) {
|
||||||
|
checkNotNull(zone, "zone");
|
||||||
|
checkNotNull(group, "group");
|
||||||
|
logger.debug(">> creating keyPair zone(%s) group(%s)", zone, group);
|
||||||
|
|
||||||
|
KeyPair keyPair = null;
|
||||||
|
while (keyPair == null) {
|
||||||
|
try {
|
||||||
|
keyPair = novaClient.getKeyPairExtensionForZone(zone).get().createKeyPair(getNextName(group));
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("<< created keyPair(%s)", keyPair);
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNextName(String group) {
|
||||||
|
return String.format("jclouds#%s#%s", group, randomSuffix.get());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds 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.v1_1.compute.loaders;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adam Lowe
|
||||||
|
*/
|
||||||
|
public class FindKeyPairOrCreate extends CacheLoader<ZoneAndName, KeyPair> {
|
||||||
|
protected final Function<ZoneAndName, KeyPair> keypairCreator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public FindKeyPairOrCreate(
|
||||||
|
Function<ZoneAndName, KeyPair> keypairCreator) {
|
||||||
|
this.keypairCreator = checkNotNull(keypairCreator, "keypairCreator");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyPair load(ZoneAndName in) {
|
||||||
|
// not retrieving KeyPairs from the server as the private key isn't returned
|
||||||
|
return keypairCreator.apply(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "returnExistingKeyPairInZoneOrCreateAsNeeded()";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -72,6 +72,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
||||||
private boolean autoAssignFloatingIp = false;
|
private boolean autoAssignFloatingIp = false;
|
||||||
private boolean generateKeyPair = false;
|
private boolean generateKeyPair = false;
|
||||||
private Set<String> securityGroupNames = ImmutableSet.of();
|
private Set<String> securityGroupNames = ImmutableSet.of();
|
||||||
|
private String keyPairName;
|
||||||
|
|
||||||
public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
|
public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
|
||||||
|
|
||||||
|
@ -91,6 +92,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #shouldGenerateKeyPair()
|
||||||
|
*/
|
||||||
|
public NovaTemplateOptions keyPairName(String keyPairName) {
|
||||||
|
this.keyPairName = keyPairName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
|
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
|
||||||
|
@ -121,6 +130,14 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
||||||
return autoAssignFloatingIp;
|
return autoAssignFloatingIp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the keypair used to run instances with
|
||||||
|
* @return the keypair to be used
|
||||||
|
*/
|
||||||
|
public String getKeyPairName() {
|
||||||
|
return keyPairName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <h3>Note</h3>
|
* <h3>Note</h3>
|
||||||
*
|
*
|
||||||
|
@ -156,6 +173,13 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
|
||||||
return new NovaTemplateOptions().generateKeyPair(enable);
|
return new NovaTemplateOptions().generateKeyPair(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see NovaTemplateOptions#getKeyPairName()
|
||||||
|
*/
|
||||||
|
public static NovaTemplateOptions keyPairName(String keyPairName) {
|
||||||
|
return new NovaTemplateOptions().keyPairName(keyPairName);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
|
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.jclouds.concurrent.Futures;
|
||||||
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.functions.AllocateAndAddFloatingIpToNode;
|
import org.jclouds.openstack.nova.v1_1.compute.functions.AllocateAndAddFloatingIpToNode;
|
||||||
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
import org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
|
||||||
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts;
|
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts;
|
||||||
|
@ -66,6 +67,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
||||||
|
|
||||||
private final AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode;
|
private final AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode;
|
||||||
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache;
|
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache;
|
||||||
|
private final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
|
||||||
private final NovaClient novaClient;
|
private final NovaClient novaClient;
|
||||||
private final Provider<TemplateBuilder> templateBuilderProvider;
|
private final Provider<TemplateBuilder> templateBuilderProvider;
|
||||||
|
|
||||||
|
@ -78,11 +80,13 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
||||||
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
|
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
|
||||||
Provider<TemplateBuilder> templateBuilderProvider,
|
Provider<TemplateBuilder> templateBuilderProvider,
|
||||||
AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode,
|
AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode,
|
||||||
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache, NovaClient novaClient) {
|
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache,
|
||||||
|
LoadingCache<ZoneAndName, KeyPair> keyPairCache, NovaClient novaClient) {
|
||||||
super(addNodeWithTagStrategy, listNodesStrategy, nodeNamingConvention, executor,
|
super(addNodeWithTagStrategy, listNodesStrategy, nodeNamingConvention, executor,
|
||||||
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
|
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
|
||||||
this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
|
this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
|
||||||
this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
|
this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
|
||||||
|
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||||
this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode,
|
this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode,
|
||||||
"allocateAndAddFloatingIpToNode");
|
"allocateAndAddFloatingIpToNode");
|
||||||
this.novaClient = checkNotNull(novaClient, "novaClient");
|
this.novaClient = checkNotNull(novaClient, "novaClient");
|
||||||
|
@ -93,9 +97,6 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
||||||
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
|
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
|
||||||
// ensure we don't mutate the input template
|
// ensure we don't mutate the input template
|
||||||
Template mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build();
|
Template mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build();
|
||||||
|
|
||||||
// TODO: make NovaTemplateOptions with the following:
|
|
||||||
// security group, key pair
|
|
||||||
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions());
|
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions());
|
||||||
|
|
||||||
String zone = mutableTemplate.getLocation().getId();
|
String zone = mutableTemplate.getLocation().getId();
|
||||||
|
@ -110,6 +111,11 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
||||||
checkArgument(novaClient.getKeyPairExtensionForZone(zone).isPresent(),
|
checkArgument(novaClient.getKeyPairExtensionForZone(zone).isPresent(),
|
||||||
"Key Pairs are required by options, but the extension is not available! options: %s",
|
"Key Pairs are required by options, but the extension is not available! options: %s",
|
||||||
templateOptions);
|
templateOptions);
|
||||||
|
if (templateOptions.getKeyPairName() == null) {
|
||||||
|
KeyPair keyPair = keyPairCache.getUnchecked(ZoneAndName.fromZoneAndName(zone, "jclouds#" + group));
|
||||||
|
keyPairCache.asMap().put(ZoneAndName.fromZoneAndName(zone, keyPair.getName()), keyPair);
|
||||||
|
templateOptions.keyPairName(keyPair.getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean securityGroupExensionPresent = novaClient.getSecurityGroupExtensionForZone(zone).isPresent();
|
boolean securityGroupExensionPresent = novaClient.getSecurityGroupExtensionForZone(zone).isPresent();
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds 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.v1_1.predicates;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicates handy when working with KeyPairs
|
||||||
|
*
|
||||||
|
* @author Adam Lowe
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class KeyPairPredicates {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* matches name of the given keypair starts with the specified prefix
|
||||||
|
*
|
||||||
|
* @param prefix the prefix you are looking for
|
||||||
|
* @return the predicate
|
||||||
|
*/
|
||||||
|
public static Predicate<KeyPair> nameStartsWith(final String prefix) {
|
||||||
|
checkNotNull(prefix, "name must be defined");
|
||||||
|
|
||||||
|
return new Predicate<KeyPair>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(KeyPair ext) {
|
||||||
|
return ext.getName() != null && ext.getName().startsWith(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "nameStartsWith(" + prefix + ")";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
/**
|
||||||
|
* Licensed to jclouds, Inc. (jclouds) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. jclouds 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.v1_1.compute.functions;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.base.Supplier;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.NovaClient;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
|
||||||
|
import org.jclouds.openstack.nova.v1_1.extensions.KeyPairClient;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
import static org.easymock.EasyMock.expect;
|
||||||
|
import static org.easymock.classextension.EasyMock.*;
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Adam Lowe
|
||||||
|
*/
|
||||||
|
@Test(groups = "unit")
|
||||||
|
public class CreateUniqueKeyPairTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApply() throws UnknownHostException {
|
||||||
|
NovaClient client = createMock(NovaClient.class);
|
||||||
|
KeyPairClient keyClient = createMock(KeyPairClient.class);
|
||||||
|
Supplier<String> uniqueIdSupplier = createMock(Supplier.class);
|
||||||
|
|
||||||
|
KeyPair pair = createMock(KeyPair.class);
|
||||||
|
|
||||||
|
expect(client.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyClient)).atLeastOnce();
|
||||||
|
|
||||||
|
expect(uniqueIdSupplier.get()).andReturn("1");
|
||||||
|
expect(keyClient.createKeyPair("jclouds#group#1")).andReturn(pair);
|
||||||
|
|
||||||
|
replay(client);
|
||||||
|
replay(keyClient);
|
||||||
|
replay(uniqueIdSupplier);
|
||||||
|
|
||||||
|
CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier);
|
||||||
|
|
||||||
|
assertEquals(parser.createNewKeyPairInZone("zone", "group"), pair);
|
||||||
|
|
||||||
|
verify(client);
|
||||||
|
verify(keyClient);
|
||||||
|
verify(uniqueIdSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyWithIllegalStateException() throws UnknownHostException {
|
||||||
|
NovaClient client = createMock(NovaClient.class);
|
||||||
|
KeyPairClient keyClient = createMock(KeyPairClient.class);
|
||||||
|
Supplier<String> uniqueIdSupplier = createMock(Supplier.class);
|
||||||
|
|
||||||
|
KeyPair pair = createMock(KeyPair.class);
|
||||||
|
|
||||||
|
expect(client.getKeyPairExtensionForZone("zone")).andReturn(Optional.of(keyClient)).atLeastOnce();
|
||||||
|
|
||||||
|
expect(uniqueIdSupplier.get()).andReturn("1");
|
||||||
|
expect(keyClient.createKeyPair("jclouds#group#1")).andThrow(new IllegalStateException());
|
||||||
|
expect(uniqueIdSupplier.get()).andReturn("2");
|
||||||
|
expect(keyClient.createKeyPair("jclouds#group#2")).andReturn(pair);
|
||||||
|
|
||||||
|
replay(client);
|
||||||
|
replay(keyClient);
|
||||||
|
replay(uniqueIdSupplier);
|
||||||
|
|
||||||
|
CreateUniqueKeyPair parser = new CreateUniqueKeyPair(client, uniqueIdSupplier);
|
||||||
|
|
||||||
|
assertEquals(parser.createNewKeyPairInZone("zone", "group"), pair);
|
||||||
|
|
||||||
|
verify(client);
|
||||||
|
verify(keyClient);
|
||||||
|
verify(uniqueIdSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue