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