com.cloudera.director.azure.compute.provider.CleanUpTask.java Source code

Java tutorial

Introduction

Here is the source code for com.cloudera.director.azure.compute.provider.CleanUpTask.java

Source

/*
 * Copyright (c) 2016 Cloudera, Inc.
 *
 * Licensed 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 com.cloudera.director.azure.compute.provider;

import com.cloudera.director.azure.compute.instance.TaskResult;
import com.microsoft.azure.management.compute.models.ComputeOperationResponse;
import com.microsoft.azure.management.compute.models.VirtualMachine;
import com.microsoft.azure.utility.ResourceContext;
import com.microsoft.windowsazure.core.OperationResponse;
import com.microsoft.windowsazure.exception.ServiceException;

import java.io.IOException;
import java.net.URISyntaxException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Task to cleanup VM and its supporting resources (NIC, public IP, storage account).
 * <p/>
 * There are 2 types of cleanup tasks:
 * 1. VM is successfully created and live.
 * Cleanup task will delete the VM and then delete the VM's supporting resources.
 * 2. VM is not created but its supporting resources are created.
 * Cleanup task will delete the supporting resources.
 */
public class CleanUpTask extends AbstractAzureComputeProviderTask implements Callable<TaskResult> {
    private ResourceContext context;
    private String resourceGroup;
    private VirtualMachine vm; // this is not null if we have a live VM
    private boolean isPublicIPConfigured;
    private DateTime startTime;
    private static final Logger LOG = LoggerFactory.getLogger(CleanUpTask.class);
    private int azureOperationPollingTimeout;

    /**
     * Creates a CleanUpTask.
     *
     * NOTE: This method assume the VM is properly created and the VM object contain all the info
     * needed for proper cleanup.
     * @param resourceGroup         Azure Resource Group name
     * @param vm                    VirtualMachine info
     * @param computeProviderHelper
     * @param isPublicIPConfigured  does the template define a public IP that should be cleaned up
     */
    public CleanUpTask(String resourceGroup, VirtualMachine vm, AzureComputeProviderHelper computeProviderHelper,
            boolean isPublicIPConfigured, int azureOperationPollingTimeout) {
        this.resourceGroup = resourceGroup;
        this.vm = vm;
        this.computeProviderHelper = computeProviderHelper;
        this.isPublicIPConfigured = isPublicIPConfigured;
        this.startTime = DateTime.now();
        this.azureOperationPollingTimeout = azureOperationPollingTimeout;
    }

    /**
     * Creates a CleanUpTask.
     *
     * NOTE: VM object may or may not be created properly, use info in the resource context to delete
     * any left over resources.
     * @param resourceGroup Azure Resource Group name
     * @param context       ResourceContext info
     * @param computeProviderHelper
     * @param isPublicIPConfigured
     */
    public CleanUpTask(String resourceGroup, ResourceContext context,
            AzureComputeProviderHelper computeProviderHelper, boolean isPublicIPConfigured,
            int azureOperationPollingTimeout) {
        this.resourceGroup = resourceGroup;
        this.context = context;
        this.computeProviderHelper = computeProviderHelper;
        this.isPublicIPConfigured = isPublicIPConfigured;
        this.startTime = DateTime.now();
        this.vm = null;
        this.azureOperationPollingTimeout = azureOperationPollingTimeout;
    }

    public TaskResult call() {
        // Got a live VM, try delete it and then its supporting resources.
        if (vm != null) {
            return deleteUsingVmInfo();
        }
        // We got context delete using info in the context.
        // This is a MUST in the case where VM didn't create successfully,
        // this will clean up the other resources.
        if (context != null) {
            return deleteUsingContextInfo();
        }

        // Programming error.
        throw new RuntimeException("Programming Error in CleanUpTask.");
    }

    /**
     * Deletes a VM first and then its supporting resources using VirtualMachine info.
     *
     * @return result of CleanUpTask
     */
    private TaskResult deleteUsingVmInfo() {
        if (vm == null) {
            LOG.error("VM identifier is null, can not delete VM and associated resources.");
            return new TaskResult(false, null);
        }

        // Create ResourceContext to hold VM information, verify the required resource info is present.
        ResourceContext ctx = computeProviderHelper.getResourceContextFromVm(resourceGroup, vm);
        try {
            ctx.setNetworkInterfaceName(computeProviderHelper.getNicNameFromVm(vm));
            ctx.setStorageAccountName(computeProviderHelper.getStorageAccountFromVM(vm));
            ctx.setAvailabilitySetName(computeProviderHelper.getAvailabilitySetNameFromVm(vm));
        } catch (Exception e) {
            LOG.error("VirtualMachine info for VM{} is malformed.", vm.getName(), e);
            return new TaskResult(false, ctx);
        }

        String vmName = vm.getName();
        try {
            // Azure SDK throws ServiceException if VM does not exist in the RG
            computeProviderHelper.getVirtualMachineStatus(resourceGroup, vmName);
        } catch (ServiceException | IOException | URISyntaxException e) {
            LOG.debug("Failed to find VM: {} in resource group: {} due to error, can't delete it.", vmName,
                    resourceGroup, e);
            // local context delete will/should take care of the rest
            return new TaskResult(false, ctx);
        }

        LOG.info("Begin deleting VM: {} in resource group: {}.", vm.getName(), resourceGroup);
        int successCount = 0;
        try {
            ComputeOperationResponse response = computeProviderHelper.beginDeleteVirtualMachine(resourceGroup, vm);
            successCount = pollPendingOperation(response, azureOperationPollingTimeout, defaultSleepIntervalInSec,
                    LOG, vm.getName());
        } catch (IOException | ServiceException e) {
            LOG.error("Failed to delete VM {}. Please check Azure portal for any not deleted resources.",
                    vm.getName(), e);
        } catch (InterruptedException e) {
            LOG.error("Deletion of VM {} is interrupted. Please check Azure portal for any remaining resources.",
                    vm.getName(), e);
        }

        if (successCount != 1) {
            return new TaskResult(false, ctx);
        }

        // Delete the rest of the resources in any order should be fine
        OperationResponse result;
        String nicName = computeProviderHelper.getNicNameFromVm(vm);
        try {
            result = computeProviderHelper.beginDeleteNetworkResourcesOnVM(resourceGroup, vm, isPublicIPConfigured);
            LOG.debug("VM: {}. Delete NetworkInterface {} status code: {}.", vmName, nicName,
                    result.getStatusCode());
        } catch (IOException | ServiceException | ExecutionException | InterruptedException e) {
            LOG.error("VM: {}. Delete NetworkInterface {} encountered error:", vmName, nicName, e);
        }

        String saName = computeProviderHelper.getStorageAccountFromVM(vm);
        try {
            result = computeProviderHelper.beginDeleteStorageAccountOnVM(resourceGroup, vm);
            LOG.debug("VM: {}. Delete StorageAccount {} status code: {}.", vmName, saName, result.getStatusCode());
        } catch (IOException | ServiceException e) {
            LOG.error("VM: {}. Delete StorageAccount {} encountered error:", vmName, saName, e);
        }

        long timeSeconds = (DateTime.now().getMillis() - startTime.getMillis()) / 1000;
        LOG.info("Delete VM {} and its resources took {} seconds.", vm.getName(), timeSeconds);

        return new TaskResult(true, ctx);
    }

    /**
     * Delete VM first and then its supporting resources using ResourceContext info.
     *
     * VM may not have been allocated successfully in this case.
     *
     * @return result of CleanUpTask
     */
    private TaskResult deleteUsingContextInfo() {
        boolean hasError = false;
        String vmName = "UNKNOWN_VM";

        LOG.debug("Begin clean up with ResourceContext used in VM creation");
        if (context.getVMInput() != null) {
            // Context contains VirtualMachine info, try using it to delete first.
            this.vm = context.getVMInput();
            vmName = vm.getName();
            TaskResult deleteVmResult = deleteUsingVmInfo();

            // VM and its supporting resources deleted, all done.
            if (deleteVmResult.isSuccessful()) {
                LOG.info("VM {} and its resources have been cleaned up.", vmName);
                return deleteVmResult;
            }

            // Fall through and cleanup VM resources (NIC, Public IP, Storage Acct etc.)
        }

        OperationResponse result;
        // clean up individual resources if the above steps failed
        if (context.getStorageAccountName() != null) {
            String saName = context.getStorageAccountName();
            try {
                result = computeProviderHelper.beginDeleteStorageAccountByName(resourceGroup, saName);
                LOG.debug("VM: {}. Delete StorageAccount {} status code: {}.", vmName, saName,
                        result.getStatusCode());
            } catch (IOException | ServiceException e) {
                hasError = true;
                LOG.error("VM: {}. Delete StorageAccount {} failed:", vmName, saName, e);
            }
        }

        if (context.getNetworkInterface() != null) {
            String nicName = context.getNetworkInterfaceName();
            try {
                result = computeProviderHelper.beginDeleteNetworkInterfaceByName(resourceGroup, nicName);
                LOG.debug("VM: {}. Delete NetworkInterface {} status code: {}.", vmName, nicName,
                        result.getStatusCode());
            } catch (IOException | ExecutionException | InterruptedException e) {
                hasError = true;
                LOG.error("VM: {}. Delete NetworkInterface {} encountered error:", vmName, nicName, e);
            }
        }

        if (isPublicIPConfigured && context.getPublicIpAddress() != null) {
            String pip = context.getPublicIpName();
            try {
                result = computeProviderHelper.beginDeletePublicIpAddressByName(resourceGroup, pip);
                LOG.debug("VM: {}. Delete PublicIP {} status code: {}.", vmName, pip, result.getStatusCode());
            } catch (InterruptedException | ExecutionException | IOException e) {
                hasError = true;
                LOG.error("VM: {}. Delete PublicIP {} encountered error:", vmName, pip, e);
            }
        } else {
            LOG.debug(
                    "Skipping delete of public IP address: isPublicIPConfigured {}; "
                            + "context.getPublicIpAddress(): {}",
                    isPublicIPConfigured, context.getPublicIpAddress());
        }

        long timeSeconds = (DateTime.now().getMillis() - startTime.getMillis()) / 1000;
        LOG.info("Delete VM {} context resources took {} seconds.", vm.getName(), timeSeconds);

        return new TaskResult(!hasError, this.context);
    }
}