sub run_sysprep()

in managementnode/lib/VCL/Module/OS/Windows/Version_5.pm [191:368]


sub run_sysprep {
	my $self = shift;
	if (ref($self) !~ /windows/i) {
		notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
		return;
	}

	my $computer_node_name   = $self->data->get_computer_node_name();
	my $system32_path        = $self->get_system32_path();
	
	# Specify where on the node the sysprep.inf file will reside
	my $node_configuration_directory = $self->get_node_configuration_directory();
	my $node_configuration_sysprep_directory = "$node_configuration_directory/Utilities/Sysprep";
	my $node_configuration_sysprep_inf_path = "$node_configuration_sysprep_directory/sysprep.inf";
	my $node_working_sysprep_directory = 'C:/Sysprep';
	my $node_working_sysprep_exe_path = 'C:\\Sysprep\\sysprep.exe';
	
	# Get the sysprep.inf file contents
	my $sysprep_contents = $self->get_sysprep_inf_contents();
	if (!$sysprep_contents) {
		notify($ERRORS{'WARNING'}, 0, "failed to get sysprep.inf contents");
		return;
	}
	
	# Create a tempfile on the management node to store the sysprep.inf contents
	my $tmp_sysprep_inf_path;
	my $tmp_sysprep_inf_fh;
	do {
		$tmp_sysprep_inf_path = tmpnam();
	}
	until $tmp_sysprep_inf_fh = IO::File->new($tmp_sysprep_inf_path, O_RDWR|O_CREAT|O_EXCL);
	notify($ERRORS{'DEBUG'}, 0, "created tempfile: $tmp_sysprep_inf_path");
	
	# Print the sysprep.inf contents to the tempfile
	if (print $tmp_sysprep_inf_fh $sysprep_contents) {
		notify($ERRORS{'DEBUG'}, 0, "printed sysprep.inf contents to tempfile: $tmp_sysprep_inf_path");
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to print sysprep.inf contents to tempfile: $tmp_sysprep_inf_path");
		return;
	}
	
	# SCP the sysprep.inf tempfile to the computer
	if (!$self->copy_file_to($tmp_sysprep_inf_path, $node_configuration_sysprep_inf_path)) {
		notify($ERRORS{'WARNING'}, 0, "failed to copy $tmp_sysprep_inf_path to $computer_node_name:$node_configuration_sysprep_inf_path");
		return;
	}
	
	# Delete the sysprep.inf tempfile from the management node
	if (unlink $tmp_sysprep_inf_path) {
		notify($ERRORS{'DEBUG'}, 0, "deleted sysprep.inf tempfile: $tmp_sysprep_inf_path");
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to delete sysprep.inf tempfile: $tmp_sysprep_inf_path, $!");
	}

	
	# Remove old C:\Sysprep directory if it exists
	notify($ERRORS{'DEBUG'}, 0, "attempting to remove old $node_working_sysprep_directory directory if it exists");
	if (!$self->delete_file($node_working_sysprep_directory)) {
		notify($ERRORS{'WARNING'}, 0, "unable to remove existing $node_working_sysprep_directory directory");
		return 0;
	}
	
	# Copy Sysprep files to C:\Sysprep
	my $xcopy_command = "cp -rvf \"$node_configuration_sysprep_directory\" \"$node_working_sysprep_directory\"";
	my ($xcopy_status, $xcopy_output) = $self->execute($xcopy_command);
	if (defined($xcopy_status) && $xcopy_status == 0) {
		notify($ERRORS{'OK'}, 0, "copied Sysprep files to $node_working_sysprep_directory");
	}
	elsif (defined($xcopy_status)) {
		notify($ERRORS{'WARNING'}, 0, "failed to copy Sysprep files to $node_working_sysprep_directory, exit status: $xcopy_status, output:\n@{$xcopy_output}");
		return 0;
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to copy Sysprep files to $node_working_sysprep_directory");
		return 0;
	}
	
	
	# Set the DevicePath registry key
	# This is used to locate device drivers
	if (!$self->set_device_path_key()) {
		notify($ERRORS{'WARNING'}, 0, "failed to set the DevicePath registry key");
		return;
	}
	
	# Delete driver cache files
	$self->delete_files_by_pattern('$SYSTEMROOT/inf', '.*INFCACHE.*', 1);
	$self->delete_files_by_pattern('$SYSTEMROOT/inf', '.*oem[0-9]+\\..*', 1);
	
	# Delete setupapi.log - this is the main log file for troubleshooting Sysprep
	# Contains device & driver changes, major system changes, service pack installations, hotfix installations
	$self->delete_files_by_pattern('$SYSTEMROOT', '.*\/setupapi.*', 1);
	
	# Delete Windows setup log files
	$self->delete_files_by_pattern('$SYSTEMROOT', '.*\/setuperr\\..*', 1);
	$self->delete_files_by_pattern('$SYSTEMROOT', '.*\/setuplog\\..*', 1);
	$self->delete_files_by_pattern('$SYSTEMROOT', '.*\/setupact\\..*', 1);
	
	# Configure the firewall to allow the sessmgr.exe program
	# Sysprep may hang with a dialog box asking to allow this program
	if (!$self->firewall_enable_sessmgr()) {
		notify($ERRORS{'WARNING'}, 0, "unable to configure firewall to allow sessmgr.exe program, Sysprep may hang");
	}
	
	# Kill the screen saver process, it occasionally prevents reboots and shutdowns from working
	$self->kill_process('logon.scr');
	
	# Assemble the Sysprep command
	# Run Sysprep.exe, use cygstart to lauch the .exe and return immediately
	my $sysprep_command = "/bin/cygstart.exe \$SYSTEMROOT/system32/cmd.exe /c \"";
	
	# Run Sysprep.exe
	$sysprep_command .= "C:/Sysprep/sysprep.exe /quiet /reseal /mini /forceshutdown & ";
	
	# Shutdown the computer - Sysprep does not always shut the computer down automatically
	# Check if tsshutdn.exe exists on the computer
	# tsshutdn.exe is the preferred utility, shutdown.exe often fails on Windows Server 2003
	my $windows_product_name = $self->get_product_name() || '';
	if ($windows_product_name =~ /2003/ && $self->file_exists("$system32_path/tsshutdn.exe")) {
		$sysprep_command .= "$system32_path/tsshutdn.exe 0 /POWERDOWN /DELAY:0 /V";
	}
	else {
		$sysprep_command .= "$system32_path/shutdown.exe /s /t 0 /f";
	}
	
	$sysprep_command .= "\"";
	
	my ($sysprep_status, $sysprep_output) = $self->execute($sysprep_command);
	if (defined($sysprep_status) && $sysprep_status == 0) {
		notify($ERRORS{'OK'}, 0, "initiated Sysprep.exe, waiting for $computer_node_name to become unresponsive");
	}
	elsif (defined($sysprep_status)) {
		notify($ERRORS{'WARNING'}, 0, "failed to initiate Sysprep.exe, exit status: $sysprep_status, output:\n@{$sysprep_output}");
		return 0;
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to initiate Sysprep.exe");
		return 0;
	}

	# Wait maximum of 10 minutes for the computer to become unresponsive
	if (!$self->wait_for_no_ping(600)) {
		# Computer never stopped responding to ping
		notify($ERRORS{'WARNING'}, 0, "$computer_node_name never became unresponsive to ping");
		return 0;
	}
	
	# Wait maximum of 10 minutes for computer to power off
	my $power_off = $self->provisioner->wait_for_power_off(600);
	if (!defined($power_off)) {
		# wait_for_power_off result will be undefined if the provisioning module doesn't implement a power_status subroutine
		notify($ERRORS{'OK'}, 0, "unable to determine power status of $computer_node_name from provisioning module, sleeping 5 minutes to allow computer time to power off");
		sleep 300;
	}
	elsif ($power_off) {
		notify($ERRORS{'OK'}, 0, "$computer_node_name powered off after running Sysprep.exe");
		return 1;
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "$computer_node_name never powered off after running sysprep.exe");
	}
	
	# Computer never powered off, check if provisioning module can forcefully power off the computer
	if (!$self->provisioner->can('power_off')) {
		notify($ERRORS{'OK'}, 0, "provisioning module does not implement a power_off subroutine");
		return 1;
	}
	elsif ($self->provisioner->power_off()) {
		notify($ERRORS{'OK'}, 0, "forcefully powered off $computer_node_name");
		return 1;
	}
	else {
		notify($ERRORS{'WARNING'}, 0, "failed to forcefully power off $computer_node_name");
		return 0;
	}
}