Author: arkurth
Date: Wed May  2 18:13:54 2012
New Revision: 1333139

URL: http://svn.apache.org/viewvc?rev=1333139&view=rev
Log:
VCL-450
Fixed problem that occurs on VMware Server 2.0 if linked clones are used. 
vmware-vdiskmanager can't copy the delta file and generates this error: " 
Failed to convert disk: The called function cannot be performed on partial 
chains." The workaround is to make a copy of the original vmdk that the delta 
file is pointing to, modify the delta vmdk descriptor file and the VM's .vmsd 
file to point to this copy instead of the original, and then remove all 
snapshots from the VM. This merges the delta vmdk into the new copy.

Removed VIX_API.pm. It has never been maintained.

Changed warnings to debug messages in vSphere_SDK.pm if initialization fails. 
Additional warnings will be generated by VMware.pm::initialize if this was 
really a problem.

Removed:
    
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIX_API.pm
Modified:
    
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
    
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
    
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm

Modified: 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
URL: 
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm?rev=1333139&r1=1333138&r2=1333139&view=diff
==============================================================================
--- 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
 (original)
+++ 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VIM_SSH.pm
 Wed May  2 18:13:54 2012
@@ -1728,6 +1728,66 @@ sub create_snapshot {
 
 #/////////////////////////////////////////////////////////////////////////////
 
+=head2 remove_snapshots
+
+ Parameters  : $vmx_file_path
+ Returns     : boolean
+ Description : Removes all snapshots for a VM.
+
+=cut
+
+sub remove_snapshots {
+       my $self = shift;
+       if (ref($self) !~ /VCL::Module/i) {
+               notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a 
function, it must be called as a class method");
+               return;
+       }
+       
+       # Get the vmx file path argument
+       my $vmx_file_path = shift;
+       if (!$vmx_file_path) {
+               notify($ERRORS{'WARNING'}, 0, "vmx file path argument was not 
supplied");
+               return;
+       }
+       
+       # Get the VM ID
+       my $vm_id = $self->_get_vm_id($vmx_file_path);
+       if (!defined($vm_id)) {
+               notify($ERRORS{'WARNING'}, 0, "unable to create snapshot 
because VM ID could not be determined");
+               return;
+       }
+       
+       my $vim_cmd_arguments = "vmsvc/snapshot.removeall $vm_id";
+       my ($exit_status, $output) = $self->_run_vim_cmd($vim_cmd_arguments);
+       return if !$output;
+       
+       notify($ERRORS{'DEBUG'}, 0, "remove snapshots output:\n" . join("\n", 
@$output));
+       
+       if (grep(/failed|invalid/i, @$output)) {
+               notify($ERRORS{'WARNING'}, 0, "failed to remove snapshots for 
VM $vmx_file_path, VIM command arguments: '$vim_cmd_arguments', output:\n" . 
join("\n", @$output));
+               return;
+       }
+       
+       # Get the task ID
+       my @task_ids = $self->_get_task_ids($vmx_file_path, 
'removeAllSnapshots');
+       if (!@task_ids) {
+               notify($ERRORS{'WARNING'}, 0, "unable to retrieve the ID of the 
task created to remove snapshots");
+               return;
+       }
+       
+       # Wait for the task to complete
+       if ($self->_wait_for_task($task_ids[0])) {
+               notify($ERRORS{'OK'}, 0, "removed snapshots for VM: 
$vmx_file_path");
+               return 1;
+       }
+       else {
+               notify($ERRORS{'WARNING'}, 0, "failed to remove snapshots for 
VM: $vmx_file_path, the vim task did not complete successfully, vim-cmd 
$vim_cmd_arguments output:\n" . join("\n", @$output));
+               return;
+       }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
 =head2 snapshot_exists
 
  Parameters  : $vmx_file_path

Modified: 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
URL: 
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm?rev=1333139&r1=1333138&r2=1333139&view=diff
==============================================================================
--- 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm 
(original)
+++ 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm 
Wed May  2 18:13:54 2012
@@ -5901,6 +5901,40 @@ sub copy_vmdk {
                        notify($ERRORS{'DEBUG'}, 0, "unable to copy virtual 
disk using vmware-vdiskmanager because the command is not available on VM host 
$vmhost_name");
                }
                else {
+                       my $partial_chains_error = 0;
+                       my $delta_vmdk_file_path = $source_vmdk_file_path;
+                       
+                       # Check if the following error was displayed:
+                       # Failed to convert disk: The called function cannot be 
performed on partial chains. Please open the parent virtual disk (0x500003e83).
+                       # This occurs on VMware Server 2.0 when attempting to 
copy a delta vmdk created for a linked-clone VM
+                       # The workaround is to make a copy of the original 
master image, change the VM's .vmsd file and the delta .vmdk file to point to 
the copy, and then remove the snapshots from the VM
+                       # This effectively merges the delta changes into the 
copy of the master
+                       if (grep(/function cannot be performed on partial 
chains/i, @$output)) {
+                               $partial_chains_error = 1;
+                               
+                               # Find the parentFileNameHint line in the vmdk 
descriptor file, this desribes which vmdk is the master for the linked clone 
delta vmdk
+                               # parentFileNameHint="/var/lib/vmware/Virtual 
Machines/vmwarewinxp-base234-v28/vmwarewinxp-base234-v28.vmdk"
+                               
+                               my @source_vmdk_file_contents = 
$self->vmhost_os->get_file_contents($source_vmdk_file_path);
+                               my ($parent_vmdk_file_path) = join("\n", 
@source_vmdk_file_contents) =~ /parentFileNameHint="([^"]+)"/;
+                               if ($parent_vmdk_file_path) {
+                                       notify($ERRORS{'DEBUG'}, 0, "retrieved 
parent file path from source vmdk descriptor file: '$parent_vmdk_file_path'");
+                                       
+                                       # Change $source_vmdk_file_path to the 
path of the original master vmdk, a copy of this will be created
+                                       # Use $source_vmdk_file_path rather 
than a new variable name so that the repair code below doesn't need to be 
changed
+                                       $source_vmdk_file_path = 
$parent_vmdk_file_path;
+                                       
+                                       $vdisk_command = "vmware-vdiskmanager 
-r \"$source_vmdk_file_path\" -t 1 \"$destination_vmdk_file_path\"";
+                                       notify($ERRORS{'DEBUG'}, 0, "attempting 
to copy parent virtual disk using vmware-vdiskmanager, disk type: 
2gbsparse:\n'$source_vmdk_file_path' --> '$destination_vmdk_file_path'");
+                                       
+                                       $start_time = time;
+                                       ($exit_status, $output) = 
$self->vmhost_os->execute($vdisk_command, 1, 7200);
+                               }
+                               else {
+                                       notify($ERRORS{'WARNING'}, 0, "failed 
to copy virtual disk, unable to retrieve parent file path from source vmdk 
descriptor file contents:\n" . join("\n", @source_vmdk_file_contents));
+                               }
+                       }
+                       
                        # Check if virtual disk needs to be repaired, 
vmware-vdisk manager may display the following:
                        # Failed to convert diskCreating disk '<path>'
                        # The specified virtual disk needs repair (0xe00003e86).
@@ -5914,13 +5948,14 @@ sub copy_vmdk {
                                if (!defined($vdisk_repair_output)) {
                                        notify($ERRORS{'WARNING'}, 0, "failed 
to run command to repair the virtual disk: '$vdisk_repair_command'");
                                }
+                               
                                elsif (grep(/(has been successfully repaired|no 
errors)/i, @$vdisk_repair_output)) {
                                        notify($ERRORS{'DEBUG'}, 0, "repaired 
virtual disk using vmware-vdiskmanage, output:\n" . join("\n", 
@$vdisk_repair_output));
                                        
                                        # Attempt to run the 
vmware-vdiskmanager copy command again
                                        notify($ERRORS{'DEBUG'}, 0, "making a 
2nd attempt to copy virtual disk using vmware-vdiskmanager after the source was 
repaired, disk type: 2gbsparse:\n'$source_vmdk_file_path' --> 
'$destination_vmdk_file_path'");
                                        $start_time = time;
-                                       ($exit_status, $output) = 
$self->vmhost_os->execute($vdisk_command);
+                                       ($exit_status, $output) = 
$self->vmhost_os->execute($vdisk_command, 1, 7200);
                                }
                                else {
                                        notify($ERRORS{'WARNING'}, 0, "failed 
to repair the virtual disk on VM host $vmhost_name, output:\n" . join("\n", 
@$vdisk_repair_output));
@@ -5939,6 +5974,50 @@ sub copy_vmdk {
                        elsif (!grep(/(100\% done|success)/, @$output)) {
                                notify($ERRORS{'WARNING'}, 0, "failed to copy 
virtual disk on VM host $vmhost_name, output does not contain '100% done' or 
'success', command: '$vdisk_command', output:\n" . join("\n", @$output));
                        }
+                       elsif ($partial_chains_error) {
+                               # Had to make a copy of the original master 
vmdk earlier, not the desired source vmdk
+                               # Still need to merge the delta vmdk into this 
copy
+                               $copy_result = 1;
+                               
+                               # Determine the .vmsd file path
+                               my $vmx_file_path = $self->get_vmx_file_path();
+                               (my $vmsd_file_path = $vmx_file_path) =~ 
s/\.vmx$/\.vmsd/;
+                               
+                               # Escape the strings which will be 
found/replaced in the files
+                               (my $source_vmdk_file_path_escaped = 
$source_vmdk_file_path) =~ s/\//\\\//g;
+                               (my $destination_vmdk_file_path_escaped = 
$destination_vmdk_file_path) =~ s/\//\\\//g;
+                               
+                               # Modify the .vmsd and delta .vmdk files
+                               # Change them to point to the copy of the 
original base image vmdk instead of the original master vmdk
+                               for my $replace_file_path ($vmsd_file_path, 
$delta_vmdk_file_path) {
+                                       my $sed_command = "sed -i -e 
's/$source_vmdk_file_path_escaped/$destination_vmdk_file_path_escaped/' 
\"$replace_file_path\"";
+                                       my ($sed_exit_status, $sed_output) = 
$self->vmhost_os->execute($sed_command);
+                                       if (!defined($sed_output)) {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to execute command to replace original vmdk file path with copied vmdk 
file path in $replace_file_path");
+                                               $copy_result = 0;
+                                               last;
+                                       }
+                                       elsif (grep(/sed: /, @$sed_output)) {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to replace original vmdk file path with copied vmdk file path in 
'$replace_file_path'\n'$source_vmdk_file_path' --> 
'$destination_vmdk_file_path'\ncommand: '$sed_command'\noutput:\n" . join("\n", 
@$sed_output));
+                                               $copy_result = 0;
+                                               last;
+                                       }
+                                       else {
+                                               notify($ERRORS{'DEBUG'}, 0, 
"replaced original vmdk file path with copied vmdk file path in 
'$replace_file_path'\n'$source_vmdk_file_path' --> 
'$destination_vmdk_file_path'\ncommand: '$sed_command'\noutput:\n" . join("\n", 
@$sed_output));
+                                       }
+                               }
+                               
+                               # Remove the VM's snapshots, this merges the 
delta vmdk into the copy of the original master vmdk
+                               if ($copy_result) {
+                                       if 
($self->api->remove_snapshots($vmx_file_path)) {
+                                               notify($ERRORS{'DEBUG'}, 0, 
"removed snapshots from VM, the merged delta vmdk '$delta_vmdk_file_path' with 
destination vmdk '$destination_vmdk_file_path'");
+                                       }
+                                       else {
+                                               notify($ERRORS{'WARNING'}, 0, 
"failed to remove snapshots from VM, delta vmdk '$delta_vmdk_file_path' was NOT 
merged with destination vmdk '$destination_vmdk_file_path'");
+                                               return;
+                                       }
+                               }
+                       }
                        else {
                                notify($ERRORS{'OK'}, 0, "copied virtual disk 
on VM host $vmhost_name using vmware-vdiskmanager:\n'$source_vmdk_file_path' 
--> '$destination_vmdk_file_path'");
                                $copy_result = 1;

Modified: 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
URL: 
http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm?rev=1333139&r1=1333138&r2=1333139&view=diff
==============================================================================
--- 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
 (original)
+++ 
incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/vSphere_SDK.pm
 Wed May  2 18:13:54 2012
@@ -161,11 +161,11 @@ sub initialize {
        }
        
        if (!$vim) {
-               notify($ERRORS{'WARNING'}, 0, "failed to connect to VM host 
$vmhost_hostname");
+               notify($ERRORS{'DEBUG'}, 0, "failed to connect to VM host 
$vmhost_hostname");
                return;
        }
        elsif (!ref($vim)) {
-               notify($ERRORS{'WARNING'}, 0, "failed to connect to VM host 
$vmhost_hostname, Util::connect returned '$vim'");
+               notify($ERRORS{'DEBUG'}, 0, "failed to connect to VM host 
$vmhost_hostname, Util::connect returned '$vim'");
                return;
        }
        else {


Reply via email to