Merge pull request #503 from aplowe/master

openstack-nova: Auto key pair generation wiring into compute service and adaptor
This commit is contained in:
Adrian Cole 2012-03-21 11:50:58 -07:00
commit 0f15559f2c
15 changed files with 475 additions and 32 deletions

View File

@ -22,6 +22,7 @@ import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.Constants.PROPERTY_ISO3166_CODES;
import static org.jclouds.compute.reference.ComputeServiceConstants.PROPERTY_TIMEOUT_NODE_TERMINATED;
import static org.jclouds.openstack.nova.v1_1.reference.NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS;
import static org.jclouds.openstack.nova.v1_1.reference.NovaConstants.PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS;
import java.util.Properties;
@ -39,6 +40,7 @@ public class HPCloudComputePropertiesBuilder extends NovaPropertiesBuilder {
properties.setProperty(PROPERTY_ISO3166_CODES, "US-NV");
properties.setProperty(PROPERTY_ENDPOINT, "https://region-a.geo-1.identity.hpcloudsvc.com:35357");
properties.setProperty(PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS, "true");
properties.setProperty(PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS, "true");
// deallocating ip addresses can take a while
properties.setProperty(PROPERTY_TIMEOUT_NODE_TERMINATED, 60 * 1000 + "");
return properties;

View File

@ -4,15 +4,18 @@ import java.util.Set;
import javax.inject.Inject;
import com.google.common.cache.LoadingCache;
import org.jclouds.location.Zone;
import org.jclouds.openstack.nova.v1_1.NovaClient;
import org.jclouds.openstack.nova.v1_1.compute.NovaComputeServiceAdapter;
import org.jclouds.openstack.nova.v1_1.compute.functions.RemoveFloatingIpFromNodeAndDeallocate;
import org.jclouds.openstack.nova.v1_1.domain.KeyPair;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ImageInZone;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.collect.Iterables;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneAndName;
/**
*
@ -22,8 +25,8 @@ public class HPCloudComputeServiceAdapter extends NovaComputeServiceAdapter {
@Inject
public HPCloudComputeServiceAdapter(NovaClient novaClient, @Zone Supplier<Set<String>> zoneIds,
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate) {
super(novaClient, zoneIds, removeFloatingIpFromNodeAndDeallocate);
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate, LoadingCache<ZoneAndName, KeyPair> keyPairCache) {
super(novaClient, zoneIds, removeFloatingIpFromNodeAndDeallocate, keyPairCache);
}
@Override

View File

@ -21,6 +21,7 @@ package org.jclouds.openstack.nova.v1_1;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.openstack.nova.v1_1.reference.NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS;
import static org.jclouds.openstack.nova.v1_1.reference.NovaConstants.PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS;
import static org.jclouds.openstack.nova.v1_1.reference.NovaConstants.PROPERTY_NOVA_TIMEOUT_SECURITYGROUP_PRESENT;
import java.util.Properties;
@ -44,6 +45,7 @@ public class NovaPropertiesBuilder extends PropertiesBuilder {
properties.setProperty(KeystoneProperties.VERSION, "2.0");
properties.setProperty(PROPERTY_API_VERSION, "1.1");
properties.setProperty(PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS, "false");
properties.setProperty(PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS, "false");
properties.setProperty(PROPERTY_NOVA_TIMEOUT_SECURITYGROUP_PRESENT, "500");
return properties;
}

View File

@ -54,10 +54,13 @@ import org.jclouds.domain.Credentials;
import org.jclouds.domain.Location;
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.domain.KeyPair;
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.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.predicates.KeyPairPredicates;
import org.jclouds.openstack.nova.v1_1.predicates.SecurityGroupPredicates;
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
@ -77,6 +80,7 @@ import com.google.common.collect.Multimap;
public class NovaComputeService extends BaseComputeService {
private final NovaClient novaClient;
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap;
private final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
private final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId;
@Inject
@ -96,6 +100,7 @@ public class NovaComputeService extends BaseComputeService {
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor, NovaClient novaClient,
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupMap,
LoadingCache<ZoneAndName, KeyPair> keyPairCache,
Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByZoneId) {
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getNodeMetadataStrategy,
runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy, startNodeStrategy,
@ -104,6 +109,7 @@ public class NovaComputeService extends BaseComputeService {
timeouts, executor);
this.novaClient = checkNotNull(novaClient, "novaClient");
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
this.orphanedGroupsByZoneId = checkNotNull(orphanedGroupsByZoneId, "orphanedGroupsByZoneId");
}
@ -117,6 +123,7 @@ public class NovaComputeService extends BaseComputeService {
protected void cleanOrphanedGroupsInZone(Set<String> groups, String zoneId) {
cleanupOrphanedSecurityGroupsInZone(groups, zoneId);
cleanupOrphanedKeyPairsInZone(groups, zoneId);
}
private void cleanupOrphanedSecurityGroupsInZone(Set<String> groups, String zoneId) {
@ -135,6 +142,25 @@ 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);
}
}
keyPairCache.invalidate(ZoneAndName.fromZoneAndName(zoneId, "jclouds#" + group));
}
}
}
/**
* returns template options, except of type {@link NovaTemplateOptions}.

View File

@ -26,6 +26,7 @@ import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import com.google.common.cache.LoadingCache;
import org.jclouds.compute.ComputeServiceAdapter;
import org.jclouds.compute.domain.Template;
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.domain.Flavor;
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.Server;
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.ServerInZone;
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.predicates.ImagePredicates;
@ -70,14 +73,17 @@ public class NovaComputeServiceAdapter implements
protected final NovaClient novaClient;
protected final Supplier<Set<String>> zoneIds;
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
protected final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
@Inject
public NovaComputeServiceAdapter(NovaClient novaClient, @Zone Supplier<Set<String>> zoneIds,
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate) {
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
LoadingCache<ZoneAndName, KeyPair> keyPairCache) {
this.novaClient = checkNotNull(novaClient, "novaClient");
this.zoneIds = checkNotNull(zoneIds, "zoneIds");
this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate,
"removeFloatingIpFromNodeAndDeallocate");
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
}
/**
@ -89,15 +95,26 @@ public class NovaComputeServiceAdapter implements
public NodeAndInitialCredentials<ServerInZone> createNodeWithGroupEncodedIntoName(String group, String name,
Template template) {
LoginCredentials.Builder credentialsBuilder = LoginCredentials.builder();
NovaTemplateOptions templateOptions = template.getOptions().as(NovaTemplateOptions.class);
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();
Server server = novaClient.getServerClientForZone(zoneId).createServer(name, template.getImage().getProviderId(),
template.getHardware().getProviderId(), options);
ServerInZone serverInZone = new ServerInZone(server, zoneId);
return new NodeAndInitialCredentials<ServerInZone>(serverInZone, serverInZone.slashEncode(), LoginCredentials
.builder().password(server.getAdminPass()).build());
return new NodeAndInitialCredentials<ServerInZone>(serverInZone, serverInZone.slashEncode(), credentialsBuilder
.password(server.getAdminPass()).build());
}
@Override

View File

@ -18,6 +18,7 @@
*/
package org.jclouds.openstack.nova.v1_1.compute.config;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Set;
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.compute.NovaComputeService;
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.FlavorInZoneToHardware;
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.functions.*;
import org.jclouds.openstack.nova.v1_1.compute.loaders.FindKeyPairOrCreate;
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.options.NovaTemplateOptions;
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.ImageInZone;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.SecurityGroupInZone;
@ -131,13 +129,20 @@ public class NovaComputeServiceContextModule
bind(CreateNodesWithGroupEncodedIntoNameThenAddToSet.class).to(
ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.class);
bind(new TypeLiteral<Function<ZoneAndName, KeyPair>>() {
}).to(CreateUniqueKeyPair.class);
bind(new TypeLiteral<CacheLoader<ZoneAndName, KeyPair>>() {
}).to(FindKeyPairOrCreate.class);
}
@Override
protected TemplateOptions provideTemplateOptions(Injector injector, TemplateOptions options) {
return options.as(NovaTemplateOptions.class).autoAssignFloatingIp(
injector.getInstance(Key.get(boolean.class, Names
.named(NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS))));
return options.as(NovaTemplateOptions.class)
.autoAssignFloatingIp(injector.getInstance(
Key.get(boolean.class, Names.named(NovaConstants.PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS))))
.generateKeyPair(injector.getInstance(
Key.get(boolean.class, Names.named(NovaConstants.PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS))));
}
@Provides
@ -164,6 +169,27 @@ public class NovaComputeServiceContextModule
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
@Singleton
protected Supplier<Map<String, Location>> createLocationIndexedById(

View File

@ -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());
}
}

View File

@ -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()";
}
}

View File

@ -70,7 +70,9 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
}
private boolean autoAssignFloatingIp = false;
private boolean generateKeyPair = false;
private Set<String> securityGroupNames = ImmutableSet.of();
private String keyPairName;
public static final NovaTemplateOptions NONE = new NovaTemplateOptions();
@ -83,15 +85,31 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
}
/**
*
* @see CreateServerOptions#getSecurityGroupNames
* @see #shouldGenerateKeyPair()
*/
public NovaTemplateOptions generateKeyPair(boolean enable) {
this.generateKeyPair = enable;
return this;
}
/**
* @see #shouldGenerateKeyPair()
*/
public NovaTemplateOptions keyPairName(String keyPairName) {
this.keyPairName = keyPairName;
return this;
}
/**
*
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
*/
public NovaTemplateOptions securityGroupNames(String... securityGroupNames) {
return securityGroupNames(ImmutableSet.copyOf(checkNotNull(securityGroupNames, "securityGroupNames")));
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
*/
public NovaTemplateOptions securityGroupNames(Iterable<String> securityGroupNames) {
for (String groupName : checkNotNull(securityGroupNames, "securityGroupNames"))
@ -113,7 +131,27 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* Specifies the keypair used to run instances with
* @return the keypair to be used
*/
public String getKeyPairName() {
return keyPairName;
}
/**
* <h3>Note</h3>
*
* This requires that {@link NovaClient#getKeyPairExtensionForZone(String)} to return
* {@link Optional#isPresent present}
*
* @return true if auto generation of keypairs is enabled
*/
public boolean shouldGenerateKeyPair() {
return generateKeyPair;
}
/**
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
*/
public Set<String> getSecurityGroupNames() {
return securityGroupNames;
@ -129,7 +167,21 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* @see NovaTemplateOptions#shouldGenerateKeyPair()
*/
public static NovaTemplateOptions generateKeyPair(boolean 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
*/
public static NovaTemplateOptions securityGroupNames(String... groupNames) {
NovaTemplateOptions options = new NovaTemplateOptions();
@ -137,7 +189,7 @@ public class NovaTemplateOptions extends TemplateOptions implements Cloneable {
}
/**
* @see CreateServerOptions#getSecurityGroupNames
* @see org.jclouds.openstack.nova.v1_1.options.CreateServerOptions#getSecurityGroupNames
*/
public static NovaTemplateOptions securityGroupNames(Iterable<String> groupNames) {
NovaTemplateOptions options = new NovaTemplateOptions();

View File

@ -47,6 +47,7 @@ import org.jclouds.concurrent.Futures;
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.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.ZoneAndName;
import org.jclouds.openstack.nova.v1_1.domain.zonescoped.ZoneSecurityGroupNameAndPorts;
@ -66,6 +67,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
private final AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode;
private final LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache;
private final LoadingCache<ZoneAndName, KeyPair> keyPairCache;
private final NovaClient novaClient;
private final Provider<TemplateBuilder> templateBuilderProvider;
@ -78,11 +80,13 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
@Named(Constants.PROPERTY_USER_THREADS) ExecutorService executor,
Provider<TemplateBuilder> templateBuilderProvider,
AllocateAndAddFloatingIpToNode allocateAndAddFloatingIpToNode,
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache, NovaClient novaClient) {
LoadingCache<ZoneAndName, SecurityGroupInZone> securityGroupCache,
LoadingCache<ZoneAndName, KeyPair> keyPairCache, NovaClient novaClient) {
super(addNodeWithTagStrategy, listNodesStrategy, nodeNamingConvention, executor,
customizeNodeAndAddToGoodMapOrPutExceptionIntoBadMapFactory);
this.templateBuilderProvider = checkNotNull(templateBuilderProvider, "templateBuilderProvider");
this.securityGroupCache = checkNotNull(securityGroupCache, "securityGroupCache");
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
this.allocateAndAddFloatingIpToNode = checkNotNull(allocateAndAddFloatingIpToNode,
"allocateAndAddFloatingIpToNode");
this.novaClient = checkNotNull(novaClient, "novaClient");
@ -93,9 +97,6 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
Map<NodeMetadata, Exception> badNodes, Multimap<NodeMetadata, CustomizationResponse> customizationResponses) {
// ensure we don't mutate the input template
Template mutableTemplate = templateBuilderProvider.get().fromTemplate(template).build();
// TODO: make NovaTemplateOptions with the following:
// security group, key pair
NovaTemplateOptions templateOptions = NovaTemplateOptions.class.cast(mutableTemplate.getOptions());
String zone = mutableTemplate.getLocation().getId();
@ -106,6 +107,17 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
templateOptions);
}
if (templateOptions.shouldGenerateKeyPair()) {
checkArgument(novaClient.getKeyPairExtensionForZone(zone).isPresent(),
"Key Pairs are required by options, but the extension is not available! options: %s",
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();
List<Integer> inboundPorts = Ints.asList(templateOptions.getInboundPorts());
if (templateOptions.getSecurityGroupNames().size() > 0) {

View File

@ -232,7 +232,7 @@ public class CreateServerOptions implements MapBinder {
public static class Builder {
/**
* @see CreateServerOptions#withFile(String,byte [])
* @see CreateServerOptions#withFile(String, byte[])
*/
public static CreateServerOptions withFile(String path, byte[] contents) {
CreateServerOptions options = new CreateServerOptions();

View File

@ -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 + ")";
}
};
}
}

View File

@ -36,4 +36,10 @@ public class NovaConstants {
*/
public static final String PROPERTY_NOVA_AUTO_ALLOCATE_FLOATING_IPS = "jclouds.openstack-nova.auto-allocate-floating-ips";
/**
* Whenever a node is created, automatically generate keypairs for groups, as needed, also
* delete the keypair(s) when the last node in the group is destroyed.
*/
public static final String PROPERTY_NOVA_AUTO_GENERATE_KEYPAIRS = "jclouds.openstack-nova.auto-generate-keypairs";
}

View File

@ -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);
}
}

View File

@ -18,12 +18,7 @@
*/
package org.jclouds.openstack.nova.v1_1.compute.options;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.authorizePublicKey;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.autoAssignFloatingIp;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.blockOnPort;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.inboundPorts;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.installPrivateKey;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.securityGroupNames;
import static org.jclouds.openstack.nova.v1_1.compute.options.NovaTemplateOptions.Builder.*;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
@ -110,6 +105,24 @@ public class NovaTemplateOptionsTest {
assert options.shouldAutoAssignFloatingIp();
}
@Test
public void testGenerateKeyPairDefault() {
NovaTemplateOptions options = new NovaTemplateOptions();
assert !options.shouldGenerateKeyPair();
}
@Test
public void testGenerateKeyPair() {
NovaTemplateOptions options = new NovaTemplateOptions().generateKeyPair(true);
assert options.shouldGenerateKeyPair();
}
@Test
public void testGenerateKeyPairStatic() {
NovaTemplateOptions options = generateKeyPair(true);
assert options.shouldGenerateKeyPair();
}
// superclass tests
@Test(expectedExceptions = IllegalArgumentException.class)
public void testinstallPrivateKeyBadFormat() {