sub capture()

in managementnode/lib/VCL/Module/Provisioning/one.pm [292:481]


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;
	my $image_name = $self->data->get_image_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 $computer_name = $self->data->get_computer_hostname();
	my $one_new_image_id = 0;
	
	$self->data->set_imagemeta_sysprep(0);
	
	notify($ERRORS{'OK'}, 0, "ONE module starting image capture.");
	
	my $vmid = $self->_one_get_object_id("computer",$computer_name);
	if ($vmid) {
		# get {TEMPLATE}{DISK}{IMAGE} of $vmid, the name of current image
		
		$old_image_name = $self->_one_get_vm_disk($vmid);
		
		my @savedisk = $one{'server'}->call('one.vm.savedisk', $one{'auth'},$vmid,0,$image_name,'OS',$one{'false'});
		if ($savedisk[0][0]->value()) {
			notify($ERRORS{'OK'}, 0, "VM $vmid will be captured as $image_name");
			
		} else {
			notify($ERRORS{'CRITICAL'}, 0, $savedisk[0][1]);
			return;
		}
	} else {
		notify($ERRORS{'CRITICAL'}, 0, "Couldn't find vmid for $computer_name. Abort.");
		return 0;
	}

	# Call the OS module's pre_capture() subroutine (don't shutdown at the end)
	if ($self->os->can("pre_capture")) {
	 	if (!$self->os->pre_capture({end_state => 'on'})) {
			notify($ERRORS{'CRITICAL'}, 0, "failed to complete OS module's pre_capture tasks");
			return;
		} else {
			notify($ERRORS{'OK'}, 0, "OS's pre_capture complited OK.");
		}
	} else {
		notify($ERRORS{'CRITICAL'}, 0, "OS module doesn't implement pre_capture(). Abort.");
		return;
	}
	
	# pre_capture was called with {end_state => 'on'}. Need to shutdown VM via ACPI.
	if (!$self->power_off()) {
		notify($ERRORS{'CRITICAL'}, 0, "Couldn't shutdown $computer_name with power_off()");
		return 0;
	} else {
		# notify($ERRORS{'DEBUG'}, 0, "Sent 'shutdown' to computer $computer_name via provisioning module");
		# make sure VM enters STATE=ACTIVE (3) & LCM_STATE=EPILOG(11)
		# if VM doesn't reach EPILOG, wait for ACTIVE/RUNNING (3/3) and then send 'shutdown-hard', check for EPILOG again.
		my $sleep = 5;
		my $wait_time = 5 * 60; # how long to wait for LCM_STATE = EPILOG
		my $flag = 0;
		my $state;
		my $lcm_state;
		
		notify($ERRORS{'OK'}, 0, "Wait for the VM $vmid to enter ACTIVE/EPILOG (3/11) state...");
		EPILOG: while (1) {
			$state = $self->_one_get_vm_state($vmid);
			notify($ERRORS{'OK'}, 0, "VM $vmid is in $state state");
			if ($state == 3) {
				$lcm_state = $self->_one_get_vm_lcm_state($vmid);
				notify($ERRORS{'OK'}, 0, "VM $vmid is in $lcm_state lcm_state");
				if ($lcm_state == 11) {
					notify($ERRORS{'OK'}, 0, "VM $vmid is in EPILOG state. OK");
					last EPILOG;
				} else {
					notify($ERRORS{'OK'}, 0, "VM $vmid is in $state / $lcm_state state...");
				}
			} else {
				notify($ERRORS{'DEBUG'}, 0, "VM $vmid should be in ACTIVE (3) state, but it's in $state state");
				return 0;
			}
			sleep $sleep;
			$wait_time = $wait_time - $sleep;
			notify($ERRORS{'OK'}, 0, "Waiting for VM $vmid to enter ACTIVE/EPILOG state, $wait_time sec left ...");
			
			if ($wait_time <= $sleep) {
				notify($ERRORS{'DEBUG'}, 0, "VM $vmid never reached EPILOG state. Wait for ACTIVE/RUNNING (3/3) and send 'shutdown-hard'");
				my $sleep = 15;
				my $wait_time = 20 * 60; #how long to wait for ACTIVE/RUNNING (3/3)
				
				while (1) {
					$state = $self->_one_get_vm_state($vmid);
					if ($state == 3) {
						$lcm_state = $self->_one_get_vm_lcm_state($vmid);
						if ($lcm_state == 3) {
							notify($ERRORS{'OK'}, 0, "VM $vmid is in $state / $lcm_state state. Wait $sleep sec and send 'shutdown-hard'");
							sleep $sleep;
							if (!$self->power_off('hard')) {
								notify($ERRORS{'CRITICAL'}, 0, "Couldn't shutdown $computer_name with power_off('hard')");
								return 0;
							} 
							last EPILOG;
						} else {
							notify($ERRORS{'OK'}, 0, "VM $vmid is in $state / $lcm_state state...");
						}
					} else {
						notify($ERRORS{'OK'}, 0, "VM $vmid is in $state state...");
					}
					sleep $sleep if ($wait_time > 0);
					$wait_time = $wait_time - $sleep;
					notify($ERRORS{'OK'}, 0, "Waiting for VM $vmid to enter ACTIVE/RUNNING state, $wait_time sec left ...");
					if ($wait_time <= $sleep) {
						notify($ERRORS{'CRITICAL'}, 0, "VM $vmid is in $state / $lcm_state after $wait_time sec ... Fail!");
						return 0;
					}
				}
			}
		}
	}

	# Check that we have new_image_name created on ONE (it will be in LOCKED state until disk_save is done).
	# just procation, image stub should be created already.
	my $sleep = 5;
	my $wait_time = 20 * 60; # in min * 60 = seconds
	while (1) {
		$one_new_image_id = $self->_one_get_object_id("image",$image_name);
		last if ($one_new_image_id);
		$wait_time = $wait_time - $sleep;
		if ($wait_time <= 0) {
			notify($ERRORS{'CRITICAL'}, 0, "Could not locate new disk id for $image_name. disk_save wasn't successfull.");
			last;
		}
		sleep $sleep;
	}
	
	# wait until disk_save is done
	
	$wait_time = 30 * 60; # in min * 60 = seconds
	while (1) {
		notify($ERRORS{'OK'}, 0, "check status for new image id $one_new_image_id, $wait_time sec left...");
		my $one_image_state = $self->_one_get_image_state($one_new_image_id);
		if ($one_image_state == 4) {
			notify($ERRORS{'OK'}, 0, "disk save in pregress, image id $one_new_image_id is LOCKED");
		}
		if ($one_image_state == 5) {
			notify($ERRORS{'CRITICAL'}, 0, "disk save failed, image id $one_new_image_id is ERROR");
			return 0;
		}
		if ($one_image_state == 1) {
			notify($ERRORS{'OK'}, 0, "disk save OK, image id $one_new_image_id is READY");
			# check if template exists for the old image and create template for the new image.
			my $one_template_id = $self->_one_get_template_id($old_image_name); 
			if ($one_template_id) {
				notify($ERRORS{'OK'}, 0, "Found existing template id $one_template_id for $old_image_name");
				my @template_info = $one{'server'}->call('one.template.info',$one{'auth'},$one_template_id);
				if ($template_info[0][0]->value()) {
					my $data = XMLin($template_info[0][1]);
					my $template = $data->{TEMPLATE};
					$template->{NAME} = $image_name;
					
					if ((ref($template->{DISK})) eq "ARRAY" ) { # template has multiple disks, update [0]
						$template->{DISK}[0]{IMAGE_ID} = $one_new_image_id;
					} else { #template has one disk
						$template->{DISK}{IMAGE_ID} = $one_new_image_id;
					}	
					
					if (!$self->_one_create_template(XMLout($template,NoAttr => 1,RootName=>'TEMPLATE',))) {
						notify($ERRORS{'CRITICAL'}, 0, "Could't create $image_name template. Abort.");
					}
				} else {
					notify($ERRORS{'CRITICAL'}, 0, "Error while making one.template.info call: $template_info[0][1]");
				}
			} else {
				notify($ERRORS{'OK'}, 0, "No template exists for $old_image_name");
			}
			return 1;
		}
		if ($wait_time <= 0) {
			notify($ERRORS{'CRITICAL'}, 0, "disk save failed, image id $one_new_image_id is NOT READY. Fail.");
			return 0;
		}	
		sleep $sleep;
		$wait_time = $wait_time - $sleep;
	}
	
	return 0;
	
}