mirror of https://github.com/apache/jclouds.git
improve roll-back strategy when floating-ip are not available
- improve destroyNodes to clean up securityGroups and keyPair created explicitly for that node - refactor clean up server in one place
This commit is contained in:
parent
100d1dac6c
commit
a4a255fa4a
|
@ -20,9 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_RUNNING;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_SUSPENDED;
|
||||
import static org.jclouds.compute.config.ComputeServiceProperties.TIMEOUT_NODE_TERMINATED;
|
||||
import static org.jclouds.openstack.nova.v2_0.predicates.KeyPairPredicates.nameMatches;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
@ -42,7 +39,6 @@ import org.jclouds.compute.domain.NodeMetadata;
|
|||
import org.jclouds.compute.domain.TemplateBuilder;
|
||||
import org.jclouds.compute.extensions.ImageExtension;
|
||||
import org.jclouds.compute.extensions.SecurityGroupExtension;
|
||||
import org.jclouds.compute.functions.GroupNamingConvention;
|
||||
import org.jclouds.compute.internal.BaseComputeService;
|
||||
import org.jclouds.compute.internal.PersistNodeCredentials;
|
||||
import org.jclouds.compute.options.TemplateOptions;
|
||||
|
@ -58,34 +54,18 @@ import org.jclouds.compute.strategy.ResumeNodeStrategy;
|
|||
import org.jclouds.compute.strategy.SuspendNodeStrategy;
|
||||
import org.jclouds.domain.Credentials;
|
||||
import org.jclouds.domain.Location;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.KeyPairApi;
|
||||
import org.jclouds.openstack.nova.v2_0.extensions.SecurityGroupApi;
|
||||
import org.jclouds.openstack.nova.v2_0.predicates.SecurityGroupPredicates;
|
||||
import org.jclouds.scriptbuilder.functions.InitAdminAccess;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||
|
||||
@Singleton
|
||||
public class NovaComputeService extends BaseComputeService {
|
||||
protected final NovaApi novaApi;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
protected final Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByRegionId;
|
||||
protected final GroupNamingConvention.Factory namingConvention;
|
||||
protected final CleanupServer cleanupServer;
|
||||
|
||||
@Inject
|
||||
protected NovaComputeService(ComputeServiceContext context, Map<String, Credentials> credentialStore,
|
||||
|
@ -102,69 +82,23 @@ public class NovaComputeService extends BaseComputeService {
|
|||
InitializeRunScriptOnNodeOrPlaceInBadMap.Factory initScriptRunnerFactory,
|
||||
RunScriptOnNode.Factory runScriptOnNodeFactory, InitAdminAccess initAdminAccess,
|
||||
PersistNodeCredentials persistNodeCredentials, Timeouts timeouts,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor, NovaApi novaApi,
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache,
|
||||
Function<Set<? extends NodeMetadata>, Multimap<String, String>> orphanedGroupsByRegionId,
|
||||
GroupNamingConvention.Factory namingConvention, Optional<ImageExtension> imageExtension,
|
||||
@Named(Constants.PROPERTY_USER_THREADS) ListeningExecutorService userExecutor,
|
||||
CleanupServer cleanupServer,
|
||||
Optional<ImageExtension> imageExtension,
|
||||
Optional<SecurityGroupExtension> securityGroupExtension) {
|
||||
super(context, credentialStore, images, sizes, locations, listNodesStrategy, getImageStrategy,
|
||||
getNodeMetadataStrategy, runNodesAndAddToSetStrategy, rebootNodeStrategy, destroyNodeStrategy,
|
||||
startNodeStrategy, stopNodeStrategy, templateBuilderProvider, templateOptionsProvider, nodeRunning,
|
||||
nodeTerminated, nodeSuspended, initScriptRunnerFactory, initAdminAccess, runScriptOnNodeFactory,
|
||||
persistNodeCredentials, timeouts, userExecutor, imageExtension, securityGroupExtension);
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.orphanedGroupsByRegionId = checkNotNull(orphanedGroupsByRegionId, "orphanedGroupsByRegionId");
|
||||
this.namingConvention = checkNotNull(namingConvention, "namingConvention");
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanUpIncidentalResourcesOfDeadNodes(Set<? extends NodeMetadata> deadNodes) {
|
||||
Multimap<String, String> regionToRegionAndGroupNames = orphanedGroupsByRegionId.apply(deadNodes);
|
||||
for (Map.Entry<String, Collection<String>> entry : regionToRegionAndGroupNames.asMap().entrySet()) {
|
||||
cleanOrphanedGroupsInRegion(ImmutableSet.copyOf(entry.getValue()), entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
protected void cleanOrphanedGroupsInRegion(Set<String> groups, String regionId) {
|
||||
cleanupOrphanedSecurityGroupsInRegion(groups, regionId);
|
||||
cleanupOrphanedKeyPairsInRegion(groups, regionId);
|
||||
}
|
||||
|
||||
private void cleanupOrphanedSecurityGroupsInRegion(Set<String> groups, String regionId) {
|
||||
Optional<? extends SecurityGroupApi> securityGroupApi = novaApi.getSecurityGroupApi(regionId);
|
||||
if (securityGroupApi.isPresent()) {
|
||||
for (String group : groups) {
|
||||
for (SecurityGroup securityGroup : Iterables.filter(securityGroupApi.get().list(),
|
||||
SecurityGroupPredicates.nameMatches(namingConvention.create().containsGroup(group)))) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionId, securityGroup.getName());
|
||||
logger.debug(">> deleting securityGroup(%s)", regionAndName);
|
||||
securityGroupApi.get().delete(securityGroup.getId());
|
||||
// TODO: test this clear happens
|
||||
securityGroupMap.invalidate(regionAndName);
|
||||
logger.debug("<< deleted securityGroup(%s)", regionAndName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanupOrphanedKeyPairsInRegion(Set<String> groups, String regionId) {
|
||||
Optional<? extends KeyPairApi> keyPairApi = novaApi.getKeyPairApi(regionId);
|
||||
if (keyPairApi.isPresent()) {
|
||||
for (String group : groups) {
|
||||
for (KeyPair pair : keyPairApi.get().list().filter(nameMatches(namingConvention.create().containsGroup(group)))) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionId, pair.getName());
|
||||
logger.debug(">> deleting keypair(%s)", regionAndName);
|
||||
keyPairApi.get().delete(pair.getName());
|
||||
// TODO: test this clear happens
|
||||
keyPairCache.invalidate(regionAndName);
|
||||
logger.debug("<< deleted keypair(%s)", regionAndName);
|
||||
}
|
||||
keyPairCache.invalidate(RegionAndName.fromRegionAndName(regionId,
|
||||
namingConvention.create().sharedNameForGroup(group)));
|
||||
}
|
||||
for (NodeMetadata deadNode : deadNodes) {
|
||||
cleanupServer.apply(deadNode.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ import static java.lang.String.format;
|
|||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.jclouds.compute.util.ComputeServiceUtils.metadataAndTagsAsCommaDelimitedValue;
|
||||
import static org.jclouds.util.Predicates2.retry;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
@ -40,6 +39,7 @@ import org.jclouds.domain.LoginCredentials;
|
|||
import org.jclouds.location.Region;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.RemoveFloatingIpFromNodeAndDeallocate;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.options.NovaTemplateOptions;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet;
|
||||
|
@ -81,16 +81,19 @@ public class NovaComputeServiceAdapter implements
|
|||
protected final Supplier<Set<String>> regionIds;
|
||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
protected final CleanupServer cleanupServer;
|
||||
|
||||
|
||||
@Inject
|
||||
public NovaComputeServiceAdapter(NovaApi novaApi, @Region Supplier<Set<String>> regionIds,
|
||||
RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache) {
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache, CleanupServer cleanupServer) {
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.regionIds = checkNotNull(regionIds, "regionIds");
|
||||
this.removeFloatingIpFromNodeAndDeallocate = checkNotNull(removeFloatingIpFromNodeAndDeallocate,
|
||||
"removeFloatingIpFromNodeAndDeallocate");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,6 +133,7 @@ public class NovaComputeServiceAdapter implements
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
final String regionId = template.getLocation().getId();
|
||||
String imageId = template.getImage().getProviderId();
|
||||
String flavorId = template.getHardware().getProviderId();
|
||||
|
@ -158,7 +162,7 @@ public class NovaComputeServiceAdapter implements
|
|||
.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Override
|
||||
public Iterable<FlavorInRegion> listHardwareProfiles() {
|
||||
Builder<FlavorInRegion> builder = ImmutableSet.builder();
|
||||
for (final String regionId : regionIds.get()) {
|
||||
|
@ -262,15 +266,7 @@ public class NovaComputeServiceAdapter implements
|
|||
|
||||
@Override
|
||||
public void destroyNode(String id) {
|
||||
RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
|
||||
if (novaApi.getFloatingIPApi(regionAndId.getRegion()).isPresent()) {
|
||||
try {
|
||||
removeFloatingIpFromNodeAndDeallocate.apply(regionAndId);
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn(e, "<< error removing and deallocating ip from node(%s): %s", id, e.getMessage());
|
||||
}
|
||||
}
|
||||
novaApi.getServerApi(regionAndId.getRegion()).delete(regionAndId.getId());
|
||||
checkState(cleanupServer.apply(id), "server(%s) still there after deleting!?", id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -51,6 +51,7 @@ import org.jclouds.openstack.nova.v2_0.compute.NovaComputeService;
|
|||
import org.jclouds.openstack.nova.v2_0.compute.NovaComputeServiceAdapter;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaImageExtension;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.extensions.NovaSecurityGroupExtension;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CleanupServer;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.CreateSecurityGroupIfNeeded;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.FlavorInRegionToHardware;
|
||||
import org.jclouds.openstack.nova.v2_0.compute.functions.ImageInRegionToImage;
|
||||
|
@ -159,6 +160,9 @@ public class NovaComputeServiceContextModule extends
|
|||
|
||||
bind(new TypeLiteral<SecurityGroupExtension>() {
|
||||
}).to(NovaSecurityGroupExtension.class);
|
||||
|
||||
bind(new TypeLiteral<Function<String, Boolean>>() {
|
||||
}).to(CleanupServer.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -44,6 +44,7 @@ import com.google.common.base.Objects;
|
|||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
|
@ -61,13 +62,15 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
private final Predicate<AtomicReference<NodeMetadata>> nodeRunning;
|
||||
private final NovaApi novaApi;
|
||||
private final LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache;
|
||||
private final CleanupServer cleanupServer;
|
||||
|
||||
@Inject
|
||||
public AllocateAndAddFloatingIpToNode(@Named(TIMEOUT_NODE_RUNNING) Predicate<AtomicReference<NodeMetadata>> nodeRunning,
|
||||
NovaApi novaApi, @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache) {
|
||||
NovaApi novaApi, @Named("FLOATINGIP") LoadingCache<RegionAndId, Iterable<? extends FloatingIP>> floatingIpCache, CleanupServer cleanupServer) {
|
||||
this.nodeRunning = checkNotNull(nodeRunning, "nodeRunning");
|
||||
this.novaApi = checkNotNull(novaApi, "novaApi");
|
||||
this.floatingIpCache = checkNotNull(floatingIpCache, "floatingIpCache");
|
||||
this.cleanupServer = checkNotNull(cleanupServer, "cleanupServer");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,6 +84,7 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
|
||||
Optional<FloatingIP> ip = allocateFloatingIPForNode(floatingIpApi, poolNames, node.getId());
|
||||
if (!ip.isPresent()) {
|
||||
cleanupServer.apply(node.getId());
|
||||
throw new InsufficientResourcesException("Failed to allocate a FloatingIP for node(" + node.getId() + ")");
|
||||
}
|
||||
logger.debug(">> adding floatingIp(%s) to node(%s)", ip.get().getIp(), node.getId());
|
||||
|
@ -88,7 +92,7 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
floatingIpApi.addToServer(ip.get().getIp(), node.getProviderId());
|
||||
|
||||
input.get().getNodeMetadata().set(NodeMetadataBuilder.fromNodeMetadata(node).publicAddresses(ImmutableSet.of(ip.get().getIp())).build());
|
||||
floatingIpCache.invalidate(RegionAndId.fromSlashEncoded(node.getId()));
|
||||
floatingIpCache.asMap().putIfAbsent(RegionAndId.fromSlashEncoded(node.getId()), ImmutableList.of(ip.get()));
|
||||
return input.get().getNodeMetadata();
|
||||
}
|
||||
|
||||
|
@ -112,7 +116,7 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
ip = floatingIpApi.allocateFromPool(poolName);
|
||||
if (ip != null)
|
||||
return Optional.of(ip);
|
||||
} catch (InsufficientResourcesException ire){
|
||||
} catch (InsufficientResourcesException ire) {
|
||||
logger.trace("<< [%s] failed to allocate floating IP from pool %s for node(%s)", ire.getMessage(), poolName, nodeID);
|
||||
}
|
||||
}
|
||||
|
@ -140,6 +144,9 @@ public class AllocateAndAddFloatingIpToNode implements
|
|||
|
||||
}));
|
||||
// try to prevent multiple parallel launches from choosing the same ip.
|
||||
if (unassignedIps.isEmpty()) {
|
||||
return Optional.absent();
|
||||
}
|
||||
Collections.shuffle(unassignedIps);
|
||||
ip = Iterables.getLast(unassignedIps);
|
||||
return Optional.fromNullable(ip);
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jclouds.openstack.nova.v2_0.compute.functions;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.JCLOUDS_KP;
|
||||
import static org.jclouds.openstack.nova.v2_0.compute.strategy.ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet.JCLOUDS_SG;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.jclouds.compute.reference.ComputeServiceConstants;
|
||||
import org.jclouds.logging.Logger;
|
||||
import org.jclouds.openstack.nova.v2_0.NovaApi;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.KeyPair;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.SecurityGroup;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.ServerWithSecurityGroups;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndId;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.RegionAndName;
|
||||
import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
|
||||
@Singleton
|
||||
public class CleanupServer implements Function<String, Boolean> {
|
||||
|
||||
@Resource
|
||||
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
|
||||
protected Logger logger = Logger.NULL;
|
||||
|
||||
protected final NovaApi novaApi;
|
||||
protected final RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
|
||||
@Inject
|
||||
public CleanupServer(NovaApi novaApi, RemoveFloatingIpFromNodeAndDeallocate removeFloatingIpFromNodeAndDeallocate,
|
||||
LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupMap,
|
||||
LoadingCache<RegionAndName, KeyPair> keyPairCache) {
|
||||
this.novaApi = novaApi;
|
||||
this.removeFloatingIpFromNodeAndDeallocate = removeFloatingIpFromNodeAndDeallocate;
|
||||
this.securityGroupMap = checkNotNull(securityGroupMap, "securityGroupMap");
|
||||
this.keyPairCache = checkNotNull(keyPairCache, "keyPairCache");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean apply(String id) {
|
||||
final RegionAndId regionAndId = RegionAndId.fromSlashEncoded(id);
|
||||
ServerWithSecurityGroups server = novaApi.getServerWithSecurityGroupsApi(regionAndId.getRegion()).get().get(regionAndId.getId());
|
||||
|
||||
if (novaApi.getFloatingIPApi(regionAndId.getRegion()).isPresent()) {
|
||||
try {
|
||||
removeFloatingIpFromNodeAndDeallocate.apply(regionAndId);
|
||||
} catch (RuntimeException e) {
|
||||
logger.warn(e, "<< error removing and deallocating ip from node(%s): %s", id, e.getMessage());
|
||||
}
|
||||
}
|
||||
if (containsMetadata(server, JCLOUDS_KP)) {
|
||||
if (novaApi.getKeyPairApi(regionAndId.getRegion()).isPresent()) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionAndId.getRegion(), server.getKeyName());
|
||||
logger.debug(">> deleting keypair(%s)", regionAndName);
|
||||
novaApi.getKeyPairApi(regionAndId.getRegion()).get().delete(server.getKeyName());
|
||||
// TODO: test this clear happens
|
||||
keyPairCache.invalidate(regionAndName);
|
||||
logger.debug("<< deleted keypair(%s)", regionAndName);
|
||||
}
|
||||
}
|
||||
|
||||
boolean serverDeleted = novaApi.getServerApi(regionAndId.getRegion()).delete(regionAndId.getId());
|
||||
|
||||
if (containsMetadata(server, JCLOUDS_SG)) {
|
||||
for (final String securityGroupName : server.getSecurityGroupNames()) {
|
||||
for (SecurityGroup securityGroup : novaApi.getSecurityGroupApi(regionAndId.getRegion()).get().list().toList()) {
|
||||
if (securityGroup.getName().equalsIgnoreCase(securityGroupName)) {
|
||||
if (novaApi.getSecurityGroupApi(regionAndId.getRegion()).isPresent()) {
|
||||
RegionAndName regionAndName = RegionAndName.fromRegionAndName(regionAndId.getRegion(), securityGroup.getName());
|
||||
logger.debug(">> deleting securityGroup(%s)", regionAndName);
|
||||
novaApi.getSecurityGroupApi(regionAndId.getRegion()).get().delete(securityGroup.getId());
|
||||
// TODO: test this clear happens
|
||||
securityGroupMap.invalidate(regionAndName);
|
||||
logger.debug("<< deleted securityGroup(%s)", regionAndName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return serverDeleted;
|
||||
}
|
||||
|
||||
private boolean containsMetadata(ServerWithSecurityGroups server, String key) {
|
||||
if (server == null || server.getMetadata() == null || server.getMetadata().get("jclouds_tags") == null)
|
||||
return false;
|
||||
return server.getMetadata().get("jclouds_tags").contains(key);
|
||||
}
|
||||
}
|
|
@ -52,6 +52,7 @@ import org.jclouds.openstack.nova.v2_0.domain.regionscoped.SecurityGroupInRegion
|
|||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.util.concurrent.Atomics;
|
||||
|
@ -63,6 +64,9 @@ import com.google.common.util.concurrent.ListeningExecutorService;
|
|||
public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddToSet extends
|
||||
CreateNodesWithGroupEncodedIntoNameThenAddToSet {
|
||||
|
||||
public static final String JCLOUDS_SG = "jclouds_securityGroup";
|
||||
public static final String JCLOUDS_KP = "jclouds_keyPair";
|
||||
|
||||
private final AllocateAndAddFloatingIpToNode createAndAddFloatingIpToNode;
|
||||
protected final LoadingCache<RegionAndName, SecurityGroupInRegion> securityGroupCache;
|
||||
protected final LoadingCache<RegionAndName, KeyPair> keyPairCache;
|
||||
|
@ -98,11 +102,12 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
assert template.getOptions().equals(templateOptions) : "options didn't clone properly";
|
||||
|
||||
String region = mutableTemplate.getLocation().getId();
|
||||
ImmutableList.Builder<String> tagsBuilder = ImmutableList.builder();
|
||||
|
||||
if (templateOptions.shouldAutoAssignFloatingIp()) {
|
||||
checkArgument(novaApi.getFloatingIPApi(region).isPresent(),
|
||||
"Floating IPs are required by options, but the extension is not available! options: %s",
|
||||
templateOptions);
|
||||
"Floating IPs are required by options, but the extension is not available! options: %s",
|
||||
templateOptions);
|
||||
}
|
||||
|
||||
boolean keyPairExtensionPresent = novaApi.getKeyPairApi(region).isPresent();
|
||||
|
@ -113,6 +118,7 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
.sharedNameForGroup(group)));
|
||||
keyPairCache.asMap().put(RegionAndName.fromRegionAndName(region, keyPair.getName()), keyPair);
|
||||
templateOptions.keyPairName(keyPair.getName());
|
||||
tagsBuilder.add(JCLOUDS_KP);
|
||||
} else if (templateOptions.getKeyPairName() != null) {
|
||||
checkArgument(keyPairExtensionPresent,
|
||||
"Key Pairs are required by options, but the extension is not available! options: %s", templateOptions);
|
||||
|
@ -139,9 +145,11 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
throw Throwables.propagate(e.getCause());
|
||||
}
|
||||
templateOptions.securityGroups(securityGroupName);
|
||||
tagsBuilder.add(JCLOUDS_SG);
|
||||
}
|
||||
}
|
||||
templateOptions.userMetadata(ComputeServiceConstants.NODE_GROUP_KEY, group);
|
||||
templateOptions.tags(tagsBuilder.build());
|
||||
|
||||
return super.execute(group, count, mutableTemplate, goodNodes, badNodes, customizationResponses);
|
||||
}
|
||||
|
@ -168,4 +176,5 @@ public class ApplyNovaTemplateOptionsCreateNodesWithGroupEncodedIntoNameThenAddT
|
|||
return future;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -97,6 +97,14 @@ public class NovaErrorHandler implements HttpErrorHandler {
|
|||
exception = new ResourceNotFoundException(message, exception);
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
// this is needed as FloatingIPApi.allocateFromPool returns 500 when floating ips are over quota
|
||||
if (command.getCurrentRequest().getMethod().equals("POST") &&
|
||||
message.indexOf("The server has either erred or is incapable of performing the requested operation.") != -1 &&
|
||||
command.getCurrentRequest().getEndpoint().getPath().indexOf("os-floating-ips") != -1) {
|
||||
exception = new InsufficientResourcesException(message, exception);
|
||||
}
|
||||
break;
|
||||
case 413:
|
||||
if (content == null) {
|
||||
exception = new InsufficientResourcesException(message, exception);
|
||||
|
@ -111,7 +119,7 @@ public class NovaErrorHandler implements HttpErrorHandler {
|
|||
* Build an exception from the response. If it contains the JSON payload then
|
||||
* that is parsed to create a {@link RetryAfterException}, otherwise a
|
||||
* {@link InsufficientResourcesException} is returned
|
||||
*
|
||||
*
|
||||
*/
|
||||
private Exception parseAndBuildRetryException(String json, String message, Exception exception) {
|
||||
Set<String> retryFields = ImmutableSet.of("retryAfter", "retryAt");
|
||||
|
|
|
@ -238,8 +238,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.addHeader("X-Auth-Token", authToken)
|
||||
.payload(
|
||||
payloadFromStringWithContentType(
|
||||
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
|
||||
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"{\"server\":{\"name\":\"test-1\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds-group\":\"test\",\"jclouds_tags\":\"jclouds_keyPair,jclouds_securityGroup\"},\"key_name\":\"jclouds-test-0\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"application/json")).build();
|
||||
|
||||
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
|
||||
|
@ -293,8 +292,7 @@ public class NovaComputeServiceExpectTest extends BaseNovaComputeServiceExpectTe
|
|||
.addHeader("X-Auth-Token", authToken)
|
||||
.payload(
|
||||
payloadFromStringWithContentType(
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\"," +
|
||||
"\"metadata\":{\"jclouds-group\":\"test\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"{\"server\":{\"name\":\"test-0\",\"imageRef\":\"14\",\"flavorRef\":\"1\",\"metadata\":{\"jclouds-group\":\"test\",\"jclouds_tags\":\"jclouds_securityGroup\"},\"key_name\":\"fooPair\",\"security_groups\":[{\"name\":\"jclouds-test\"}]}}",
|
||||
"application/json")).build();
|
||||
|
||||
HttpResponse createdServer = HttpResponse.builder().statusCode(202).message("HTTP/1.1 202 Accepted")
|
||||
|
|
Loading…
Reference in New Issue