blob: 3ca1a5db5f56fc7138d0e3788370b4ed47620433 [file] [log] [blame]
/*
* 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.azurecompute.arm.compute.strategy;
import static com.google.common.base.Predicates.not;
import static com.google.common.base.Predicates.notNull;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Maps.filterValues;
import static org.jclouds.azurecompute.arm.compute.AzureComputeServiceAdapter.AUTOGENERATED_IP_KEY;
import static org.jclouds.azurecompute.arm.config.AzureComputeProperties.TIMEOUT_RESOURCE_DELETED;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractName;
import static org.jclouds.azurecompute.arm.domain.IdReference.extractResourceGroup;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import org.jclouds.azurecompute.arm.AzureComputeApi;
import org.jclouds.azurecompute.arm.compute.domain.ResourceGroupAndName;
import org.jclouds.azurecompute.arm.domain.AvailabilitySet;
import org.jclouds.azurecompute.arm.domain.DataDisk;
import org.jclouds.azurecompute.arm.domain.IdReference;
import org.jclouds.azurecompute.arm.domain.IpConfiguration;
import org.jclouds.azurecompute.arm.domain.ManagedDiskParameters;
import org.jclouds.azurecompute.arm.domain.NetworkInterfaceCard;
import org.jclouds.azurecompute.arm.domain.NetworkProfile.NetworkInterface;
import org.jclouds.azurecompute.arm.domain.NetworkSecurityGroup;
import org.jclouds.azurecompute.arm.domain.OSDisk;
import org.jclouds.azurecompute.arm.domain.PublicIPAddress;
import org.jclouds.azurecompute.arm.domain.VirtualMachine;
import org.jclouds.azurecompute.arm.features.NetworkSecurityGroupApi;
import org.jclouds.compute.functions.GroupNamingConvention;
import org.jclouds.compute.reference.ComputeServiceConstants;
import org.jclouds.javax.annotation.Nullable;
import org.jclouds.logging.Logger;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
@Singleton
public class CleanupResources {
@Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL;
private final AzureComputeApi api;
private final Predicate<URI> resourceDeleted;
private final GroupNamingConvention.Factory namingConvention;
@Inject
CleanupResources(AzureComputeApi azureComputeApi, @Named(TIMEOUT_RESOURCE_DELETED) Predicate<URI> resourceDeleted,
GroupNamingConvention.Factory namingConvention) {
this.api = azureComputeApi;
this.resourceDeleted = resourceDeleted;
this.namingConvention = namingConvention;
}
public boolean cleanupNode(final String id) {
ResourceGroupAndName resourceGroupAndName = ResourceGroupAndName.fromSlashEncoded(id);
String resourceGroupName = resourceGroupAndName.resourceGroup();
VirtualMachine virtualMachine = api.getVirtualMachineApi(resourceGroupName).get(resourceGroupAndName.name());
if (virtualMachine == null) {
return true;
}
logger.debug(">> destroying %s ...", id);
boolean vmDeleted = deleteVirtualMachine(resourceGroupName, virtualMachine);
// We don't delete the network here, as it is global to the resource
// group. It will be deleted when the resource group is deleted
cleanupVirtualMachineNICs(virtualMachine);
cleanupManagedDisks(virtualMachine);
cleanupAvailabilitySetIfOrphaned(virtualMachine);
return vmDeleted;
}
public boolean cleanupVirtualMachineNICs(VirtualMachine virtualMachine) {
boolean deleted = true;
for (NetworkInterface nicRef : virtualMachine.properties().networkProfile().networkInterfaces()) {
String nicResourceGroup = extractResourceGroup(nicRef.id());
String nicName = extractName(nicRef.id());
NetworkInterfaceCard nic = api.getNetworkInterfaceCardApi(nicResourceGroup).get(nicName);
Iterable<IdReference> publicIps = getPublicIps(nic);
logger.debug(">> destroying nic %s...", nicName);
URI nicDeletionURI = api.getNetworkInterfaceCardApi(nicResourceGroup).delete(nicName);
deleted &= nicDeletionURI == null || resourceDeleted.apply(nicDeletionURI);
for (IdReference publicIp : publicIps) {
String publicIpResourceGroup = publicIp.resourceGroup();
String publicIpName = publicIp.name();
PublicIPAddress ip = api.getPublicIPAddressApi(publicIpResourceGroup).get(publicIpName);
if (ip.tags() != null && Boolean.parseBoolean(ip.tags().get(AUTOGENERATED_IP_KEY))) {
logger.debug(">> deleting public ip %s...", publicIpName);
deleted &= api.getPublicIPAddressApi(publicIpResourceGroup).delete(publicIpName);
}
}
}
return deleted;
}
public boolean cleanupManagedDisks(VirtualMachine virtualMachine) {
Map<String, URI> deleteJobs = new HashMap<String, URI>();
OSDisk osDisk = virtualMachine.properties().storageProfile().osDisk();
deleteManagedDisk(osDisk.managedDiskParameters(), deleteJobs);
for (DataDisk dataDisk : virtualMachine.properties().storageProfile().dataDisks()) {
deleteManagedDisk(dataDisk.managedDiskParameters(), deleteJobs);
}
Set<String> nonDeletedDisks = filterValues(deleteJobs, not(resourceDeleted)).keySet();
if (!nonDeletedDisks.isEmpty()) {
logger.warn(">> could not delete disks: %s", Joiner.on(',').join(nonDeletedDisks));
}
return nonDeletedDisks.isEmpty();
}
private void deleteManagedDisk(@Nullable ManagedDiskParameters managedDisk, Map<String, URI> deleteJobs) {
if (managedDisk != null) {
IdReference diskRef = IdReference.create(managedDisk.id());
logger.debug(">> deleting managed disk %s...", diskRef.name());
URI uri = api.getDiskApi(diskRef.resourceGroup()).delete(diskRef.name());
if (uri != null) {
deleteJobs.put(diskRef.name(), uri);
}
}
}
public boolean cleanupSecurityGroupIfOrphaned(String resourceGroup, String group) {
String name = namingConvention.create().sharedNameForGroup(group);
NetworkSecurityGroupApi sgapi = api.getNetworkSecurityGroupApi(resourceGroup);
boolean deleted = false;
try {
NetworkSecurityGroup securityGroup = sgapi.get(name);
if (securityGroup != null) {
List<NetworkInterfaceCard> nics = securityGroup.properties().networkInterfaces();
if (nics == null || nics.isEmpty()) {
logger.debug(">> deleting orphaned security group %s from %s...", name, resourceGroup);
try {
deleted = resourceDeleted.apply(sgapi.delete(name));
} catch (Exception ex) {
logger.warn(ex, ">> error deleting orphaned security group %s from %s...", name, resourceGroup);
}
}
}
} catch (Exception ex) {
logger.warn(ex, "Error deleting security groups for %s and group %s", resourceGroup, group);
}
return deleted;
}
public boolean cleanupAvailabilitySetIfOrphaned(VirtualMachine virtualMachine) {
boolean deleted = true;
IdReference availabilitySetRef = virtualMachine.properties().availabilitySet();
if (availabilitySetRef != null) {
String name = availabilitySetRef.name();
String resourceGroup = availabilitySetRef.resourceGroup();
AvailabilitySet availabilitySet = api.getAvailabilitySetApi(resourceGroup).get(name);
if (isOrphanedJcloudsAvailabilitySet(availabilitySet)) {
logger.debug(">> deleting orphaned availability set %s from %s...", name, resourceGroup);
URI uri = api.getAvailabilitySetApi(resourceGroup).delete(name);
deleted = uri == null || resourceDeleted.apply(uri);
}
}
return deleted;
}
public boolean deleteResourceGroupIfEmpty(String group) {
boolean deleted = false;
if (api.getResourceGroupApi().resources(group).isEmpty()) {
logger.debug(">> the resource group %s is empty. Deleting...", group);
deleted = resourceDeleted.apply(api.getResourceGroupApi().delete(group));
}
return deleted;
}
private Iterable<IdReference> getPublicIps(NetworkInterfaceCard nic) {
return filter(transform(nic.properties().ipConfigurations(), new Function<IpConfiguration, IdReference>() {
@Override
public IdReference apply(IpConfiguration input) {
return input.properties().publicIPAddress();
}
}), notNull());
}
private static boolean isOrphanedJcloudsAvailabilitySet(AvailabilitySet availabilitySet) {
// We check for the presence of the 'jclouds' tag to make sure we only
// delete availability sets that were automatically created by jclouds
return availabilitySet != null
&& availabilitySet.tags() != null
&& availabilitySet.tags().containsKey("jclouds")
&& (availabilitySet.properties().virtualMachines() == null || availabilitySet.properties()
.virtualMachines().isEmpty());
}
private boolean deleteVirtualMachine(String group, VirtualMachine virtualMachine) {
return resourceDeleted.apply(api.getVirtualMachineApi(group).delete(virtualMachine.name()));
}
}