sub setup_capture_base_image()

in managementnode/lib/VCL/image.pm [388:865]


sub setup_capture_base_image {
	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;
	}
	
	# Get the management node id, needed to insert a reservation row later on
	my $management_node_id = $self->data->get_management_node_id();
	if (!$management_node_id) {
		print "ERROR: failed to determine the management node ID\n";
		return;
	}
	
	my ($request_id, $reservation_id) = 0;
	my $image_is_virtual = 0;
	
	print "\nTesting api call\n";
	if ($self->setup_test_rpc_xml(0)) {
		print "VCL API call successful\n\n";
	}
	
	# Is vcld service running
	if (!run_command('service vcld restart')) {
		print "ERROR: Unable to confirm vcld is running, Attempted to use service vcld restart\n";
		return;
	}
	
	
	# Get the user who the reservation and image will belong to
	my $user_id;
	my $username;
	while (!$user_id) {
		my $user_identifier = setup_get_input_string("Enter the VCL login name or ID of the user who will own the image:", 'admin');
		return if (!defined($user_identifier));
		my $user_info = get_user_info($user_identifier);
		if (!$user_info) {
			print "User was not found: $user_identifier\n";
		}
		else {
			$user_id = $user_info->{id};
			$username = $user_info->{unityid};
		}
	}
	print "\nUser who will own the image: $username (ID: $user_id)\n\n";
	
	# Determine the computer ID
	my $computer_id;
	my %computer_info;
	while (!$computer_id) {
		my $computer_identifier = setup_get_input_string("Enter the hostname or IP address of the computer to be captured:");
		return if (!defined($computer_identifier));
		
		# Search the computer table for a match
		my @computer_ids = get_computer_ids($computer_identifier);
		if (!@computer_ids) {
			print "No VCL computers were found with the name or IP address: $computer_identifier\n";
			next;
		}
		
		# Get information from the database for all of the computers found
		for my $computer_id (@computer_ids) {
			$computer_info{$computer_id} = get_computer_info($computer_id);
			if (!$computer_info{$computer_id}) {
				print "ERROR: unable to retrieve information for computer ID: $computer_id\n";
				return;
			}
		}
	
		if (scalar(@computer_ids) > 1) {
			print "Multiple VCL computers were found with the name or IP address: '$computer_identifier' (@computer_ids)\n\n";
			print "Choose a computer:\n";
			$computer_id = setup_get_hash_choice(\%computer_info, 'hostname');
			return if (!defined($computer_id));
		}
		else {
			$computer_id = (keys %computer_info)[0];
		}
		
	}
	
	my $computer_hostname = $computer_info{$computer_id}{hostname};
	my $computer_state_name = $computer_info{$computer_id}{state}{name};
	my $computer_provisioning_id = $computer_info{$computer_id}{provisioning}{id};
	my $computer_provisioning_module_name = $computer_info{$computer_id}{provisioning}{module}{name};
	my $computer_provisioning_module_pretty_name = $computer_info{$computer_id}{provisioning}{module}{prettyname};
	my $computer_provisioning_pretty_name = $computer_info{$computer_id}{provisioning}{prettyname};
	my $computer_provisioning_name = $computer_info{$computer_id}{provisioning}{name};
	my $computer_node_name = $computer_info{$computer_id}{SHORTNAME};
	
	my $osinstalltype_info = get_provisioning_osinstalltype_info($computer_provisioning_id);
	my @provisioning_osinstalltype_names = map { $osinstalltype_info->{$_}{name} } keys %$osinstalltype_info;
	
	print "\nComputer to be captured: $computer_hostname (ID: $computer_id)\n";
	print "Computer shortname: $computer_node_name\n";
	print "Computer State: $computer_state_name\n";
	print "Provisioning module: $computer_provisioning_module_pretty_name\n";
	print "OS install types: " . join(", ", sort @provisioning_osinstalltype_names) . "\n";
	
	my $vmhost_name;
	if ($computer_provisioning_module_name !~ /xcat/i) {
		$image_is_virtual = 1;
		#should have a vmhost assigned
		if ($computer_info{$computer_id}{vmhostid}) {
			$vmhost_name = $computer_info{$computer_id}{vmhost}{computer}{SHORTNAME};
			print "VM host name: $vmhost_name\n";
			print "VM host profile: $computer_info{$computer_id}{vmhost}{vmprofile}{profilename}\n";
			print "\n";
		}
		else {
			print "ERROR: Install type is vmware, $computer_node_name is NOT assigned to a vmhost\n";
			print "ERROR: Assign $computer_node_name to a vmhost before proceeding.\n";
			print "\n";
			return;
		}
	}
	
	print "Testing ssh access to $computer_hostname\n";
	
	# Node Checks
	# is the node up and accessible through ssh pki
	# If it is a vm, is it assigned to a vmhost
	# Try nmap to see if any of the ssh ports are open before attempting to run a test command
	my $port_22_status = nmap_port($computer_node_name, 22) ? "open" : "closed";
	my $port_24_status = nmap_port($computer_node_name, 24) ? "open" : "closed";
	if ($port_22_status ne 'open' && $port_24_status ne 'open') {
		print "Error: ssh port on $computer_node_name is NOT responding to SSH, ports 22 or 24 are both closed\n";
		return;
	}
	
	my ($exit_status, $output) = run_ssh_command({
		node => $computer_node_name,
		command => "echo \"testing ssh on $computer_node_name\"",
		max_attempts => 2,
		output_level => 0,
		timeout_seconds => 30,
	});
	
	# The exit status will be 0 if the command succeeded
	if (defined($output) && grep(/testing/, @$output)) {
		print "$computer_node_name is responding to SSH, port 22: $port_22_status, port 24: $port_24_status\n";
		print "\n";
	}
	else {
		print "ERROR: $computer_node_name is NOT responding to SSH, SSH command failed, port 22: $port_22_status, port 24: $port_24_status\n";
		print "Make sure you can login using ssh PKI on $computer_node_name before continuing\n"; 
		print "\n";
		return;
	}
	
	# Check if computer id is in an existing or failed imaging reservation
	my $computer_requests = get_request_by_computerid($computer_id);
	my %existing_requests_array_choices;
	if (keys(%$computer_requests)) {
		$existing_requests_array_choices{0}{"prettyname"} = "Delete all existing reservations for $computer_node_name";
		for my $competing_request_id (sort keys %$computer_requests) {
			my $competing_reservation_id = $computer_requests->{$competing_request_id}{data}->get_reservation_id();
			my $competing_imagerevision_id = $computer_requests->{$competing_request_id}{data}->get_imagerevision_id();
			my $competing_image_id = $computer_requests->{$competing_request_id}{data}->get_image_id();
			my $competing_prettyimage_name = $computer_requests->{$competing_request_id}{data}->get_image_prettyname();
			my $competing_image_name = $computer_requests->{$competing_request_id}{data}->get_image_name();
			my $competing_request_state = $computer_requests->{$competing_request_id}{data}->get_request_state_name();
			
			$existing_requests_array_choices{$competing_request_id}{"prettyname"} = $competing_prettyimage_name;
			$existing_requests_array_choices{$competing_request_id}{"name"} = $competing_image_name;
			$existing_requests_array_choices{$competing_request_id}{"image_id"} = $competing_image_id;
			$existing_requests_array_choices{$competing_request_id}{"image_revision_id"} = $competing_imagerevision_id;
			$existing_requests_array_choices{$competing_request_id}{"current_state"} = $competing_request_state;
			$existing_requests_array_choices{$competing_request_id}{"reservation_id"} = $competing_reservation_id;
		}
		
		my $num_computer_requests = keys(%$computer_requests);
		print "WARNING: Image capture reservation exists for $computer_node_name.\n"; 
		print "Either choose the image name to restart image capture for that request or choose none to delete the previous reservations:\n"; 
		
		my $chosen_request_id = setup_get_hash_choice(\%existing_requests_array_choices, 'prettyname');
		return if (!defined($chosen_request_id));
		my $chosen_prettyname = $existing_requests_array_choices{$chosen_request_id}{prettyname};
		print "\nSelected reservation: $chosen_request_id $chosen_prettyname\n\n";
		
		# if 0 selected, delete all reservations related to $computer_node_name
		# Set $computer_node_name to available, proceed with questions
		my $epoch_time = convert_to_epoch_seconds;
		if ($chosen_request_id == 0) {
			delete $existing_requests_array_choices{0};
			
			foreach my $request_id_del (sort keys %existing_requests_array_choices) {
				my $del_reservation_id = $existing_requests_array_choices{$request_id_del}{reservation_id};
				my $del_image_id = $existing_requests_array_choices{$request_id_del}{image_id};
				my $del_imagerevision_id = $existing_requests_array_choices{$request_id_del}{image_revision_id};
				my $del_image_name = $existing_requests_array_choices{$request_id_del}{name};
				print "del_image_name= $del_image_name\n";
				my $new_image_name = $del_image_name . $epoch_time;
				my $new_prettyimage_name = $existing_requests_array_choices{$request_id_del}{prettyname} . $epoch_time;
				
				if (reservation_being_processed($del_reservation_id)) {
					print "WARNING: The selected reservation is currently being processed. You must wait until it has completed.\n";
					print "Reservation id: $del_reservation_id\n";
					print "\n";
					next;
				}
				
				if (delete_request($request_id_del)) {
					print "Removed reservation id $request_id_del for $del_image_name\n";
					if (update_image_name($del_image_id, $del_imagerevision_id, $new_image_name, $new_prettyimage_name)) {
					}
					if (update_computer_state($computer_id, "available")) {
						print "Set $computer_node_name to available state\n";
					}
				}
			}
		}
		# Elseif a request id is choosen. set $computer_node_name to available, test ssh access, restart image capture
		if ($chosen_request_id) {
			$request_id = $chosen_request_id;
			$reservation_id = $existing_requests_array_choices{$chosen_request_id}{reservation_id};
			if (reservation_being_processed($chosen_request_id)) {
				print "WARNING: The selected reservation is currently being processed. You must wait until it has completed.\n";
				print "Reservation id: $chosen_request_id\n";
				print "\n";
				my @yes_no_choices = (
					'Yes',
					'No',
				);
				
				print "Monitor vcld.log for completion?:\n";
				my $monitor_choice_index = setup_get_array_choice(@yes_no_choices);
				last if (!defined($monitor_choice_index));
				my $monitor_choice = $yes_no_choices[$monitor_choice_index];
				if ($monitor_choice =~ /yes/i) {
					print ".\n";	
					goto MONITOR_LOG_OUTPUT;
				}
				else {
					return;
				}
			}
			if (update_computer_state($computer_id, "available")) {
				print "Set $computer_node_name to available state\n";
			}
			$chosen_prettyname = $existing_requests_array_choices{$chosen_request_id}{prettyname};
			print "Restarting image capture for: \nRequest id= $chosen_request_id \nImage Name: $chosen_prettyname \nNode Name: $computer_node_name\n";
			
			if (update_request_state($chosen_request_id, "image", "image", 1)) {
				print "Set request_id= $chosen_request_id to image state\n\n";
				print "Starting monitor process:\n\n";
				
				goto MONITOR_LOG_OUTPUT;
			}
			else {
				print "ERROR: failed to update request state for $chosen_request_id, state_name= image, last_state: image\n";
			}
		}
	}
	
	# Make sure the computer state is valid
	if ($computer_state_name =~ /(maintenance|deleted)/i) {
		print "ERROR: state of $computer_node_name is $computer_state_name\n";
		print "\n";
		return;
	}
	
	# Get the OS table contents from the database
	my $os_info = get_os_info();
	if (!$os_info) {
		print "ERROR: failed to retrieve OS info from the database\n";
		return;
	}
	
	# Loop through the OS table info
	OS_ID: for my $os_id (keys %$os_info) {
		my $osinstalltype_name = $os_info->{$os_id}{installtype};
		
		# Remove keys where the name begins with esx - deprecated OS type
		if ($osinstalltype_name =~ /^vmwareesx/i) {
			delete $os_info->{$os_id};
			next;
		}
		
		# Remove keys which don't match the selected computer type
		for my $provisioning_osinstalltype_name (@provisioning_osinstalltype_names) {
			if ($provisioning_osinstalltype_name eq $osinstalltype_name) {
				next OS_ID;
			}
		}
		
		delete $os_info->{$os_id};
	}
	
	print "Select the OS to be captured (install type: " . join(', ', sort @provisioning_osinstalltype_names) . "):\n";
	my $os_id = setup_get_hash_choice($os_info, 'prettyname');
	return if (!defined($os_id));
	my $os_prettyname = $os_info->{$os_id}{prettyname};
	my $os_module_perl_package = $os_info->{$os_id}{module}{perlpackage};
	my $os_type = $os_info->{$os_id}{type};
	print "\nSelected OS: $os_prettyname\n\n";
	
	my @architecture_choices = (
		'x86',
		'x86_64',
	);
	print "Image architecture:\n";
	my $architecture_choice_index = setup_get_array_choice(@architecture_choices);
	last if (!defined($architecture_choice_index));
	my $architecture_choice = $architecture_choices[$architecture_choice_index];
	print "\nImage architecture: $architecture_choice\n\n";
	
	# If Windows, ask if Sysprep should be used
	my $use_sysprep = 1;
	if ($os_type =~ /windows/i) {
		my @yes_no_choices = (
			'Yes',
			'No',
		);
		
		print "Use Sysprep:\n";
		my $sysprep_choice_index = setup_get_array_choice(@yes_no_choices);
		last if (!defined($sysprep_choice_index));
		my $use_sysprep_choice = $yes_no_choices[$sysprep_choice_index];
		print "\nUse Sysprep: $use_sysprep_choice\n\n";
		
		if ($use_sysprep_choice =~ /no/i) {
			$use_sysprep = 0;
		}
	}
	
	my $image_prettyname;
	while (!$image_prettyname) {
		$image_prettyname = setup_get_input_string("Enter the name of the image to be captured:");
		return if (!defined($image_prettyname));
		#if ($image_prettyname =~ //) {
		#	print "Image name is not valid: $image_prettyname\n";
		#	$image_prettyname = 0;
		#}
	}
	
	my $image_name = $image_prettyname;
	$image_name =~ s/[\s\W]//g;
	$image_name = $os_info->{$os_id}{name} . "-$image_name-v0";
	
	my $insert_imagemeta_statement = <<EOF;
INSERT INTO imagemeta
(sysprep)
VALUES
('$use_sysprep')
EOF
	
	my $imagemeta_id = database_execute($insert_imagemeta_statement);
	if (!defined($imagemeta_id)) {
		print "ERROR: failed to insert into imagemeta table.\n";
		return;
	}
	
	
	my $insert_image_statement = <<EOF;
INSERT INTO image
(name, prettyname, ownerid, platformid, OSid, imagemetaid, deleted, lastupdate, size, architecture, basedoffrevisionid)
VALUES
(
'$image_name',
'$image_prettyname',
'$user_id',
'1',
$os_id,
$imagemeta_id,
'1',
NOW(),
'1',
'$architecture_choice',
(SELECT id FROM imagerevision WHERE imagename = 'noimage')
)
EOF
	
	my $image_id = database_execute($insert_image_statement);
	if (!defined($image_id)) {
		print "ERROR: failed to insert into image table. Please choose another name.\n";
		return;
	}
	
	# Add the newly inserted image ID to the image name
	$image_name =~ s/-v0$/$image_id-v0/;
	
	# Upadate the name in the image table
	my $update_image_statement = <<EOF;
UPDATE image
SET name = '$image_name'
WHERE
id = $image_id
EOF
	if (!database_execute($update_image_statement)) {
		print "ERROR: failed to update the image table with the correct image name: $image_name\n";
		return;
	}
	
	
	my $insert_imagerevision_statement = <<EOF;
INSERT INTO imagerevision
(imageid, revision, userid, datecreated, deleted, production, imagename)
VALUES
($image_id, '0', '$user_id', NOW(), '1', '1', '$image_name')
EOF

	my $imagerevision_id = database_execute($insert_imagerevision_statement);
	if (!defined($imagerevision_id)) {
		print "ERROR: failed to insert into imagerevision table\n";
		return;
	}
	
	my $insert_resource_statement = <<EOF;
INSERT INTO resource
(resourcetypeid, subid)
VALUES ('13', '$image_id')
EOF
	
	my $resource_id = database_execute($insert_resource_statement);
	if (!defined($resource_id)) {
		print "ERROR: failed to insert into resource table\n";
		return;
	}
	
	# Add image resource_id to users' new image group
	if (!add_imageid_to_newimages($user_id, $resource_id, $image_is_virtual)) {
		print "\nWARNING: Failed to add image to user's new images group\n";
		print "You might need to add manually to the new images or all images image groups\n";
		print "Continuing to with image capture\n\n";
	}
	
	print "\nAdded new image to database: '$image_prettyname'\n";
	print "   image.name: $image_name\n";
	print "   image.id: $image_id\n";
	print "   imagerevision.id: $imagerevision_id\n";
	print "   imagemeta.id: $imagemeta_id\n";
	print "   resource.id: $resource_id\n\n";
	
	
	($request_id, $reservation_id) = insert_request($management_node_id, 'image', 'image', $username, $computer_id, $image_id, $imagerevision_id, 0, 60);
	if (!defined($request_id) || !defined($reservation_id)) {
		print "ERROR: failed to insert new imaging request\n";
		return;
	}
	
	my $process_regex = get_reservation_vcld_process_name_regex($reservation_id) || $reservation_id;
	$process_regex =~ s/^\w+\s//;
	
	my $message = <<EOF;
Inserted imaging request to the database:
request ID: $request_id
reservation ID: $reservation_id

This process will now display the contents of the vcld.log file if the vcld
daemon is running. If you do not see many lines of additional output, exit this
process, start the vcld daemon, and monitor the image capture process by running
the command:
tail -f $LOGFILE | grep -P '$process_regex'

EOF
	
	print '-' x 76 . "\n";
	print "$message";
	print '-' x 76 . "\n";
	
	MONITOR_LOG_OUTPUT:
	# Pipe the command output to a file handle
	# The open function returns the pid of the process
	if (open(COMMAND, "tail -f $LOGFILE 2>&1 |")) {
		# Capture the output of the command
		while (my $output = <COMMAND>) {
			if ($output =~ /$reservation_id/) {
				print $output;
				if ($output =~ /complete/i) {
					last;
				}
			}
		}
	}
	
	exit;
}