mirror of https://github.com/apache/jclouds.git
Merge pull request #503 from aplowe/master
openstack-nova: Auto key pair generation wiring into compute service and adaptor
This commit is contained in:
commit
0f15559f2c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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}.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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()";
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 + ")";
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in New Issue