vdiiomark
Enthusiast
Enthusiast

Did I find an API bug? : Adding multiple disks soap error

I am able to add the disks linearly like below:

  my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[0]]);

  $vmView->ReconfigVM(spec => $vmSpec);

  my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[1]]);

  $vmView->ReconfigVM(spec => $vmSpec);

  my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[2]]);

  $vmView->ReconfigVM(spec => $vmSpec);

However, when I attempt to add all the disks at once I get a SOAP fault

  my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => \@devSpecs);

  $vmView->ReconfigVM(spec => $vmSpec);

The Error:

SOAP Fault:

-----------

Fault string: Cannot complete the operation because the file or folder /vmfs/volumes/ba5e81fe-201a6c4e/paul-vm1/paul-vm1_0_3.vmdk already exists

Fault detail: FileAlreadyExistsPAULWORK>clear

Is there anyone out there that knows what is going on?

stumpr
Virtuoso
Virtuoso

I've created new disks before in bulk.  Are you adding or creating disks?  Got more of your code to show?

Reuben Stump | http://www.virtuin.com | @ReubenStump
Reply
0 Kudos
vdiiomark
Enthusiast
Enthusiast

I did not want to do a code dump, but here you go

  1. package VManagement;
  2. # Paul Givens
  3. # Evaluator Group
  4. # 6/11/2013
  5. # A package for manipulaing virtual machines through vSphere
  6. # Public functions
  7. # addDisks
  8. use strict;
  9. use warnings;
  10. # I do not know why this lib is needed, delete it for a fun time
  11. use lib "/usr/lib/vmware-vcli/apps";
  12. use Exporter;
  13. use VMware::VIRuntime;
  14. use AppUtil::VMUtil;
  15. # Making a module
  16. our @ISA       = qw( Exporter );
  17. our @EXPORT_OK = qw( addDisks );
  18. our @EXPORT    = qw( addDisks );
  19. # Declarations
  20. sub addDisks;
  21. sub atomicAddDisks;
  22. # addDisks
  23. # a function to add disks to a virtual machine
  24. sub addDisks{
  25.   # Getting arguments
  26.   (my $viServer, my $login, my $password, my $verbose) = @_;
  27.   # Setting the global variables
  28.   $ENV{VI_SERVER}   ="$viServer";
  29.   $ENV{VI_USERNAME} ="$login";
  30.   $ENV{VI_PASSWORD} ="$password";
  31.   # Connecting
  32.   Opts::parse();
  33.   Opts::validate();
  34.   Util::connect();
  35.   # TEST start
  36.   my @diskSizes = (1,2,1);
  37.   my @datastores = ("zebi1n", "zebi1n", "zebi1n");
  38.   # TEST stop
  39.   if ($verbose) {print "Connected to server\n";}
  40.   atomicAddDisks("paul-vm1", \@diskSizes, \@datastores, $verbose); 
  41.   # Disconnecting
  42.   Util::disconnect();
  43. }
  44. sub atomicAddDisks{
  45.   # Getting arguments
  46.   (my $vmName, my $diskSizesRef, my $datastoresRef, my $verbose) = @_;
  47.   # Dereferencing
  48.   my @diskSizes  = @$diskSizesRef;
  49.   my @datastores = @$datastoresRef;
  50.   # Kb to Gb conversion factor
  51.   my $scsiNum  = 0;
  52.   my @devSpecs = ();
  53.   my $vmView   = Vim::find_entity_view(view_type => 'VirtualMachine',
  54.          filter => { 'name' => $vmName } );
  55.   my $vmDevices = $vmView->config->hardware->device;
  56.   my $diskFilename;
  57.   my $diskFilenameIncrement = 0;
  58.   my $diskFilenameCur;
  59.   my $diskNum = 0;
  60.   foreach my $vmDevice (@$vmDevices) {
  61.          # Adjusting the diskNum for disks already present 
  62.          if (ref($vmDevice) eq 'VirtualDisk') {
  63.                 $diskNum++;
  64.                 }
  65.   }
  66.   # Search virtual SCSI controller
  67.   my $controller;
  68.   my $numController = 0;
  69.   foreach my $vmDevice (@$vmDevices) {
  70.          if (ref($vmDevice) =~/VirtualBusLogicController|VirtualLsiLogicController|VirtualLsiLogicSASController|ParaVirtualSCSIController/) {
  71.                 Util::trace(1, "SCSI controller found : $&\n");
  72.                 $numController++;
  73.                 $controller = $vmDevice;
  74.          }
  75.   }
  76.   my $controllerKey = $controller->key;
  77.   # Set new unit number (7 cannot be used, and limit is 15)
  78.   my $unitNum;
  79.   my $vdiskNumber = $#{$controller->device} + 1;
  80.   if ($vdiskNumber < 7) {
  81.          $unitNum = $vdiskNumber;
  82.   }
  83.   elsif ($vdiskNumber == 15) {
  84.          die "ERR: one SCSI controller cannot have more than 15 virtual disks\n";
  85.   }
  86.   else {
  87.          $unitNum = $vdiskNumber + 1;
  88.   }
  89.   for(my $i = 0; $i < @datastores; $i++) {
  90.          # Conversion to GB
  91.          my $disksize =  $diskSizes[$i] * 1048576;
  92.          my $datastore = $datastores[$i];
  93.          # Increase the disk number to represent the machine to be added
  94.          $diskNum++;
  95.         
  96.          $diskFilename = "[$datastore] paul-vm1/paul-vm1_" . "$scsiNum" . "_" . $unitNum . ".vmdk";
  97.          # Build virtual spec data object
  98.          my $diskMode = VMUtils::get_diskmode(
  99.                 nopersist   => "",
  100.                 independent => ""
  101.          );
  102.          if ($verbose){
  103.                 print "SPECS\n";
  104.                 print "\tVM Name:\t$vmName\n";
  105.                 print "\tDisk Name:\t$diskFilename\n";
  106.                 print "\tController Key:\t$controllerKey\n";
  107.                 print "\tUnit Number\t$unitNum\n";
  108.                 print "\tDisk Size\t$disksize\n";
  109.          }
  110.          my $devSpec = VMUtils::get_vdisk_spec(
  111.                 vm            => $vmView,
  112.                 backingtype   => 'regular',
  113.                 diskMode      => $diskMode,
  114.                 fileName      => $diskFilename,
  115.                 controllerKey => $controllerKey,
  116.                 unitNumber    => $unitNum,
  117.                 size          => $disksize);
  118.          ###########################################
  119.          # Run ReconfigVM method (in VMUtils package)
  120.          # with previously defined specs
  121.          ###########################################
  122.          push(@devSpecs, $devSpec);
  123.          #increase the unit number for each device
  124.          $unitNum++;
  125.   }
  126.   my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => \@devSpecs);
  127.   $vmView->ReconfigVM(spec => $vmSpec);
  128.   #my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[0]]);
  129.   #$vmView->ReconfigVM(spec => $vmSpec);
  130.   #my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[1]]);
  131.   #$vmView->ReconfigVM(spec => $vmSpec);
  132.   #my $vmSpec = VirtualMachineConfigSpec->new(deviceChange => [$devSpecs[2]]);
  133.   #$vmView->ReconfigVM(spec => $vmSpec);
  134. }
Reply
0 Kudos
stumpr
Virtuoso
Virtuoso

Since you're adding an existing VMDK, any chance you're adding it twice or it's already attached to that VM? 

Reuben Stump | http://www.virtuin.com | @ReubenStump
Reply
0 Kudos
vdiiomark
Enthusiast
Enthusiast

I dont think so, If that was true, why could I add the three individual machines by array index as apposed to passing it the array of three items

Reply
0 Kudos
stumpr
Virtuoso
Virtuoso

Do this, add "print Dumper($vmSpec)" before line 150.  We can eyeball the spec sent to the API.

Reuben Stump | http://www.virtuin.com | @ReubenStump
vdiiomark
Enthusiast
Enthusiast

I did a dump like you said and it was very helpful.  I needed to add the code below to make it work

Turns out that I need a unique key for every disk that I am adding.  Thank you for your help

my $disk = VirtualDisk->new(

       controllerKey  => $devSpec->device->controllerKey,

       unitNumber     => $devSpec->device->unitNumber,

       key            => -1 *($i + 1),

       backing        => $devSpec->device->backing,

       capacityInKB   => $devSpec->device->capacityInKB,

    );

$devSpec = VirtualDeviceConfigSpec->new(

    operation     => $devSpec->operation,

    fileOperation => $devSpec->fileOperation,

    device        => $disk,

);

Is there a better way to do it than this

View solution in original post

Reply
0 Kudos
MR-Z
VMware Employee
VMware Employee

"key => -1" works fine for me:

Since I am adding a bunch of pre-existing disks, what I do is this:

- locate the disks based on the datastore and folder location using datastore browser (SearchDatastoreSubFolders call).

- do an inventory of the controllers so that I can reference the controller keys when needed

- do an inventory of the virtual disks currently attached to the vm (so that I know if adding more disk to a particular virtual node makes sense).

then loop through the diks that you have found from the datastore and are supposedly attaching disks to.

                    my $backing = @$disks[$i]->diskType->new (

                                diskMode => 'independent_persistent',

                                fileName => $result->folderPath . @$disks[$i]->path,

                                );

                        my $connectInfo = VirtualDeviceConnectInfo->new (

                                allowGuestControl => 0,

                                connected => 1,

                                startConnected => 1,

                                );

                        my $devInfo = VirtualDisk->new (

                                backing => $backing,

                                controllerKey => $ctrlKey,

                                unitNumber => $unit,

                                connectable => $connectInfo,

                                key => -1,

                                capacityInKB => @$disks[$i]->capacityKb,

                                );

                        $devspec[$i] = VirtualDeviceConfigSpec->new(

                                operation => VirtualDeviceConfigSpecOperation->new('add'),

                                device => $devInfo,

                                );

Once finish the loop, construct the $spec and run reconfigVM (or reconfigVM_task):

          my $spec = VirtualMachineConfigSpec->new(deviceChange => [@devspec] );    

                if (@devspec) {

                        print "Reconfig VM with disk changes...\n";

                        # Asynchronous vs Synchronous execution

                        #eval { $vm->ReconfigVM(spec => $spec); };

                        eval { $vm->ReconfigVM_Task(spec => $spec); };

                        if ($@) { print "Reconfiguration failed.\n $@";}

                        else {print "Disks added successfully.\n"; }

                } else { print "Nothing to add!\n"};

Reply
0 Kudos