sub capture()

in managementnode/lib/VCL/Module/Provisioning/libvirt.pm [353:557]


sub capture {
	my $self = shift;
	unless (ref($self) && $self->isa('VCL::Module')) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return;
	}
	
	my $old_image_name = $self->data->get_image_name();
	
	# Construct the new image name
	my $new_image_name = $self->get_new_image_name();
	$self->data->set_image_name($new_image_name);
	
	my $request_state_name = $self->data->get_request_state_name();
	my $image_id = $self->data->get_image_id();
	my $imagerevision_id = $self->data->get_imagerevision_id();
	my $image_type = $self->data->get_imagetype_name();
	my $node_name = $self->data->get_vmhost_short_name();
	my $computer_name = $self->data->get_computer_short_name();
	my $master_image_directory_path = $self->get_master_image_directory_path();
	my $master_image_file_path = $self->get_master_image_file_path();
	my $datastore_image_type = $self->data->get_vmhost_datastore_imagetype_name();
	my $repository_image_directory_path = $self->get_repository_image_directory_path();
	my $repository_image_file_path = $self->get_repository_image_file_path();
	my $repository_image_type = $self->data->get_vmhost_repository_imagetype_name();
	
	# Set the imagemeta Sysprep value to 0 to prevent Sysprep from being used
	$self->data->set_imagemeta_sysprep(0);
	
	# Get the domain name
	my $domain_name = $self->get_domain_name();
	if (!$domain_name) {
		notify($ERRORS{'WARNING'}, 0, "unable to capture image on $node_name, domain name could not be determined");
		return;
	}
	
	notify($ERRORS{'DEBUG'}, 0, "beginning image capture:\n" . <<EOF
image id: $image_id
imagerevision id: $imagerevision_id
old image name: $old_image_name
new image name: $new_image_name
---
host node: $node_name
computer: $computer_name
---
master image directory path: $master_image_directory_path
master image file path: $master_image_file_path
---
old image type: $image_type
datastore image type: $datastore_image_type
---
repository image type: $repository_image_type
repository image directory path: $repository_image_directory_path
repository image file path: $repository_image_file_path
EOF
);
	
	# Call the OS module's pre_capture() subroutine
	if ($self->os->can("pre_capture") && !$self->os->pre_capture({end_state => 'on'})) {
		notify($ERRORS{'WARNING'}, 0, "failed to complete OS module's pre_capture tasks");
		return;
	}

	# Check the power status before proceeding
	my $power_status = $self->power_status();
	if (!$power_status) {
		notify($ERRORS{'WARNING'}, 0, "unable to capture image on $node_name, power status of '$domain_name' domain could not be determined");
		return;
	}
	elsif ($power_status !~ /on/i) {
		notify($ERRORS{'WARNING'}, 0, "unable to capture image on $node_name, power status of '$domain_name' domain is $power_status");
		return;
	}
	
	# Make sure the master image file doesn't already exist
	if ($self->vmhost_os->file_exists($master_image_file_path)) {
		notify($ERRORS{'WARNING'}, 0, "master image file already exists on $node_name: $master_image_file_path");
		return;
	}
	
	# Make sure the repository image file doesn't already exist if the repository path is configured
	if ($repository_image_file_path && $self->vmhost_os->file_exists($repository_image_file_path)) {
		notify($ERRORS{'WARNING'}, 0, "repository image file already exists on $node_name: $repository_image_file_path");
		return;
	}
	
	# Get the domain XML definition
	my $domain_xml_string = $self->get_domain_xml_string($domain_name);
	if (!$domain_xml_string) {
		notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to retrieve domain XML definition: $domain_name");
		return;
	}
	
	# Delete existing XML definition files
	$self->os->delete_file("~/*-v*.xml");
	
	# Save the domain XML definition to a file in the image
	my $image_xml_file_path = "~/$new_image_name.xml";
	if ($self->os->create_text_file($image_xml_file_path, $domain_xml_string)) {
		notify($ERRORS{'OK'}, 0, "saved domain XML definition text file in image: $image_xml_file_path");
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to save domain XML definition text file in image: $image_xml_file_path, contents:\n$domain_xml_string");
		return;
	}
	
	# Save the domain XML definition to a file in the master image directory
	my $master_xml_file_path = $self->get_master_xml_file_path();
	if ($self->vmhost_os->create_text_file($master_xml_file_path, $domain_xml_string)) {
		notify($ERRORS{'OK'}, 0, "saved domain XML definition text file to master image directory: $master_xml_file_path");
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to save domain XML definition text file: $master_xml_file_path");
		return;
	}

	# Update the image name in the database
	if ($old_image_name ne $new_image_name && !update_image_name($image_id, $imagerevision_id, $new_image_name)) {
		notify($ERRORS{'WARNING'}, 0, "failed to update image name in the database: $old_image_name --> $new_image_name");
		return;
	}
	
	# Update the image type in the database to the datastore image type
	if ($image_type ne $datastore_image_type && !update_image_type($image_id, $datastore_image_type)) {
		notify($ERRORS{'WARNING'}, 0, "failed to update image type in the database: $image_type --> $datastore_image_type");
		return;
	}
	
	# Shutdown domain 
	if (!$self->os->shutdown()) {
		notify($ERRORS{'WARNING'}, 0, "$domain_name has not powered off after the OS module's pre_capture tasks were completed, powering off forcefully");
		if (!$self->power_off($domain_name)) {
			notify($ERRORS{'WARNING'}, 0, "failed to power off $domain_name after the OS module's pre_capture tasks were completed");
			return;
		}
	}
	
	# Get the disk file paths from the domain definition
	my @disk_file_paths = $self->get_domain_disk_file_paths($domain_name);
	if (scalar @disk_file_paths == 0) {
		notify($ERRORS{'WARNING'}, 0, "did not find any disks defined in the XML definition for $domain_name:\n" . format_data($domain_name));
		return;
	}
	elsif (scalar @disk_file_paths > 1) {
		notify($ERRORS{'WARNING'}, 0, "found multiple disks defined in the XML definition for $domain_name, only the first disk will be captured:\n" . format_data(\@disk_file_paths));
	}

	# Copy the linked clone to create a new master image file
	my $linked_clone_file_path = $disk_file_paths[0];
	notify($ERRORS{'DEBUG'}, 0, "retrieved linked clone file path from domain $domain_name: $linked_clone_file_path");
	if ($self->driver->can('copy_virtual_disk')) {
		# Get a semaphore so that multiple processes don't try to copy/access the image at the same time
		# Since this is a new image, should get semaphore on 1st try
		if (my $semaphore = $self->get_master_image_semaphore()) {
			if ($self->driver->copy_virtual_disk($linked_clone_file_path, $master_image_file_path, $datastore_image_type)) {
				notify($ERRORS{'DEBUG'}, 0, "created master image from linked clone: $linked_clone_file_path --> $master_image_file_path");
			}
			else {
				notify($ERRORS{'WARNING'}, 0, "failed to create master image from linked clone: $linked_clone_file_path --> $master_image_file_path");
				return;
			}
		}
		else {
			notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to obtain semaphore before creating master image from linked clone: $linked_clone_file_path --> $master_image_file_path");
			return;
		}
		
		# Copy the master image to the repository if the repository path is configured in the VM host profile
		if ($repository_image_file_path) {
			if (my $semaphore = $self->get_repository_image_semaphore()) {
				if ($self->driver->copy_virtual_disk($master_image_file_path, $repository_image_file_path, $repository_image_type)) {
					notify($ERRORS{'DEBUG'}, 0, "created repository image from master image: $master_image_file_path --> $repository_image_file_path");
				}
				else {
					notify($ERRORS{'WARNING'}, 0, "failed to create repository image from master image: $master_image_file_path --> $repository_image_file_path");
					return;
				}
			}
			else {
				notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to obtain semaphore before copying master image to repository mounted on $node_name: $master_image_file_path --> $repository_image_file_path");
				return;
			}
			
			# Save the domain XML definition to a file in the repository image directory
			my $repository_xml_file_path = $self->get_repository_xml_file_path();
			if ($self->vmhost_os->create_text_file($repository_xml_file_path, $domain_xml_string)) {
				notify($ERRORS{'OK'}, 0, "saved domain XML definition text file to repository image directory: $master_xml_file_path");
			}
			else {
				notify($ERRORS{'WARNING'}, 0, "failed to capture image on $node_name, unable to save domain XML definition text file to repository image directory: $master_xml_file_path");
				return;
			}
		}
	}
	
	if ($request_state_name !~ /^(image)$/) {
		notify($ERRORS{'OK'}, 0, "domain will NOT be deleted because the request state is '$request_state_name'");
	}
	else {
		# Image has been captured, delete the domain
		$self->delete_domain($domain_name);
	}
	
	return 1;
} ## end sub capture