Recently, I had to create a Cloud Template in Cloud Assembly which will enable the user to add disks to windows VM with a custom label, drive letter and size. It is fairly straight forward to add disks in the Cloud Template but the catch is that when the VM gets deployed, the disks are not added in the same sequence that the user requested in. For example the user requests the disks in the below order.

  • 100 GB, SQLDATA, D
  • 50 GB, SQLTEMP, E
  • 150 GB, SQLBACKUP, F

Due to the disks not being added in the same order they were requested, the label and drive letter gets assigned to the incorrect size of disk. This is by design and hopefully VMware fixes it soon. The disks in my scenario are getting initialised using an Ansible playbook.

To get around this, I followed the below steps.

  • Utilize the IaaS API call for the compute.
  • Make a "GET" call to the url "/iaas/api/machines/{id}/disks" to retrieve the user requested disks and in the same order too.
  • Shutdown the VM and change the disk unitNumber of the disks, before powering on the VM. I am adding 4 disks and the unitNumber in my case begins from 8, because 7 is reserved.

However, this can be altered to suit your needs if they are different, or you can detach and then attach the disks in the order you want them to.

In my Blueprint, I have created 4 disks like below.

  Cloud_vSphere_Disk_4:
    type: Cloud.vSphere.Disk
    properties:
      count: '${input.disk4Size == 0? 0:1}'
      capacityGb: '${input.disk4Size}'
  Cloud_vSphere_Disk_3:
    type: Cloud.vSphere.Disk
    properties:
      count: '${input.disk3Size == 0? 0:1}'
      capacityGb: '${input.disk3Size}'
  Cloud_vSphere_Disk_2:
    type: Cloud.vSphere.Disk
    properties:
      count: '${input.disk2Size == 0 ? 0:1}'
      capacityGb: '${input.disk2Size}'
  Cloud_vSphere_Disk_1:
    type: Cloud.vSphere.Disk
    properties:
      count: '${input.disk1Size == 0? 0:1}'
      capacityGb: '${input.disk1Size}'

Then, I added the disks to the Machine like below.

 attachedDisks: '${map_to_object(resource.Cloud_vSphere_Disk_1[*].id + resource.Cloud_vSphere_Disk_2[*].id + resource.Cloud_vSphere_Disk_3[*].id + resource.Cloud_vSphere_Disk_4[*].id,"source")}'

Drive letter and disk label are the variables that are passed to Ansible Playbook. These are also inputs in the Blueprint for the user to enter. Host variables are passed to Ansible like so.

 hostVariables: |
        disks:
          - { disk_number: 1, drive_letter: '${input.disk1DriveLetter}', new_label: '${input.disk1Label}'}
          - { disk_number: 2, drive_letter: '${input.disk2DriveLetter}', new_label: '${input.disk2Label}'}
          - { disk_number: 3, drive_letter: '${input.disk3DriveLetter}', new_label: '${input.disk3Label}'}
          - { disk_number: 4, drive_letter: '${input.disk4DriveLetter}', new_label: '${input.disk4Label}'}

As for the vRO code, I like to organize it into Actions and write my actions as services and call them from the workflow as objects. Below is the code for my Action.

function diskService() {

    this.getDisksByResourceID = function(resourceId) {
        try {
            var request = restHost.createRequest("GET", url + resourceId + "/disks/");
            request.setHeader("Authorization", "Bearer " + accessToken);
            request.setHeader("Content-Type", "application/json");
            request.setHeader("Cache-Control", "no-cache");
            var requestResponse = request.execute();
            if (requestResponse.statusCode != 200) {
                System.log ("REST operation failed: " + requestResponse.statusCode);
                throw "ERROR " + requestResponse.statusCode;
            }
        } catch (e) {
            throw ("Error: REST operation failed: " + e);
        }
        var responseContent = requestResponse.contentAsString;
        System.debug(responseContent);
        return responseContent;
    }

    //get user entered disks
    this.sortDisks = function(vcVm, resourceId, vcvirtualMachine) {
        var disks = this.getDisksByResourceID(resourceId);
        disks = JSON.parse(disks);
        var diskArray = [];
        for each (var disk in disks.content) {
            if (disk.name != "boot-disk"){
                diskArray.push(disk.name);
            }
        }
        System.log("user entered disks ==== " + diskArray);

        var devices = vcVm.config.hardware.device;
        var deviceConfigSpecs = new Array();
        //set unit number starting from 8, because 7 is reserved
        var n = 8;
        for (var j=0; j<diskArray.length; j++){
            for (var i=0; i<devices.length; i++){
                var is_disk = devices[i] instanceof VcVirtualDisk;
                if (is_disk){
                    if ((devices[i].backing.fileName.split('/')[1].split('.')[0]).indexOf(diskArray[j]) != -1) {
                        System.debug("Found disk : " + devices[i].backing.fileName.split('/')[1].split('.')[0]);
                        var deviceConfigSpec = new VcVirtualDeviceConfigSpec();
                        deviceConfigSpec.device = new VcVirtualDisk;
                        deviceConfigSpec.device = devices[i];
                        deviceConfigSpec.device.unitNumber = n;
                        n = n+1;
                        deviceConfigSpec.operation = VcVirtualDeviceConfigSpecOperation.edit;
                        deviceConfigSpecs.push(deviceConfigSpec);
                    }        
                }        
            }
        }
        var vmConfigSpec = new VcVirtualMachineConfigSpec();
        vmConfigSpec.deviceChange = deviceConfigSpecs;
        task = vcvirtualMachine.reconfigVM_Task(vmConfigSpec);
        
    }

    var url = "/iaas/api/machines/";
    var restHost = System.getModule("org.harjinder.dev").getConfigurationElement("web-root", "vraRestHost", "endpointConfig");
    var accessToken = System.getModule("org.harjinder.dev").getVraApiToken();  

}

return diskService

My vRO workflow looks like this.

var diskService = System.getModule("org.harjinder.dev").diskService();
var diskObj = new diskService();
var userEnteredDisks = diskObj.getDisksByResourceID(resourceId);
var sortedDisks = diskObj.sortDisks(vcVm,resourceId,vcvirtualMachine);

Once this workflow is run during subscription event Compute post provision, the disks should be added in the same order as the user requested them


0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published.