Author: arkurth Date: Fri Jan 15 17:41:26 2010 New Revision: 899730 URL: http://svn.apache.org/viewvc?rev=899730&view=rev Log: VCL-145 Removed old run_sysprep and prepare_drivers subroutines from Windows.pm. These have been replaced by subroutines in Version_5.pm.
Updated Version_6.pm to utilize the product key and KMS server information now stored in the winProductKey and winKMS tables. The subroutines in Version_6.pm which call slmgr.vbs were not working for 64-bit OS's. This has been fixed. Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=899730&r1=899729&r2=899730&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Fri Jan 15 17:41:26 2010 @@ -5990,231 +5990,6 @@ #///////////////////////////////////////////////////////////////////////////// -=head2 run_sysprep - - Parameters : None - Returns : 1 if successful, 0 otherwise - Description : -Calls subroutine to prepare the hardware drivers - -Copies Sysprep files to C:\Sysprep - -Clears out the setupapi.log file - -Calls Sysprep.exe with the options to seal and shutdown the computer - -Waits for computer to become unresponsive - -Waits 3 additional minutes - -Calls provisioning module's power_off() subroutine to make sure the computer is powered off - -=cut - -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 $management_node_keys = $self->data->get_management_node_keys(); - my $computer_node_name = $self->data->get_computer_node_name(); - - # Remove old C:\Sysprep directory if it exists - notify($ERRORS{'DEBUG'}, 0, "attempting to remove old C:/Sysprep directory if it exists"); - if (!$self->delete_file('C:/Sysprep')) { - notify($ERRORS{'WARNING'}, 0, "unable to remove existing C:/Sysprep directory"); - return 0; - } - - # Fix the path, xcopy.exe requires backslashes - (my $node_configuration_directory = $NODE_CONFIGURATION_DIRECTORY) =~ s/\//\\/g; - - # Copy Sysprep files to C:\Sysprep - my $xcopy_command = "xcopy.exe /E /C /I /Q /H /K /O /Y \"$node_configuration_directory\\Utilities\\Sysprep\" \"C:\\Sysprep\""; - my ($xcopy_status, $xcopy_output) = run_ssh_command($computer_node_name, $management_node_keys, $xcopy_command); - if (defined($xcopy_status) && $xcopy_status == 0) { - notify($ERRORS{'OK'}, 0, "copied Sysprep files to C:/Sysprep"); - } - elsif (defined($xcopy_status)) { - notify($ERRORS{'WARNING'}, 0, "failed to copy Sysprep files to C:/Sysprep, exit status: $xcopy_status, output:\...@{$xcopy_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to copy Sysprep files to C:/Sysprep"); - return 0; - } - - # Copy and scan drivers - notify($ERRORS{'DEBUG'}, 0, "attempting to copy and scan drivers"); - if (!$self->prepare_drivers()) { - notify($ERRORS{'WARNING'}, 0, "unable to copy and scan drivers"); - return 0; - } - - # 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"); - return 0; - } - - # Clear out setupapi.log - my $setupapi_command = "/bin/cat C:/Windows/setupapi.log >> C:/Windows/setupapi_save.log && /bin/cp /dev/null C:/Windows/setupapi.log"; - my ($setupapi_status, $setupapi_output) = run_ssh_command($computer_node_name, $management_node_keys, $setupapi_command); - if (defined($setupapi_status) && $setupapi_status == 0) { - notify($ERRORS{'OK'}, 0, "cleared out setupapi.log"); - } - elsif (defined($setupapi_status)) { - notify($ERRORS{'OK'}, 0, "failed to clear out setupapi.log, exit status: $setupapi_status, output:\...@{$setupapi_output}"); - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to clear out setupapi.log"); - return 0; - } - - # Run Sysprep.exe, use cygstart to lauch the .exe and return immediately - my $sysprep_command = '/bin/cygstart.exe cmd.exe /c "C:/Sysprep/sysprep.exe /forceshutdown /quiet /reseal /mini"'; - my ($sysprep_status, $sysprep_output) = run_ssh_command($computer_node_name, $management_node_keys, $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{'OK'}, 0, "failed to initiate Sysprep.exe, exit status: $sysprep_status, output:\...@{$sysprep_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to initiate Sysprep.exe"); - return 0; - } - - # Wait maximum of 5 minutes for the computer to become unresponsive - if (!$self->wait_for_no_ping(5)) { - # Computer never stopped responding to ping - notify($ERRORS{'WARNING'}, 0, "$computer_node_name never became unresponsive to ping"); - return 0; - } - - # Wait for 3 minutes then call provisioning module's power_off() subroutine - # Sysprep does not always shut down the computer when it is done - notify($ERRORS{'OK'}, 0, "sleeping for 3 minutes to allow Sysprep.exe to finish"); - sleep 180; - - # Call power_off() to make sure computer is shut down - if (!$self->provisioner->power_off()) { - # Computer could not be shut off - notify($ERRORS{'WARNING'}, 0, "unable to power off $computer_node_name"); - return 0; - } - - return 1; -} ## end sub run_sysprep - -#///////////////////////////////////////////////////////////////////////////// - -=head2 prepare_drivers - - Parameters : - Returns : - Description : - -=cut - -sub prepare_drivers { - 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 $management_node_keys = $self->data->get_management_node_keys(); - my $computer_node_name = $self->data->get_computer_node_name(); - my $imagemeta_sysprep = $self->data->get_imagemeta_sysprep(); - - my $driver_directory; - if ($imagemeta_sysprep) { - $driver_directory = 'C:/Sysprep/Drivers'; - } - else { - $driver_directory = 'C:/Drivers'; - } - - - # Remove old driver directories if they exists - notify($ERRORS{'DEBUG'}, 0, "attempting to remove old C:/Sysprep\\Drivers directory if it exists"); - if (!$self->delete_file("C:/Sysprep/Drivers")) { - notify($ERRORS{'WARNING'}, 0, "unable to remove existing C:/Sysprep/Drivers directory"); - } - notify($ERRORS{'DEBUG'}, 0, "attempting to remove old C:/Drivers directory if it exists"); - if (!$self->delete_file("C:/Drivers")) { - notify($ERRORS{'WARNING'}, 0, "unable to remove existing C:/Drivers directory"); - } - - # Copy driver files to C:/Drivers - my $cp_command = "mkdir -p \"$driver_directory\" && cp -rf -T \"$NODE_CONFIGURATION_DIRECTORY/Drivers\" \"$driver_directory\""; - my ($cp_status, $cp_output) = run_ssh_command($computer_node_name, $management_node_keys, $cp_command); - if (defined($cp_status) && $cp_status == 0) { - notify($ERRORS{'DEBUG'}, 0, "copied driver files to $driver_directory"); - } - elsif (defined($cp_status)) { - notify($ERRORS{'OK'}, 0, "failed to copy driver files to $driver_directory, exit status: $cp_status, output:\...@{$cp_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to drivers files to $driver_directory"); - return 0; - } - - # Delete existing DevicePath key - my $reg_del_command = $self->get_system32_path() . '/reg.exe DELETE "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion" /v DevicePath /f'; - my ($reg_del_status, $reg_del_output) = run_ssh_command($computer_node_name, $management_node_keys, $reg_del_command); - if (defined($reg_del_status) && $reg_del_status == 0) { - notify($ERRORS{'DEBUG'}, 0, "deleted existing DevicePath key"); - } - elsif (defined($reg_del_status)) { - notify($ERRORS{'OK'}, 0, "failed to delete existing DevicePath key, exit status: $reg_del_status, output:\...@{$reg_del_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to delete existing DevicePath key"); - return 0; - } - - # Run spdrvscn.exe - my $spdrvscn_command = "$NODE_CONFIGURATION_DIRECTORY/Utilities/SPDrvScn/spdrvscn.exe /p \"$driver_directory\" /e inf /d \$SYSTEMROOT\\\\inf /a /s /q"; - my ($spdrvscn_status, $spdrvscn_output) = run_ssh_command($computer_node_name, $management_node_keys, $spdrvscn_command); - if (defined($spdrvscn_status) && $spdrvscn_status == 0) { - notify($ERRORS{'OK'}, 0, "executed spdrvscn.exe"); - } - elsif (defined($spdrvscn_status)) { - notify($ERRORS{'WARNING'}, 0, "failed to execute spdrvscn.exe, exit status: $spdrvscn_status, output:\...@{$spdrvscn_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to execute spdrvscn.exe"); - return 0; - } - - # Query the DevicePath registry value in order to save it in the log for troubleshooting - my $reg_query_command = $self->get_system32_path() . '/reg.exe QUERY "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion" /v DevicePath'; - my ($reg_query_status, $reg_query_output) = run_ssh_command($computer_node_name, $management_node_keys, $reg_query_command, '', '', 1); - if (defined($reg_query_status) && $reg_query_status == 0) { - notify($ERRORS{'DEBUG'}, 0, "queried DevicePath registry key:\n" . join("\n", @{$reg_query_output})); - } - elsif (defined($reg_query_status)) { - notify($ERRORS{'WARNING'}, 0, "failed to query DevicePath registry key, exit status: $reg_query_status, output:\...@{$reg_query_output}"); - return 0; - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to query DevicePath registry key"); - return 0; - } - - # Format the string for the log output - my ($device_path_string) = grep(/devicepath\s+(reg_.*sz)/i, @{$reg_query_output}); - $device_path_string =~ s/.*(devicepath\s+reg_.*sz)\s*/$1\n/i; - $device_path_string =~ s/;/\n/g; - notify($ERRORS{'OK'}, 0, "device path string: $device_path_string"); - - return 1; -} ## end sub prepare_drivers - -#///////////////////////////////////////////////////////////////////////////// - =head2 clean_hard_drive Parameters : @@ -7743,7 +7518,7 @@ # Get the PROCESSOR_IDENTIFIER environment variable to determine if OS is 32 or 64-bit my ($set_exit_status, $set_output) = run_ssh_command($computer_node_name, $management_node_keys, 'set', '', '', 1); if (defined($set_exit_status) && $set_exit_status == 0) { - notify($ERRORS{'OK'}, 0, "executed set command to determine architecture on $computer_node_name"); + notify($ERRORS{'DEBUG'}, 0, "executed set command to determine architecture on $computer_node_name"); } elsif (defined($set_exit_status)) { notify($ERRORS{'WARNING'}, 0, "failed to execute set command to determine architecture on $computer_node_name, exit status: $set_exit_status, output:\...@{$set_output}"); @@ -7820,6 +7595,14 @@ Description : Retrieves the Windows product name from the registry. This is stored at: HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProductName + + The product name stored in the registry is used in the + winProductKey table to match a product key up with a product. It + must match exactly. Known strings for some versions of Windows: + "Microsoft Windows XP" + "Microsoft Windows Server 2003" + "Windows Server (R) 2008 Datacenter" + "Windows Vista (TM) Enterprise" =cut @@ -8150,14 +7933,9 @@ return; } - # Get the product name, could be: - # "Microsoft Windows XP" - # "Microsoft Windows Server 2003" + # Get the product name from the registry on the computer my $product_name = shift || $self->get_product_name(); - if ($product_name) { - notify($ERRORS{'DEBUG'}, 0, "product name: $product_name"); - } - else { + if (!$product_name) { notify($ERRORS{'WARNING'}, 0, "product name argument was not passed and could not be determined from computer"); return; } Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm?rev=899730&r1=899729&r2=899730&view=diff ============================================================================== --- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm (original) +++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm Fri Jan 15 17:41:26 2010 @@ -249,10 +249,11 @@ Parameters : None Returns : If successful: true If failed: false - Description : Runs cscript.exe slmgr.vbs -skms to set the KMS server address - stored on the computer. - Runs cscript.exe slmgr.vbs -ato to activate licensing on the - computer. + Description : Activates Microsoft Windows. A first attempt is made using a + MAK key if one has been configured in the winProductKey table + for the version of Windows installed on the computer. If unable + to activate using a MAK key, activation is attempting using a + KMS server configured in the winKMS table. =cut @@ -263,136 +264,180 @@ return; } - my $management_node_keys = $self->data->get_management_node_keys(); - my $computer_node_name = $self->data->get_computer_node_name(); - my $product_name = $self->get_product_name(); + # Check if Windows has already been activated + my $license_status = $self->get_license_status(); + if ($license_status && $license_status =~ /licensed/i) { + notify($ERRORS{'OK'}, 0, "Windows has already been activated"); + return 1; + } + + ## Attempt to activate using MAK product key + #return 1 if $self->activate_mak(); - # Get the image affiliation name - my $image_affiliation_name = $self->data->get_image_affiliation_name(); - if ($image_affiliation_name) { - notify($ERRORS{'DEBUG'}, 0, "image affiliation name: $image_affiliation_name"); + # Attempt to activate using KMS server + return 1 if $self->activate_kms(); + + notify($ERRORS{'WARNING'}, 0, "failed to activate Windows using MAK or KMS methods"); + return; +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 activate_mak + + Parameters : None + Returns : If successful: true + If failed: false + Description : Attempts to activate Windows using a MAK key stored in the + winProductKey table. + +=cut + +sub activate_mak { + 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; + } + + # Attempt to get the product key stored in the winProductKey table + # This will return the correct key for the affiliation and version of Windows installed on the computer + my $product_key = $self->get_product_key(); + if ($product_key) { + notify($ERRORS{'DEBUG'}, 0, "retrieved MAK product key from the winProductKey table: $product_key"); } else { - notify($ERRORS{'WARNING'}, 0, "image affiliation name could not be retrieved, using default licensing configuration"); - $image_affiliation_name = 'default'; + notify($ERRORS{'OK'}, 0, "MAK product key could not be retrieved from the winProductKey table"); + return; } - # Get the Windows activation data from the windows-activation variable - my $activation_data = $self->data->get_variable('windows-activation'); - if ($activation_data) { - notify($ERRORS{'DEBUG'}, 0, "activation data:\n" . format_data($activation_data)); + # Attempt to install the MAK product key + if ($self->run_slmgr_ipk($product_key)) { + notify($ERRORS{'DEBUG'}, 0, "installed MAK product key: $product_key"); } else { - notify($ERRORS{'WARNING'}, 0, "activation data could not be retrieved"); + notify($ERRORS{'WARNING'}, 0, "failed to install MAK product key: $product_key"); return; } - # Get the activation data specific to the image affiliation - my $affiliation_config = $activation_data->{$image_affiliation_name}; - if ($affiliation_config) { - notify($ERRORS{'DEBUG'}, 0, "$image_affiliation_name affiliation activation configuration:\n" . format_data($affiliation_config)); + # Attempt to activate the license + if ($self->run_slmgr_ato()) { + notify($ERRORS{'OK'}, 0, "activated Windows using MAK product key: $product_key"); + return 1; } else { - notify($ERRORS{'WARNING'}, 0, "activation configuration does not exist for affiliation: $image_affiliation_name, attempting to retrieve default configuration"); - - $affiliation_config = $activation_data->{'default'}; - if ($affiliation_config) { - notify($ERRORS{'DEBUG'}, 0, "default activation configuration:\n" . format_data($affiliation_config)); - } - else { - notify($ERRORS{'WARNING'}, 0, "default activation configuration does not exist"); - return; - } + notify($ERRORS{'WARNING'}, 0, "failed to activate Windows using MAK product key: $product_key"); + return; + } +} + +#///////////////////////////////////////////////////////////////////////////// + +=head2 activate_kms + + Parameters : None + Returns : If successful: true + If failed: false + Description : Attempts to activate Windows using a KMS server configured in + the winKMS table. + +=cut + +sub activate_kms { + 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; + } + + # Get the KMS server info from the winKMS table + my $kms_server_info = $self->get_kms_servers(); + if (!$kms_server_info) { + notify($ERRORS{'WARNING'}, 0, "KMS server information could not be retrieved"); + return; + } + + # Attempt to get the KMS client product key + # This is a publically available key that needs to be installed in order to activate via KMS + my $product_key = $self->get_kms_client_product_key(); + if ($product_key) { + notify($ERRORS{'DEBUG'}, 0, "retrieved KMS client product key: $product_key"); + } + else { + notify($ERRORS{'WARNING'}, 0, "KMS client product key could not be retrieved"); + return; } + # Attempt to install the KMS client product key + if ($self->run_slmgr_ipk($product_key)) { + notify($ERRORS{'DEBUG'}, 0, "installed KMS client product key: $product_key"); + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to install KMS client product key: $product_key"); + return; + } - # Loop through the activation methods for the affiliation - for my $activation_config (@$affiliation_config) { - my $activation_method = $activation_config->{method}; + # Loop through the KMS servers, set KMS server, attempt to activate + for my $kms_server (@{$kms_server_info}) { + my $kms_address = $kms_server->{address}; + my $kms_port = $kms_server->{port}; + notify($ERRORS{'DEBUG'}, 0, "attempting to set KMS server: $kms_address:$kms_port"); - if ($activation_method =~ /kms/i) { - my $kms_address = $activation_config->{address}; - my $kms_port = $activation_config->{port} || 1688; - notify($ERRORS{'DEBUG'}, 0, "attempting to set kms server: $kms_address, port: $kms_port"); - - # Attempt to install the KMS client product key - # This must be done or else the slmgr.vbs -skms option won't be available - if ($self->install_kms_client_product_key()) { - notify($ERRORS{'DEBUG'}, 0, "installed the KMS client product key"); - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to install the KMS client product key"); - next; - } + # Run slmgr.vbs -skms to configure the computer to use the KMS server + if ($self->run_slmgr_skms($kms_address, $kms_port)) { + notify($ERRORS{'OK'}, 0, "set KMS server: $kms_address:$kms_port"); - # Run slmgr.vbs -skms to configure the computer to use the KMS server - if ($self->set_kms($kms_address, $kms_port)) { - notify($ERRORS{'DEBUG'}, 0, "set KMS address"); + # Attempt to activate the license + if ($self->run_slmgr_ato()) { + notify($ERRORS{'OK'}, 0, "activated Windows using KMS server: $kms_address:$kms_port"); + return 1; } else { - notify($ERRORS{'WARNING'}, 0, "failed to set KMS address"); + notify($ERRORS{'WARNING'}, 0, "failed to activate Windows using KMS server: $kms_address:$kms_port"); next; } } - elsif ($activation_method =~ /mak/i) { - my $mak_key = $activation_config->{key}; - my $mak_product = $activation_config->{product}; - - if ($mak_product eq $product_name) { - notify($ERRORS{'DEBUG'}, 0, "attempting to set install MAK key for $mak_product: $mak_key"); - } - else { - notify($ERRORS{'DEBUG'}, 0, "MAK key product ($mak_product) does not match installed version of Windows ($product_name)"); - next; - } - - # Attempt to install the MAK product key - if ($self->install_product_key($mak_key)) { - notify($ERRORS{'DEBUG'}, 0, "installed MAK product key: $mak_key"); - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to install MAK product key: $mak_key"); - next; - } - } - else { - notify($ERRORS{'WARNING'}, 0, "unsupported activation method: $activation_method"); - next; - } - - # Attempt to activate the license - if ($self->activate_license()) { - notify($ERRORS{'OK'}, 0, "activated license"); - return 1; - } else { - notify($ERRORS{'WARNING'}, 0, "failed to activate license"); + notify($ERRORS{'WARNING'}, 0, "failed to set KMS server: $kms_address:$kms_port"); next; } } - notify($ERRORS{'WARNING'}, 0, "failed to activate license on $computer_node_name using any configured method"); + notify($ERRORS{'WARNING'}, 0, "failed to activate Windows using any KMS servers configured in the winKMS table"); return; } #///////////////////////////////////////////////////////////////////////////// -=head2 install_kms_client_product_key +=head2 get_kms_client_product_key - Parameters : None - Returns : If successful: true + Parameters : $product_name (optional + Returns : If successful: string If failed: false - Description : + Description : Returns a KMS client product key based on the version of Windows + either specified as an argument or installed on the computer. A + KMS client product key is a publically shared product key which + must be installed before activating using a KMS server. =cut -sub install_kms_client_product_key { +sub get_kms_client_product_key { 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; } + # Get the product name + my $product_name = shift || $self->get_product_name(); + if (!$product_name) { + notify($ERRORS{'WARNING'}, 0, "product name was not passed as an argument and could not be retrieved from computer"); + return; + } + + # Remove (TM) or (R) from the product name + $product_name =~ s/ \([tmr]*\)//ig; + # Create a hash of KMS setup product keys # These are publically available from Microsoft's Volume Activation 2.0 Deployment Guide my %kms_product_keys = ( @@ -410,42 +455,28 @@ 'Windows Web Server 2008' => 'WYR28-R7TFJ-3X2YQ-YCY4H-M249D', ); - # Get the KMS setup product key from the hash - my $product_name = $self->get_product_name(); - - # Remove (TM) or (R) from the product name - $product_name =~ s/ \([tmr]*\)//ig; - + # Get the matching product key from the hash for the product name my $product_key = $kms_product_keys{$product_name}; if (!$product_key) { - notify($ERRORS{'WARNING'}, 0, "failed to retrieve KMS setup key for Windows product: $product_name"); - return; - } - notify($ERRORS{'DEBUG'}, 0, "KMS client setup key for $product_name: $product_key"); - - # Install the KMS client product key - if ($self->install_product_key($product_key)) { - notify($ERRORS{'OK'}, 0, "installed KMS client product key"); - return 1; - } - else { - notify($ERRORS{'WARNING'}, 0, "failed to install KMS client product key"); + notify($ERRORS{'WARNING'}, 0, "unsupported product name: $product_name, KMS client product key is not known"); return; } + notify($ERRORS{'DEBUG'}, 0, "returning KMS client setup key for $product_name: $product_key"); + return $product_key; } #///////////////////////////////////////////////////////////////////////////// -=head2 install_product_key +=head2 run_slmgr_ipk Parameters : None Returns : If successful: true If failed: false - Description : + Description : Runs slmgr.vbs -ipk to install a product key. =cut -sub install_product_key { +sub run_slmgr_ipk { 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"); @@ -454,6 +485,7 @@ my $management_node_keys = $self->data->get_management_node_keys(); my $computer_node_name = $self->data->get_computer_node_name(); + my $system32_path = $self->get_system32_path(); # Get the arguments my $product_key = shift; @@ -463,7 +495,7 @@ } # Run cscript.exe slmgr.vbs -ipk to install the product key - my $ipk_command = 'cscript.exe //NoLogo $SYSTEMROOT/System32/slmgr.vbs -ipk ' . $product_key; + my $ipk_command = "$system32_path/cmd.exe /c cscript.exe //NoLogo C:/Windows/System32/slmgr.vbs -ipk $product_key"; my ($ipk_exit_status, $ipk_output) = run_ssh_command($computer_node_name, $management_node_keys, $ipk_command); if (defined($ipk_exit_status) && $ipk_exit_status == 0 && grep(/successfully/i, @$ipk_output)) { notify($ERRORS{'OK'}, 0, "installed product key: $product_key"); @@ -482,16 +514,16 @@ #///////////////////////////////////////////////////////////////////////////// -=head2 set_kms +=head2 run_slmgr_skms Parameters : None Returns : If successful: true If failed: false - Description : + Description : Runs slmgr.vbs -skms to set the KMS server on a Windows client. =cut -sub set_kms { +sub run_slmgr_skms { 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"); @@ -500,6 +532,7 @@ my $management_node_keys = $self->data->get_management_node_keys(); my $computer_node_name = $self->data->get_computer_node_name(); + my $system32_path = $self->get_system32_path(); # Get the KMS address argument my $kms_address = shift; @@ -512,7 +545,8 @@ my $kms_port = shift || 1688; # Run slmgr.vbs -skms to configure the computer to use the KMS server - my $skms_command = 'cscript.exe //NoLogo $SYSTEMROOT/System32/slmgr.vbs -skms ' . "$kms_address:$kms_port"; + # slmgr.vbs must be run in a command shell using the correct System32 path or the task it's supposed to do won't really take effect + my $skms_command = "$system32_path/cmd.exe /c cscript.exe //NoLogo C:/Windows/System32/slmgr.vbs -skms $kms_address:$kms_port"; my ($skms_exit_status, $skms_output) = run_ssh_command($computer_node_name, $management_node_keys, $skms_command); if (defined($skms_exit_status) && $skms_exit_status == 0 && grep(/successfully/i, @$skms_output)) { notify($ERRORS{'OK'}, 0, "set kms server to $kms_address:$kms_port"); @@ -531,16 +565,16 @@ #///////////////////////////////////////////////////////////////////////////// -=head2 activate_license +=head2 run_slmgr_ato Parameters : None Returns : If successful: true If failed: false - Description : + Description : Runs slmgr.vbs -ato to activate Windows. =cut -sub activate_license { +sub run_slmgr_ato { 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"); @@ -549,9 +583,10 @@ my $management_node_keys = $self->data->get_management_node_keys(); my $computer_node_name = $self->data->get_computer_node_name(); + my $system32_path = $self->get_system32_path(); # Run cscript.exe slmgr.vbs -ato to install the product key - my $ato_command = 'cscript.exe //NoLogo $SYSTEMROOT/System32/slmgr.vbs -ato'; + my $ato_command = "$system32_path/cmd.exe /c cscript.exe //NoLogo C:/Windows/System32/slmgr.vbs -ato"; my ($ato_exit_status, $ato_output) = run_ssh_command($computer_node_name, $management_node_keys, $ato_command); if (defined($ato_exit_status) && $ato_exit_status == 0 && grep(/successfully/i, @$ato_output)) { notify($ERRORS{'OK'}, 0, "activated license"); @@ -570,6 +605,50 @@ #///////////////////////////////////////////////////////////////////////////// +=head2 get_license_status + + Parameters : None + Returns : If successful: string + If failed: false + Description : Runs slmgr.vbs -dlv to determine the licensing status. The value + of the "License Status" line is returned. + +=cut + +sub get_license_status { + 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 $management_node_keys = $self->data->get_management_node_keys(); + my $computer_node_name = $self->data->get_computer_node_name(); + my $system32_path = $self->get_system32_path(); + + # Run cscript.exe slmgr.vbs -dlv to get the activation status + my $dlv_command = "$system32_path/cmd.exe /c cscript.exe //NoLogo C:/Windows/System32/slmgr.vbs -dlv"; + my ($dlv_exit_status, $dlv_output) = run_ssh_command($computer_node_name, $management_node_keys, $dlv_command, '', '', 0); + if ($dlv_output && grep(/License Status/i, @$dlv_output)) { + #notify($ERRORS{'DEBUG'}, 0, "retrieved license information"); + } + elsif (defined($dlv_exit_status)) { + notify($ERRORS{'WARNING'}, 0, "failed to retrieve activation status, exit status: $dlv_exit_status, output:\...@{$dlv_output}"); + return; + } + else { + notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to retrieve activation status"); + return; + } + + my ($license_status_line) = grep(/License Status/i, @$dlv_output); + my ($license_status) = $license_status_line =~ /: (\w+)/; + notify($ERRORS{'DEBUG'}, 0, "retrieved license status: $license_status"); + return $license_status; +} + +#///////////////////////////////////////////////////////////////////////////// + =head2 deactivate Parameters : None @@ -590,6 +669,7 @@ my $management_node_keys = $self->data->get_management_node_keys(); my $computer_node_name = $self->data->get_computer_node_name(); + my $system32_path = $self->get_system32_path(); my $registry_string .= <<'EOF'; Windows Registry Editor Version 5.00 @@ -610,7 +690,7 @@ } # Run slmgr.vbs -rearm - my $rearm_command = 'cscript.exe //NoLogo $SYSTEMROOT/System32/slmgr.vbs -rearm'; + my $rearm_command = "$system32_path/cmd.exe /c cscript.exe //NoLogo C:/Windows/System32/slmgr.vbs -rearm"; my ($rearm_exit_status, $rearm_output) = run_ssh_command($computer_node_name, $management_node_keys, $rearm_command); if (defined($rearm_exit_status) && $rearm_exit_status == 0 && grep(/successfully/i, @$rearm_output)) { notify($ERRORS{'OK'}, 0, "rearmed licensing"); @@ -1157,97 +1237,6 @@ #///////////////////////////////////////////////////////////////////////////// -=head2 add_kms_server - - Parameters : $affiliation_name, $kms_address, $kms_port - Returns : If successful: true - If failed: false - Description : Adds a kms server to the windows-activation variable for the - specified affiliation name. - If a KMS server with the same address is already saved in the - windows-activation variable, it is deleted and the KMS server - specified in the subroutine arguments is added to the end of the - configuration list. - -=cut - -sub add_kms_server { - my ($affiliation_name, $kms_address, $kms_port) = @_; - - # Check the arguments - unless ($affiliation_name && $kms_address) { - notify($ERRORS{'WARNING'}, 0, "affiliation name and kms server address must be specified as arguments"); - return; - } - - # Set the default KMS port to 1688 if the argument was not specified - $kms_port = 1688 unless $kms_port; - - # Get a new DataStructure object - my $data = VCL::DataStructure->new(); - if ($data) { - notify($ERRORS{'DEBUG'}, 0, "created new DataStructure object"); - } - else { - notify($ERRORS{'WARNING'}, 0, "unable to create new DataStructure object"); - return; - } - - # Get the Windows activation data from the windows-activation variable - my $activation_data = $data->get_variable('windows-activation'); - if ($activation_data) { - notify($ERRORS{'DEBUG'}, 0, "existing activation data:\n" . format_data($activation_data)); - } - else { - notify($ERRORS{'WARNING'}, 0, "activation data could not be retrieved, hopefully this is the first entry being added"); - } - - # Loop through the existing configurations for the affiliation - for (my $i=0; $i<(@{$activation_data->{$affiliation_name}}); $i++) { - my $affiliation_configuration = @{$activation_data->{$affiliation_name}}[$i]; - - # Remove the configuration if it's not defined - if (!defined $affiliation_configuration) { - splice @{$activation_data->{$affiliation_name}}, $i--, 1; - next; - } - - # Check if an identical existing address already exists, if so, delete it - my $existing_affiliation_kms_address = $affiliation_configuration->{address}; - if ($existing_affiliation_kms_address eq $kms_address) { - splice @{$activation_data->{$affiliation_name}}, $i--, 1; - notify($ERRORS{'DEBUG'}, 0, "deleted identical existing address for $affiliation_name: $existing_affiliation_kms_address"); - } - else { - notify($ERRORS{'DEBUG'}, 0, "found existing address for $affiliation_name: $existing_affiliation_kms_address"); - } - } - - # Add the KMS configuration to the activation data - push @{$activation_data->{$affiliation_name}}, { - method => 'kms', - address => $kms_address, - port => $kms_port, - }; - - # Set the variable with the updated data - $data->set_variable('windows-activation', $activation_data); - - # Retrieve the updated configuration data - $activation_data = $data->get_variable('windows-activation'); - if ($activation_data) { - notify($ERRORS{'DEBUG'}, 0, "updated activation data:\n" . format_data($activation_data)); - } - else { - notify($ERRORS{'WARNING'}, 0, "updated activation data could not be retrieved"); - return; - } - - return 1; -} - -#///////////////////////////////////////////////////////////////////////////// - =head2 run_sysprep Parameters : None