Author: bmbouter Date: Sun Oct 25 21:16:16 2009 New Revision: 829652 URL: http://svn.apache.org/viewvc?rev=829652&view=rev Log: This includes clone monitoring and management of thin versus thick efficiency. This functionality will be explained in a future commit to the esxthin.README file. This code has been tested and works.
Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/esxthin.pm Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/esxthin.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/esxthin.pm?rev=829652&r1=829651&r2=829652&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/esxthin.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/esxthin.pm Sun Oct 25 21:16:16 2009 @@ -259,6 +259,15 @@ } ## end if ($vminfo_output =~ /^Information of Virtual Machine $computer_shortname/m) + my $density_percentage = netapp_get_vol_density_percentage($s,$volume_path); + if ($density_percentage > 0.9) { + notify($ERRORS{'CRITICAL'}, 0, "The image library volume $volume_path is too dense, and requires administrative attention IMMEDIATELY!!!"); + notify($ERRORS{'CRITICAL'}, 0, "The volume $volume_path is currently $density_percentage % dense"); + #mail($to,$subject,$mailstring, $from) + } else { + notify($ERRORS{'DEBUG'}, 0, "The image library volume $volume_path is $density_percentage % dense"); + } + # Remove old vm folder netapp_delete_dir($s,"$volume_path/inuse/$computer_shortname"); @@ -268,12 +277,48 @@ # clone vmdk file from golden to inuse my $from = "$volume_path/golden/$image_name/image.vmdk"; my $to = "$volume_path/inuse/$computer_shortname/image.vmdk"; - netapp_fileclone($s,$from,$to); + # Call the fileclone. The 1 at the end tells the function to ignore a slow, thick copy (should it need to be a thick copy) + netapp_fileclone($s,$from,$to,1); # Copy the (large) -flat.vmdk file - $from = "$volume_path/golden/$image_name/image-flat.vmdk"; $to = "$volume_path/inuse/$computer_shortname/image-flat.vmdk"; - netapp_fileclone($s,$from,$to); + my $continue = "true"; + for (my $count = 0; $continue eq "true"; $count++) { + # Setup the $from to try to pack the clones densely from the parent goldens + if ($count == 0) { + $from = "$volume_path/golden/$image_name/image-flat.vmdk"; + } else { + $from = "$volume_path/golden/$image_name/image-flat$count.vmdk/image-flat$count.vmdk"; + } + + # Make the Clone request + my $clone_status = netapp_fileclone($s,$from,$to,0); + + if ($clone_status == 0) { + # A Clone error occured. Provisioning cannot continue + return 0; + } elsif ($clone_status == -1) { + # The clone was thick and was cancelled. + # Now, check if the next one exists + my $next_count = $count + 1; + my $next_path = "$volume_path/golden/$image_name/image-flat$next_count.vmdk/image-flat.vmdk"; + if (netapp_is_file($s,$next_path) != 1) { + # The next clone parent file needs to be created + # TODO: Fix the race condition on this copy if multiple threads do an ndmpcopy over each other + my $copy_from = "$volume_path/golden/$image_name/image-flat.vmdk"; + my $copy_to = "$volume_path/golden/$image_name/image-flat$next_count.vmdk"; + notify($ERRORS{'DEBUG'}, 0, "Going to thick copy $copy_from to $copy_to"); + netapp_copy($s,$copy_from,$copy_to,$image_identity); + } else { + notify($ERRORS{'DEBUG'}, 0, "Parent $next_path exists, the clone will use it."); + } + } elsif ($clone_status == 1) { + # The thin-clone has been successfully completed + notify($ERRORS{'DEBUG'}, 0, "$to has been thinly cloned successfully"); + $continue = "false"; + } + } + # Call the fileclone. The 0 at the end will cause the clone opeartion to stop with a -1 return code if the clone becomes thick. # Author new VMX file, output to temporary file (will file-write-file it below) my @vmxfile; @@ -885,6 +930,40 @@ #///////////////////////////////////////////////////////////////////////// +=head2 netapp_is_file + + Parameters : $s, $file_path + Returns : 1($file_path exists) or 0($file_path doesn't exist) + Description : Determines if the file $file_path exists on the NetApp + backed by $s. Note, $file_path must begin with /vol and + should no contain a trailing slash. + +=cut + +sub netapp_is_file +{ + my $s = $_[0]; + my $file_path = $_[1]; + + # Check if $file_path a directory or a file + my $in = NaElement->new("file-get-file-info"); + $in->child_add_string("path",$file_path); + my $out = $s->invoke_elem($in); + if($out->results_status() eq "failed") { + #notify($ERRORS{'CRITICAL'}, 0, $out->results_reason() ."\n"); + return 0; + } else { + my $file_type = $out->child_get("file-info")->child_get_string("file-type"); + # Is this a file? + if ($file_type eq "file") { + return 1; + } + } + return 0; +} + +#///////////////////////////////////////////////////////////////////////// + =head2 netapp_get_size Parameters : $s, $path @@ -914,6 +993,42 @@ #///////////////////////////////////////////////////////////////////////// +=head2 netapp_get_vol_density_percentage + + Parameters : $s, $vol + Returns : A float between 0.0 <= 1.0 representing percentage + of density this volume currently experiences + Description : Determines the percentage of indirection $vol already has on + the NetApp backed by $s. The percentage is calculculated against + 32TB (14293651161088 Bytes). If percentage goes to 1.0 bad things + happen. This function calculates density according to the formula: + density = <volume-info><size-used> + <volume-info><sis><size-shared> + NOTE: $vol must start with /vol/ and must not contain a trailing slash. + +=cut + +sub netapp_get_vol_density_percentage +{ + my $s = $_[0]; + my $vol = $_[1]; + + # Check if $path a directory or a file + my $in = NaElement->new("volume-list-info"); + $in->child_add_string("volume",$vol); + my $out = $s->invoke_elem($in); + if($out->results_status() eq "failed") { + #notify($ERRORS{'CRITICAL'}, 0, $out->results_reason() ."\n"); + return 0; + } else { + my $size_used = $out->child_get("volumes")->child_get("volume-info")->child_get_string("size-used"); + my $sis_size_shared = $out->child_get("volumes")->child_get("volume-info")->child_get("sis")->child_get("sis-info")->child_get_string("size-shared"); + my $total = $size_used + $sis_size_shared; + my $percent = $total / 14293651161088; + return $percent; + } +} +#///////////////////////////////////////////////////////////////////////// + =head2 netapp_delete_empty_dir Parameters : $s, $dir_path @@ -974,12 +1089,53 @@ #///////////////////////////////////////////////////////////////////////// -=head2 netapp_fileclone +=head2 netapp_copy - Parameters : $s, $source_path, $dest_path + Parameters : $s, $from_path, $to_path, $netapp_identity_file Returns : 1(success) or 0(failure) + Description : copies the item at $from_path to $to_path on the NetApp + backing $s. The identity_file should be passed in from above + and likelky comes from $self->data->get_image_identity + NOTE: The to_path actually names the directory for the thick + copy to live in. The file is located inside this directory + by its same name. + +=cut + +sub netapp_copy +{ + my $s = $_[0]; + my $from_path = $_[1]; + my $to_path = $_[2]; + my $netapp_identity_file = $_[3]; + + my $user = $s->{user}; + my $netapp_ip = $s->{server}; + + notify($ERRORS{'DEBUG'}, 0, "Doing an ndmpcopy on NetApp ($netapp_ip) of $from_path to $to_path"); + if (!run_ssh_command($netapp_ip, $netapp_identity_file, "ndmpcopy $from_path $to_path", $user)) { + notify($ERRORS{'CRITICAL'}, 0, "Could not copy file on NetApp via SSH!"); + return 0; + } + return 1; + +} + +#///////////////////////////////////////////////////////////////////////// + +=head2 netapp_fileclone + + Parameters : $s, $source_path, $dest_path, $ignore_thick (boolean) + Returns : 1(success), 0(general failure), or -1 (clone was cancelled + because it was thick) Description : clones the file $source_path to $dest_path on a NetApp - storage system backing $s + storage system backing $s. This is a blocking call, it waits + for the clone operation to indicate the clone is 'complete' + before returning '1' Also, while $ignore_thick is true + if the clone copies any thick blocks then the function + stops the clone operation and returns '-1' Note: $ignore_thick + is 0 by default making the function cancel thick file copies + the default behavior. =cut @@ -988,23 +1144,81 @@ my $s = $_[0]; my $source_path = $_[1]; my $dest_path = $_[2]; + my $ignore_thick = 0; + if (defined($_[3])) { + $ignore_thick = $_[3]; + } + + #The number of seconds to sleep before retrying if there are too many clones occurring + my $retry = 5; + + my $too_many_clones_occurring = 0; + do { + my $in = NaElement->new("clone-start"); + $in->child_add_string("source-path",$source_path); + $in->child_add_string("destination-path",$dest_path); + $in->child_add_string("no-snap","false"); - my $in = NaElement->new("clone-start"); - $in->child_add_string("source-path",$source_path); - $in->child_add_string("destination-path",$dest_path); - $in->child_add_string("no-snap","false"); - - # - # Invoke clone-start API - # - my $out = $s->invoke_elem($in); - - if($out->results_status() eq "failed") { - notify($ERRORS{'CRITICAL'}, 0, $out->results_reason() ."\n"); - return 0; - } else { - return 1; - } + my $out = $s->invoke_elem($in); + + if($out->results_status() eq "failed") { + if ($out->results_errno() == 14611) { + notify($ERRORS{'DEBUG'}, 0, "Too Many Clones Currently Occuring ... will try again in $retry seconds"); + sleep($retry); + $too_many_clones_occurring = 1; + } else { + notify($ERRORS{'CRITICAL'}, 0, $out->results_reason() ."\n"); + return 0; + } + } else { + # The clone operation has begun + my $clone_status = ""; + # Grab the volume uuid and clone_op_id specifying this clone operation + my $volume_uuid = $out->child_get("clone-id")->child_get("clone-id-info")->child_get_string("volume-uuid"); + my $clone_op_id = $out->child_get("clone-id")->child_get("clone-id-info")->child_get_string("clone-op-id"); + + # Bundle a new clone-list-status request about the current clone operation + my $in = NaElement->new("clone-list-status"); + my $clone_id = NaElement->new("clone-id"); + my $clone_id_info = NaElement->new("clone-id-info"); + $clone_id_info->child_add_string("clone-op-id",$clone_op_id); + $clone_id_info->child_add_string("volume-uuid",$volume_uuid); + $clone_id->child_add($clone_id_info); + $in->child_add($clone_id); + # send the clone-list-status request + my $out = $s->invoke_elem($in); + while($out->child_get("status")->child_get("ops-info")->child_get_string("clone-state") ne "completed") { + notify($ERRORS{'DEBUG'}, 0, "Waiting for clone $dest_path to finish..."); + if($ignore_thick == 0 && $out->child_get("status")->child_get("ops-info")->child_get_string("blocks-copied") != 0) { + if($out->child_get("status")->child_get("ops-info")->child_get_string("percent-done") < 99) { + #cancel clone operation + notify($ERRORS{'DEBUG'}, 0, "The clone $dest_path is being inneficiently copied instead of being cloned..."); + notify($ERRORS{'DEBUG'}, 0, "The clone for $dest_path will now be cancelled"); + # Bundle a new clone-stop request to stop the current clone operation + my $stop_in = NaElement->new("clone-stop"); + my $stop_clone_id = NaElement->new("clone-id"); + my $stop_clone_id_info = NaElement->new("clone-id-info"); + $stop_clone_id_info->child_add_string("clone-op-id",$clone_op_id); + $stop_clone_id_info->child_add_string("volume-uuid",$volume_uuid); + $stop_clone_id->child_add($stop_clone_id_info); + $stop_in->child_add($stop_clone_id); + # send the clone-stop request + my $stop_output = $s->invoke_elem($stop_in); + if($stop_output->results_status() eq "failed") { + notify($ERRORS{'CRITICAL'}, 0, $stop_output->results_reason() ."\n"); + return 0; + } else { + notify($ERRORS{'DEBUG'}, 0, "The clone for $dest_path has successfully been cancelled"); + return -1; + } + } + } + sleep(2); + $out = $s->invoke_elem($in); + } + return 1; + } + } while($too_many_clones_occurring); } #/////////////////////////////////////////////////////////////////////////////