Author: arkurth
Date: Tue Mar 4 17:01:35 2014
New Revision: 1574134
URL: http://svn.apache.org/r1574134
Log:
VCL-685
Added VMware.pm 'vcld -setup' option to 'Purge deleted and unused images from
repository datastore'.
Modified:
vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
URL:
http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm?rev=1574134&r1=1574133&r2=1574134&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm
(original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning/VMware/VMware.pm Tue
Mar 4 17:01:35 2014
@@ -264,6 +264,7 @@ sub initialize {
return;
}
+ return 1 if ($self->data->get_request_state_name() =~ /test/i);
notify($ERRORS{'DEBUG'}, 0, "initializing " . ref($self) . " object");
# Get a DataStructure object containing data for the VM host computer
@@ -7779,9 +7780,13 @@ sub setup_vm_host_operations {
print "No VM hosts are mapped to $FQDN\n";
return;
}
-
+
print "Select a VM host:\n";
- my $vmhost_id = setup_get_hash_choice($management_node_vmhost_info,
'hostname') || return;
+ #print format_data($management_node_vmhost_info) . "\n\n";
+ my $vmhost_id = setup_get_hash_choice($management_node_vmhost_info,
'hostname', 'vmprofile_profilename') || return;
+ #For testing:
+ #my $vmhost_id = 599;
+
my $vmhost_computer_name =
$management_node_vmhost_info->{$vmhost_id}{computer}{hostname};
push @{$ENV{setup_path}}, $vmhost_computer_name;
@@ -7817,9 +7822,9 @@ sub setup_vm_host_operations {
setup_print_break('.');
my $datastore_operations_menu = {
'Purge deleted and unused images from virtual disk datastore'
=> \&setup_purge_datastore_images,
+ 'Purge deleted and unused images from repository datastore' =>
\&setup_purge_repository_images,
};
-
print "Select an operation:\n";
my $datastore_operations_choice =
setup_get_menu_choice($datastore_operations_menu) || return;
#For testing:
@@ -7828,6 +7833,11 @@ sub setup_vm_host_operations {
# "parent_menu_names" => [],
# "sub_ref" => \&setup_purge_datastore_images,
#};
+ #my $datastore_operations_choice = {
+ # "name" => "Purge deleted images from repository",
+ # "parent_menu_names" => [],
+ # "sub_ref" => \&setup_purge_repository_images,
+ #};
my $datastore_operations_choice_name =
$datastore_operations_choice->{name};
my $datastore_operations_choice_sub_ref =
$datastore_operations_choice->{sub_ref};
@@ -8030,6 +8040,7 @@ sub setup_purge_datastore_images {
my @not_deleted_candidate_newer_than_production_not_recently_created
= get_array_intersection(\@not_deleted_candidate,
\@newer_than_production, \@not_recently_created); # Purgable
setup_print_break('-');
+ print "Analyzed image revisions stored in the virtual disk path
datastore:\n";
print "|- Deleted: "
. scalar(@deleted) . "\n";
print " |- Has reservation: "
. scalar(@deleted_has_reservations) . "\n";
print " |- No reservations: "
. scalar(@deleted_no_reservations) . "\n";
@@ -8125,6 +8136,191 @@ sub setup_purge_datastore_images {
#/////////////////////////////////////////////////////////////////////////////
+=head2 setup_purge_repository_images
+
+ Parameters : none
+ Returns : boolean
+ Description : Checks all images stored in the repository path location and
+ safely purges them.
+
+=cut
+
+sub setup_purge_repository_images {
+ my $self = shift;
+ if (ref($self) !~ /VMware/i) {
+ notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a
function, it must be called as a class method");
+ return;
+ }
+
+ my $vmhost_computer_name = $self->data->get_vmhost_hostname();
+ my $vmhost_profile_datastore_path =
$self->data->get_vmhost_profile_datastore_path();
+ my $vmhost_profile_repository_path =
$self->data->get_vmhost_profile_repository_path();
+
+ if (!$vmhost_profile_repository_path) {
+ print "WARNING: images not purged because repository path is
not configured in the VM host profile\n";
+ return;
+ }
+
+ my $datastore_base_path =
$self->_get_normal_path($vmhost_profile_datastore_path);
+ if (!$datastore_base_path) {
+ print "ERROR: failed to locate virtual disk path configured in
the VM host profile on $vmhost_computer_name: $vmhost_profile_datastore_path\n";
+ return;
+ }
+
+ my $repository_base_path =
$self->_get_normal_path($vmhost_profile_repository_path);
+ if (!$repository_base_path) {
+ print "ERROR: failed to locate repository path configured in
the VM host profile on $vmhost_computer_name:
$vmhost_profile_repository_path\n";
+ return;
+ }
+
+ if ($datastore_base_path eq $repository_base_path) {
+ print "WARNING: images not purged because virtual disk path is
the same location as the repository path configured in the VM host profile:
$datastore_base_path\n";
+ return;
+ }
+
+ setup_print_break('.');
+ # Get an array of image names currently stored on the repository
+ my @repository_imagerevision_names =
$self->get_datastore_imagerevision_names($repository_base_path);
+
+ setup_print_break('.');
+ # Get various info about image revisions such as deleted, date
created...
+ my $imagerevision_cleanup_info = get_imagerevision_cleanup_info();
+
+ # Get reservation info for all imagerevisions in repository
+ my $imagerevision_reservation_info =
get_imagerevision_reservation_info();
+
+ # Get computers on which imagerevisions in datastore are currently
loaded according to the database
+ my $imagerevision_loaded_info = get_imagerevision_loaded_info();
+
+ my @deleted;
+ my @not_deleted;
+ my @has_reservations;
+ my @no_reservations;
+ my @loaded;
+ my @not_loaded;
+
+ # Build lists of imagerevisions with certain characteristics
+ for my $repository_imagerevision_name (@repository_imagerevision_names)
{
+ if
(!$imagerevision_cleanup_info->{$repository_imagerevision_name}) {
+ print "WARNING: failed to retrieve cleanup info from
database for image revision: $repository_imagerevision_name\n";
+ return;
+ }
+
+ if
($imagerevision_cleanup_info->{$repository_imagerevision_name}{deleted}) {
+ push @deleted, $repository_imagerevision_name;
+ }
+ else {
+ push @not_deleted, $repository_imagerevision_name;
+ }
+
+ if
($imagerevision_reservation_info->{$repository_imagerevision_name}) {
+ push @has_reservations, $repository_imagerevision_name;
+ }
+ else {
+ push @no_reservations, $repository_imagerevision_name;
+ }
+
+ if
($imagerevision_loaded_info->{$repository_imagerevision_name}) {
+ push @loaded, $repository_imagerevision_name;
+ }
+ else {
+ push @not_loaded, $repository_imagerevision_name;
+ }
+ }
+
+ # Find image revisions which have multiple characteristics by finding
the intersection of the arrays
+ my @deleted_has_reservations =
get_array_intersection(\@deleted, \@has_reservations);
+ my @deleted_has_reservations_loaded =
get_array_intersection(\@deleted, \@has_reservations, \@loaded);
+ my @deleted_has_reservations_not_loaded =
get_array_intersection(\@deleted, \@has_reservations, \@not_loaded);
+ my @deleted_no_reservations =
get_array_intersection(\@deleted, \@no_reservations);
+ my @deleted_no_reservations_loaded =
get_array_intersection(\@deleted, \@no_reservations, \@loaded);
+ my @deleted_no_reservations_not_loaded =
get_array_intersection(\@deleted, \@no_reservations, \@not_loaded);
+
+ setup_print_break('-');
+ print "Analyzed image revisions stored in the repository datastore:\n";
+ print "|- Deleted: " .
scalar(@deleted) . "\n";
+ print " |- Has reservation: " .
scalar(@deleted_has_reservations) . "\n";
+ print " |- No reservations: " .
scalar(@deleted_no_reservations) . "\n";
+ print " |- Loaded: " .
scalar(@deleted_no_reservations_loaded) . "\n";
+ print " |- Not loaded: " .
scalar(@deleted_no_reservations_not_loaded) . " (*)\n";
+ print "|- Not deleted: " .
scalar(@not_deleted) . "\n";
+ print "(*) May be safely purged\n\n";
+
+ my @purgable_imagerevisions;
+
+ if (@deleted_no_reservations_not_loaded) {
+ push @purgable_imagerevisions,
@deleted_no_reservations_not_loaded;
+ print "Deleted, no reservations, not loaded: " .
scalar(@deleted_no_reservations_not_loaded) . "\n";
+ print "- " . join("\n- ", @deleted_no_reservations_not_loaded)
. "\n\n";
+ }
+
+ if (!@purgable_imagerevisions) {
+ print "No image revisions were found which can be safely purged
from the repository datastore\n";
+ return;
+ }
+
+ my $purgable_imagerevision_count = scalar(@purgable_imagerevisions);
+
+ my $delete_limit;
+ while (!$delete_limit) {
+ $delete_limit = setup_get_input_string("Enter number of image
revisions to purge (0-$purgable_imagerevision_count)",
$purgable_imagerevision_count);
+ return if !$delete_limit;
+ $delete_limit =~ s/\s*//g;
+ if ($delete_limit !~ /^\d+$/ || $delete_limit >
$purgable_imagerevision_count) {
+ print "Value must be an integer between 0 and
$purgable_imagerevision_count\n";
+ $delete_limit = '';
+ }
+ }
+
+ my $delete_count = 0;
+ for my $imagerevision_name (@purgable_imagerevisions) {
+ $delete_count++;
+ setup_print_break('.');
+ print "Deleting image revision $delete_count/$delete_limit:
$imagerevision_name\n";
+
+ my $repository_directory_path =
"$repository_base_path/$imagerevision_name";
+ print "repository directory path: $repository_directory_path\n";
+
+ # Check files in directory, make sure it's safe to delete
+ my @file_paths =
$self->vmhost_os->find_files($repository_directory_path, "*", 1);
+
+ # Don't delete directories which contain files which shouldn't
reside in a repository direcotry
+ my @unsafe_file_paths = ();
+ push @unsafe_file_paths, grep(/-flat\./, @file_paths);
+ push @unsafe_file_paths, grep(/\.vmx$/, @file_paths);
+ if (@unsafe_file_paths) {
+ print "ERROR: image revision not deleted from
repository: $imagerevision_name\n";
+ print "Directory contains files which normally wouldn't
reside in an image repository directory:\n";
+ print join("\n", @unsafe_file_paths) . "\n";
+ next;
+ }
+
+ # Make sure directory contains a file name using the 2gbsparse
format
+ if (!grep(/-s\d+\.vmdk$/, @file_paths)) {
+ print "ERROR: image revision not deleted from
repository: $imagerevision_name\n";
+ print "Directory does not contain a 2GB sparse
formatted file name (xxx-s001.vmdk):\n";
+ print join("\n", @file_paths) . "\n";
+ next;
+ }
+
+ if ($self->vmhost_os->delete_file($repository_directory_path)) {
+ print "Done\n";
+ }
+ else {
+ print "\nERROR: failed to delete image revision:
$imagerevision_name\n";
+ exit;
+ }
+
+ if ($delete_count >= $delete_limit) {
+ last;
+ }
+ }
+
+ return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 get_datastore_image_names
Parameters : $datastore_base_path
@@ -8181,11 +8377,14 @@ sub get_datastore_imagerevision_names {
@datastore_imagerevision_names = sort keys
%datastore_imagerevision_name_hash;
my $datastore_imagerevision_name_count =
scalar(@datastore_imagerevision_names);
- my $ignored_count = scalar(@ignored);
print "\n";
if (@ignored) {
- print "$ignored_count files and/or directories ignored:\n" .
join("\n", @ignored) . "\n\n";
+ # Remove duplicates
+ my %ignored_hash = map { $_ => 1 } @ignored;
+ @ignored = sort keys %ignored_hash;
+ my $ignored_count = scalar(@ignored);
+ print "$ignored_count files and/or directories ignored, image
revision not found in database:\n" . join("\n", @ignored) . "\n\n";
}
print "$datastore_imagerevision_name_count images found in datastore
'$datastore_base_path'\n";