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

Java tutorial

Introduction

Here is the source code for com.cloudera.director.azure.compute.provider.CreateVMTask.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.cloudera.director.azure.utils.AzureVmImageInfo;
import com.cloudera.director.azure.utils.VmCreationParameters;
import com.microsoft.azure.management.compute.models.AvailabilitySet;
import com.microsoft.azure.management.compute.models.AvailabilitySetReference;
import com.microsoft.azure.management.compute.models.CachingTypes;
import com.microsoft.azure.management.compute.models.ComputeOperationResponse;
import com.microsoft.azure.management.compute.models.DiskCreateOptionTypes;
import com.microsoft.azure.management.compute.models.HardwareProfile;
import com.microsoft.azure.management.compute.models.ImageReference;
import com.microsoft.azure.management.compute.models.NetworkInterfaceReference;
import com.microsoft.azure.management.compute.models.NetworkProfile;
import com.microsoft.azure.management.compute.models.OSDisk;
import com.microsoft.azure.management.compute.models.OSProfile;
import com.microsoft.azure.management.compute.models.Plan;
import com.microsoft.azure.management.compute.models.PurchasePlan;
import com.microsoft.azure.management.compute.models.StorageProfile;
import com.microsoft.azure.management.compute.models.VirtualHardDisk;
import com.microsoft.azure.management.compute.models.VirtualMachine;
import com.microsoft.azure.management.compute.models.VirtualMachineImage;
import com.microsoft.azure.management.network.models.NetworkSecurityGroup;
import com.microsoft.azure.management.network.models.Subnet;
import com.microsoft.azure.management.network.models.VirtualNetwork;
import com.microsoft.azure.management.storage.models.AccountType;
import com.microsoft.azure.utility.ComputeHelper;
import com.microsoft.azure.utility.ResourceContext;

import java.util.ArrayList;
import java.util.concurrent.Callable;

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

/**
 * Task to create a VM.
 *
 * If there is any failure, the context used to create VM is returned as part of the task result.
 */
public class CreateVMTask extends AbstractAzureComputeProviderTask implements Callable<TaskResult> {
    private ResourceContext context;
    // VM resources
    private VirtualNetwork vnet;
    private NetworkSecurityGroup nsg;
    private AvailabilitySet as;
    private String vmSize;
    private String vmName;
    private String vmNamePrefix;
    private String instanceId;
    private String fqdnSuffix;
    private String adminName;
    private String sshPublicKey;
    private Subnet subnet;
    private AccountType storageAccountType;
    private int dataDiskCount;
    private int dataDiskSizeGiB;
    private AzureVmImageInfo imageInfo;

    private static final Logger LOG = LoggerFactory.getLogger(CreateVMTask.class);
    private DateTime startTime;
    private int azureOperationPollingTimeout;

    // N.B.: `vmName` is composed of the user defined vm name prefix from the director template
    // and the instance id (a UUID) supplied by director
    public CreateVMTask(ResourceContext context, VmCreationParameters parameters, int azureOperationPollingTimeout,
            AzureComputeProviderHelper computeProviderHelper) {
        this.context = context;
        this.vnet = parameters.getVnet();
        this.nsg = parameters.getNsg();
        this.as = parameters.getAvailabilitySet();
        this.vmSize = parameters.getVmSize();
        this.vmNamePrefix = parameters.getVmNamePrefix();
        this.instanceId = parameters.getInstanceId();
        this.vmName = vmNamePrefix + "-" + instanceId;
        this.fqdnSuffix = parameters.getFqdnSuffix();
        this.adminName = parameters.getAdminName();
        this.sshPublicKey = parameters.getSshPublicKey();
        this.storageAccountType = parameters.getStorageAccountType();
        this.dataDiskCount = parameters.getDataDiskCount();
        this.dataDiskSizeGiB = parameters.getDataDiskSizeGiB();
        this.imageInfo = parameters.getImageInfo();
        this.computeProviderHelper = computeProviderHelper;
        this.startTime = DateTime.now();
        this.subnet = parameters.getSubnet();
        this.azureOperationPollingTimeout = azureOperationPollingTimeout;
    }

    /**
     * Request and prepare VM resources.
     *
     * NOTE: If any resource request has failed, this method (Azure SDK calls) will throw exception
     * and the CreateVMTask will fail. Cleanup of the resources is handled by the task submitter
     * using the ResourceContext in TaskResult.
     *
     * @throws Exception several Azure SDK API calls throws generic exceptions
     */
    private void requestResources() throws Exception {
        // AZURE_SDK Azure SDK API StorageHelper#createStorageAccount throws generic Exception
        computeProviderHelper.createAndSetStorageAccount(storageAccountType, context);
        LOG.info("Created StorageAccount: {}, type {}, for VM {}.", context.getStorageAccountName(),
                storageAccountType, vmName);

        // AZURE_SDK Azure SDK API NetworkHelper throws generic Exception
        context.setVirtualNetwork(vnet);
        computeProviderHelper.createAndSetNetworkInterface(context, subnet);
        LOG.info("Created NetworkInterface: {}, for VM {}.", context.getNetworkInterfaceName(), vmName);

        // AZURE_SDK Currently Create NSG will fail due to bug in Azure SDK.
        // Update NIC to use existing NSG.
        computeProviderHelper.setNetworkSecurityGroup(nsg, context);
        LOG.info("Use existing NetworkSecurityGroup: {}, for VM {}.", nsg.getName(), vmName);

        String vhdContainer = ComputeHelper.getVhdContainerUrl(context);
        String osVhduri = vhdContainer + String.format("/os%s.vhd", "osvhd");

        VirtualMachine vm = new VirtualMachine(context.getLocation());
        vm.setName(vmName);

        // Set tags
        if (context.getTags() != null) {
            vm.setTags(context.getTags());
        }

        vm.setType("Microsoft.Compute/virtualMachines");

        // Set availability set
        AvailabilitySetReference asRef = new AvailabilitySetReference();
        asRef.setReferenceUri(as.getId());
        vm.setAvailabilitySetReference(asRef);
        LOG.info("Use existing AvailabilitySet: {}, for VM {}.", as.getName(), vmName);

        //set hardware profile
        HardwareProfile hwProfile = new HardwareProfile();
        hwProfile.setVirtualMachineSize(vmSize);
        vm.setHardwareProfile(hwProfile);

        String publisher = imageInfo.getPublisher();
        String sku = imageInfo.getSku();
        String offer = imageInfo.getOffer();
        String version = imageInfo.getVersion();

        // Set storage profile
        StorageProfile sto = new StorageProfile();
        VirtualMachineImage vmimage = computeProviderHelper.getMarketplaceVMImage(context.getLocation(), imageInfo);
        ImageReference ir = new ImageReference();
        ir.setPublisher(publisher);
        ir.setOffer(offer);
        ir.setSku(sku);
        ir.setVersion(version);
        sto.setImageReference(ir);
        // This is a thread safe call as inputs are all local to this task
        PurchasePlan purchasePlan = computeProviderHelper.getPurchasePlan(vmimage);

        // Set purchase plan if the image has one attached. Certain images does not have purchase plan
        // attached.
        if (purchasePlan != null) {
            Plan plan = new Plan();
            plan.setName(purchasePlan.getName());
            plan.setProduct(purchasePlan.getProduct());
            plan.setPromotionCode(null);
            plan.setPublisher(purchasePlan.getPublisher());
            vm.setPlan(plan);
        } else {
            LOG.info("Image {} does not have purchase plan attached.", imageInfo);
        }

        // Setup storage, osdisk + datadisk
        VirtualHardDisk vhardDisk = new VirtualHardDisk();
        vhardDisk.setUri(osVhduri);
        OSDisk osDisk = new OSDisk("osdisk", vhardDisk, DiskCreateOptionTypes.FROMIMAGE);
        osDisk.setCaching(CachingTypes.READWRITE);
        sto.setOSDisk(osDisk);
        // This is a thread safe call as inputs are all local to this task
        sto.setDataDisks(computeProviderHelper.createDataDisks(dataDiskCount, dataDiskSizeGiB, vhdContainer));

        vm.setStorageProfile(sto);

        // Set network profile
        NetworkProfile networkProfile = new NetworkProfile();
        NetworkInterfaceReference nir = new NetworkInterfaceReference();
        nir.setReferenceUri(context.getNetworkInterface().getId());
        ArrayList<NetworkInterfaceReference> nirs = new ArrayList<>(1);
        nirs.add(nir);
        networkProfile.setNetworkInterfaces(nirs);
        vm.setNetworkProfile(networkProfile);

        String vmShortName = AzureComputeProviderHelper.getShortVMName(vmNamePrefix, instanceId);

        // Set os profile
        OSProfile osProfile = new OSProfile();
        osProfile.setAdminUsername(adminName);
        // This is a thread safe call as inputs are all local to this task
        osProfile.setComputerName(vmShortName + "." + fqdnSuffix);
        osProfile.setLinuxConfiguration(AzureComputeProviderHelper.createSshLinuxConfig(osProfile, sshPublicKey));
        vm.setOSProfile(osProfile);
        vm.setTags(context.getTags());
        context.setVMInput(vm);

        // set DNS label, forward & backward FQDN
        if (context.isCreatePublicIpAddress()) {
            computeProviderHelper.setPublicDNSInfo(context, vmShortName);
        }

        LOG.info("Successfully requested resources for VM {}.", vmName);
    }

    public TaskResult call() {
        boolean success = false;
        ComputeOperationResponse op;
        int successCount = 0;

        try {
            requestResources();
            op = computeProviderHelper.submitVmCreationOp(context);
        } catch (Exception e) {
            // AZURE_SDK Catch all exception from Azure SDK calls so that we can always cleanup.
            LOG.error("Failed to create VM {} due to:", vmName, e);
            return new TaskResult(false, this.context);
        }

        try {
            String vmName = context.getVMInput().getName();
            successCount = pollPendingOperation(op, azureOperationPollingTimeout, defaultSleepIntervalInSec, LOG,
                    vmName);
        } catch (InterruptedException e) {
            LOG.info("VM {} creation is interrupted.", vmName, e);
            return new TaskResult(false, this.context);
        }

        long timeSeconds = (DateTime.now().getMillis() - startTime.getMillis()) / 1000;

        if (successCount == 1) {
            /* FIXME temporarily disable VM script runner to speed up VM deployment.
            int scriptSuccessCount = pollPendingOperation(computeProviderHelper.createCustomizedScript(context),
                defaultTimeoutInSec, defaultSleepIntervalInSec, LOG);
            if (scriptSuccessCount == 1) {
              LOG.info("Script execution succeeded");
              exitCode = 0;
            }
            */

            success = true;
            LOG.info("Creation of VM {} succeeded after {} seconds.", vmName, timeSeconds);
        } else {
            LOG.error("Creation of VM {} failed after {} seconds.", vmName, timeSeconds);
        }

        return new TaskResult(success, this.context);
    }
}