You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2017/03/07 20:28:49 UTC
svn commit: r1785881 - in /vcl/trunk/managementnode/lib/VCL:
DataStructure.pm Module/OS/Windows.pm Module/Provisioning.pm utils.pm
Author: arkurth
Date: Tue Mar 7 20:28:49 2017
New Revision: 1785881
URL: http://svn.apache.org/viewvc?rev=1785881&view=rev
Log:
VCL-867
Made another attempt to update DataStructure.pm::_automethod to not display warnings when get_image_domain* is called and the data is not defined. The previous change wasn't catching all calls.
Added utils.pm::get_active_directory_domain_credentials. This is needed to determine the domain username and password in order to remove a computer from a domain after the preloaded image was reconfigured to not join a domain. The normal $self->data->get_image_domain* information is not available in this case.
Updated Provisioning.pm to check if OS module implements a node_status_os_check subroutine and call it if it does.
Added Windows.pm::ad_check. It contains all logic to determine if an image is configured for AD, if a computer is already joined to a domain, and calls the necessary subroutines to either unjoin the domain, join the domain, or both if the computer is joined to a different domain or located in the wrong OU. Removed similar logic from ad_join.
Replaced call from ad_join to ad_check in Windows.pm::post_load.
Added Windows.pm::node_status_os_check which simply returns the value of ad_check. Naming reason: ad_check is called elsewhere and Windows-specific. node_status_os_check is more general and may perform additional functions in the future.
Reworked Windows.pm::ad_unjoin to call wmic.exe rather than building Powershell script. Need to test this and determine which is better.
Updated Windows.pm::ad_search, ad_delete_computer, and ad_search_computer to accept domain DNS name argument. This is used when a computer is joined to a domain but shouldn't be (image was reconfigured after computer was loaded).
Modified:
vcl/trunk/managementnode/lib/VCL/DataStructure.pm
vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm
vcl/trunk/managementnode/lib/VCL/utils.pm
Modified: vcl/trunk/managementnode/lib/VCL/DataStructure.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/DataStructure.pm?rev=1785881&r1=1785880&r2=1785881&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/DataStructure.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/DataStructure.pm Tue Mar 7 20:28:49 2017
@@ -900,7 +900,7 @@ sub _automethod : Automethod {
$return_value = eval $hash_path;
}
elsif (!$key_defined) {
- if ($show_warnings && $hash_path !~ /(serverrequest|image_domain)/) {
+ if ($show_warnings && $hash_path !~ /(serverrequest|domain)/) {
notify($ERRORS{'WARNING'}, 0, "corresponding data has not been initialized for $method_name: $hash_path", $self->request_data);
}
return sub { };
Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm?rev=1785881&r1=1785880&r2=1785881&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Tue Mar 7 20:28:49 2017
@@ -947,7 +947,7 @@ sub post_load {
=cut
if ($imagedomain_domaindnsname) {
- if (!$self->ad_join()) {
+ if (!$self->ad_check()) {
notify($ERRORS{'WARNING'}, 0, "failed to join Active Directory domain");
return 0;
}
@@ -13502,6 +13502,149 @@ sub ad_join_prepare {
#/////////////////////////////////////////////////////////////////////////////
+=head2 node_status_os_check
+
+ Parameters : none
+ Returns : boolean
+ Description : Called from provisioning module's node_status subroutine. This
+ checks if the loaded computer's Active Directory configuration is
+ correct if image is configured to join an AD domain.
+
+=cut
+
+sub node_status_os_check {
+ 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;
+ }
+
+ # Check if computer AD configuration is correct if image is configured for AD
+ # Returning false indicates AD configuration could not be corrected and calling subroutine should reload the computer
+ return $self->ad_check();
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 ad_check
+
+ Parameters : none
+ Returns : boolean
+ Description : Checks if the computer is joined to an Active Directory domain
+ and located in the correct OU.
+
+=cut
+
+sub ad_check {
+ 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_name = $self->data->get_computer_short_name();
+ my $image_domain_dns_name = $self->data->get_image_domain_dns_name();
+
+ # Check if the computer is joined to any AD domain
+ my $computer_current_domain_name = $self->ad_get_current_domain();
+
+ if (!$image_domain_dns_name) {
+ # Computer should NOT be joined to an AD domain
+ if (!$computer_current_domain_name) {
+ notify($ERRORS{'OK'}, 0, "image is not configured for Active Directory and $computer_name is not joined to a domain, returning 1");
+ return 1;
+ }
+
+ # Computer incorrectly joined to an AD domain, attempt to unjoin the domain
+ notify($ERRORS{'OK'}, 0, "$computer_name is joined to the $computer_current_domain_name domain but the image is not configured for Active Directory, attempting to unjoin the domain");
+ if ($self->ad_unjoin()) {
+ notify($ERRORS{'OK'}, 0, "image is not configured for Active Directory, unjoined $computer_name from $computer_current_domain_name domain, returning 1");
+ return 1;
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "image is not configured for Active Directory, failed to unjoin $computer_name from $computer_current_domain_name domain, returning undefined");
+ return;
+ }
+ }
+
+ # Computer should be joined to AD domain
+ if (!$computer_current_domain_name) {
+ # Computer is not joined to an AD domain, return the result of attempting to join
+ notify($ERRORS{'OK'}, 0, "image is configured to join the $image_domain_dns_name domain, $computer_name is not joined to a domain, attempting to join the domain");
+ return $self->ad_join();
+ }
+
+
+ # Computer is joined to an AD domain, check if it's in the correct domain
+ if ($computer_current_domain_name ne $image_domain_dns_name) {
+ # Computer is not joined to the correct domain, attempt to unjoin and then rejoin
+ notify($ERRORS{'DEBUG'}, 0, "$computer_name is joined to the $computer_current_domain_name domain, image is configured to join the $image_domain_dns_name, attempting to unjoin then join the correct domain");
+ if (!$self->ad_unjoin()) {
+ notify($ERRORS{'WARNING'}, 0, "image is configured to join the $image_domain_dns_name, failed to unjoin $computer_name from the $computer_current_domain_name domain, returning undefined");
+ return;
+ }
+ elsif (!$self->ad_join()) {
+ notify($ERRORS{'WARNING'}, 0, "image is configured to join the $image_domain_dns_name, unjoined $computer_name from the incorrect $computer_current_domain_name domain but failed to rejoin the correct $image_domain_dns_name domain, returning undefined");
+ return;
+ }
+ else {
+ notify($ERRORS{'OK'}, 0, "unjoined $computer_name from the incorrect $computer_current_domain_name and rejoined to the correct domain: $image_domain_dns_name, returning 1");
+ return 1;
+ }
+ }
+
+ # Computer is joined to the correct AD domain, make sure computer object is in the correct OU
+
+ # Determine the OU configured for the image
+ my $image_ou_dn = $self->get_ad_computer_ou_dn();
+ if (!$image_ou_dn) {
+ notify($ERRORS{'WARNING'}, 0, "image is configured to join the $image_domain_dns_name domain but proper computer OU DN could not be determined, returning undefined");
+ return;
+ }
+
+ # Get the computer's current OU
+ my $computer_current_dn = $self->ad_search_computer();
+ if (!$computer_current_dn) {
+ notify($ERRORS{'WARNING'}, 0, "$computer_name is joined to the correct $computer_current_domain_name domain but current OU could not be determined, assuming computer object is in the correct OU, returning 1");
+ return 1;
+ }
+
+ # Extract the OU DN from the DN of the computer object
+ my ($computer_current_ou_dn) = $computer_current_dn =~ /^[^,]+,(OU=.+)$/;
+ if (!$computer_current_ou_dn) {
+ notify($ERRORS{'WARNING'}, 0, "$computer_name is joined to the correct $computer_current_domain_name domain but current OU DN could not be parsed from current computer object DN: '$computer_current_dn', assuming computer object is in the correct OU, returning 1");
+ return 1;
+ }
+
+ if ($computer_current_ou_dn =~ /^$image_ou_dn$/i) {
+ notify($ERRORS{'OK'}, 0, "$computer_name is joined to the correct domain and in the correct OU, returning 1:\n" .
+ "current domain: $computer_current_domain_name\n" .
+ "computer object OU: $computer_current_ou_dn"
+ );
+ return 1;
+ }
+
+ # Computer is in the wrong OU
+ notify($ERRORS{'OK'}, 0, "$computer_name is joined to the correct $computer_current_domain_name domain but located in the wrong OU, attempting to unjoin then rejoin the domain in the correct OU:\n" .
+ "OU configured for image : $image_ou_dn\n" .
+ "current computer object OU : $computer_current_ou_dn"
+ );
+ if (!$self->ad_unjoin()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to unjoin $computer_name from the $computer_current_domain_name domain in order to rejoin in the correct OU, returning undefined");
+ return;
+ }
+ elsif (!$self->ad_join()) {
+ notify($ERRORS{'WARNING'}, 0, "failed to rejoin $computer_name to the correct OU in the $image_domain_dns_name domain: '$image_ou_dn', returning undefined");
+ return;
+ }
+ else {
+ notify($ERRORS{'OK'}, 0, "rejoined $computer_name to the correct OU in the $image_domain_dns_name domain: '$image_ou_dn', returning 1");
+ return 1;
+ }
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
=head2 ad_join
Parameters : none
@@ -13545,43 +13688,6 @@ sub ad_join {
return;
}
- # Make sure the computer is not already a member of a domain
- # TODO: add logic to check if computer belongs to the correct domain in the correct OU
- # If not, remove and rejoin
- my $current_domain_name = $self->ad_get_current_domain();
- if ($current_domain_name) {
- if ($current_domain_name ne $domain_dns_name) {
- notify($ERRORS{'WARNING'}, 0, "unable to add $computer_name to $domain_dns_name domain, it is already a member of a different domain: $current_domain_name");
- return;
- }
-
- # Search for the computer object in the domain
- my $current_computer_dn = $self->ad_search_computer();
- if (!$current_computer_dn) {
- notify($ERRORS{'WARNING'}, 0, "unable to add $computer_name to $domain_dns_name domain, it appears to already a member of the domain but the current DN could not be determined");
- return;
- }
-
- # Extract the OU DN from the computer DN
- my ($current_computer_ou_dn) = $current_computer_dn =~ /^[^,]+,(OU=.+)$/;
- if (!$current_computer_ou_dn) {
- notify($ERRORS{'WARNING'}, 0, "unable to add $computer_name to $domain_dns_name domain, failed to parse OU DN from current computer DN: $current_computer_dn");
- return;
- }
-
- if ($current_computer_ou_dn =~ /^$computer_ou_dn$/i) {
- notify($ERRORS{'OK'}, 0, "$computer_name is already joined to $domain_dns_name domain and in the correct OU: $current_computer_ou_dn");
- return 1;
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "$computer_name is already joined to $domain_dns_name domain but in the a different OU:\n" .
- "correct OU: $computer_ou_dn\n" .
- "current OU: $current_computer_ou_dn"
- );
- $self->ad_unjoin() || return;
- }
- }
-
# Figure out/fix the computer OU and assemble optional section to add to PowerShell command
my $domain_computer_command_section = '';
if ($computer_ou_dn) {
@@ -13594,7 +13700,7 @@ sub ad_join {
"domain password: $domain_password\n" .
"domain computer OU DN: " . ($computer_ou_dn ? $computer_ou_dn : '<not configured>')
);
-
+
# Perform preparation tasks
$self->ad_join_prepare() || return;
@@ -13719,69 +13825,124 @@ sub ad_unjoin {
my $computer_name = $self->data->get_computer_short_name();
my $image_name = $self->data->get_image_name();
+ my $system32_path = $self->get_system32_path() || return;
- my $domain_dns_name = $self->data->get_image_domain_dns_name();
- my $domain_username = $self->data->get_image_domain_username();
- my $domain_password = $self->data->get_image_domain_password();
+ my $computer_current_domain = $self->ad_get_current_domain();
+ if (!$computer_current_domain) {
+ notify($ERRORS{'DEBUG'}, 0, "$computer_name does not need to be removed from AD because it is not currently joined to a domain");
+ return 1;
+ }
- if (!defined($domain_dns_name)) {
- notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, image $image_name is not assigned to a domain");
+ # Expected output:
+ # Executing (\\<COMPUTERNAME>\ROOT\CIMV2:Win32_ComputerSystem.Name="<COMPUTERNAME>")->UnJoinDomainOrWorkgroup()
+ # Method execution successful.s
+ # Out Parameters:
+ # instance of __PARAMETERS
+ # {
+ # ReturnValue = 0;
+ # };
+
+ # Assemble the unjoin command
+ my $unjoin_command = "echo | cmd.exe /c \"$system32_path/Wbem/wmic.exe /INTERACTIVE:OFF COMPUTERSYSTEM WHERE Name=\\\"%COMPUTERNAME%\\\" Call UnJoinDomainOrWorkgroup FUnjoinOptions=0\"";
+ notify($ERRORS{'DEBUG'}, 0, "attempting to unjoin $computer_name from $computer_current_domain Active Directory domain");
+ my ($unjoin_exit_status, $unjoin_output) = $self->execute($unjoin_command);
+ if (!defined($unjoin_output)) {
+ notify($ERRORS{'DEBUG'}, 0, "failed to execute command to unjoin the $computer_current_domain Active Directory domain");
return;
}
- elsif (!defined($domain_username)) {
- notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, user name is not configured for $domain_dns_name domain");
+ elsif (grep(/ERROR/i, @$unjoin_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to unjoin $computer_current_domain Active Directory domain, output:\n" . join("\n", @$unjoin_output));
return;
}
- elsif (!defined($domain_password)) {
- notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, password is not configured for $domain_dns_name domain");
+ elsif (grep(/ReturnValue\s+=\s+[1-9]/i, @$unjoin_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to unjoin $computer_current_domain Active Directory domain, return value is not 0, output:\n" . join("\n", @$unjoin_output));
return;
}
-
- if (!$self->ad_get_current_domain()) {
- notify($ERRORS{'DEBUG'}, 0, "$computer_name does not need to be removed from AD because it is not currently joined to a domain");
- return 1;
+ elsif (grep(/Method execution successful/i, @$unjoin_output)) {
+ notify($ERRORS{'OK'}, 0, "unjoined $computer_current_domain Active Directory domain, output:\n" . join("\n", @$unjoin_output));
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unexpected output unjoining $computer_current_domain Active Directory domain, output:\n" . join("\n", @$unjoin_output));
}
- notify($ERRORS{'DEBUG'}, 0, "attempting to unjoin $computer_name from AD");
- # Assemble the PowerShell script
- my $ad_powershell_script = <<EOF;
-\$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size(5000, 500)
-\$ps_credential = New-Object System.Management.Automation.PsCredential("$domain_dns_name\\$domain_username", (ConvertTo-SecureString "$domain_password" -AsPlainText -Force))
-try {
- Add-Computer -WorkgroupName VCL -Credential \$ps_credential -ErrorAction Stop
-}
-catch {
- Write-Host "ERROR: failed to add computer to workgroup, error: \$(\$_.Exception.Message)"
- exit 1
-}
-EOF
-
- my ($exit_status, $output) = $self->run_powershell_as_script($ad_powershell_script, 1, 1);
- if (!defined($output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to execute PowerShell script to remove $computer_name from Active Directory domain");
+ # Assemble the join workgroup command
+ my $join_workgroup_command = "echo | cmd.exe /c \"$system32_path/Wbem/wmic.exe /INTERACTIVE:OFF COMPUTERSYSTEM WHERE Name=\\\"%COMPUTERNAME%\\\" Call JoinDomainOrWorkgroup name=VCL\"";
+ my ($join_workgroup_exit_status, $join_workgroup_output) = $self->execute($join_workgroup_command);
+ if (!defined($join_workgroup_output)) {
+ notify($ERRORS{'DEBUG'}, 0, "failed to execute command to join workgroup");
return;
}
- elsif (grep(/ERROR/, @$output)) {
- # Computer object was already or deleted or can't be found for some reason:
- # This command cannot be executed on target computer('') due to following error: No mapping between account names and security IDs was done.
- if (grep(/No mapping between account names/, @$output)) {
- notify($ERRORS{'WARNING'}, 0, "failed to remove $computer_name from Active Directory domain, the computer object may have been deleted from the domain, output:\n" . join("\n", @$output));
- }
- else {
- notify($ERRORS{'WARNING'}, 0, "failed to remove $computer_name from Active Directory domain, output:\n" . join("\n", @$output));
- }
- return 0;
+ elsif (grep(/ERROR/i, @$join_workgroup_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to join workgroup, output:\n" . join("\n", @$join_workgroup_output));
}
-
- notify($ERRORS{'OK'}, 0, "removed $computer_name from Active Directory domain, output:\n" . join("\n", @$output));
-
+ elsif (grep(/ReturnValue\s+=\s+[1-9]/i, @$join_workgroup_output)) {
+ notify($ERRORS{'WARNING'}, 0, "failed to join workgroup, return value is not 0, output:\n" . join("\n", @$join_workgroup_output));
+ }
+ elsif (grep(/Method execution successful/i, @$join_workgroup_output)) {
+ notify($ERRORS{'OK'}, 0, "joined workgroup, output:\n" . join("\n", @$join_workgroup_output));
+ }
+ else {
+ notify($ERRORS{'WARNING'}, 0, "unexpected output joining workgroup, output:\n" . join("\n", @$join_workgroup_output));
+ }
+
if (!$self->reboot(300, 3, 1)) {
- notify($ERRORS{'WARNING'}, 0, "failed to remove $computer_name from Active Directory domain, failed to reboot computer after unjoining domain");
+ notify($ERRORS{'WARNING'}, 0, "failed to unjoin $computer_name from Active Directory domain, failed to reboot computer after unjoining domain");
+ return;
+ }
+
+ # Verify the computer no longer is joined to a domain
+ my $new_computer_current_domain = $self->ad_get_current_domain();
+ if ($new_computer_current_domain) {
+ notify($ERRORS{'WARNING'}, 0, "failed to unjoin $computer_name from Active Directory domain, it appears to still be a member of the $new_computer_current_domain domain");
return;
}
- $self->ad_delete_computer($computer_name);
+ #if (!defined($domain_dns_name)) {
+ # notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, image $image_name is not assigned to a domain");
+ # return;
+ #}
+ #elsif (!defined($domain_username)) {
+ # notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, user name is not configured for $domain_dns_name domain");
+ # return;
+ #}
+ #elsif (!defined($domain_password)) {
+ # notify($ERRORS{'WARNING'}, 0, "unable to remove $computer_name from AD, password is not configured for $domain_dns_name domain");
+ # return;
+ #}
+ # # Assemble the PowerShell script
+ # my $ad_powershell_script = <<EOF;
+ #\$Host.UI.RawUI.BufferSize = New-Object Management.Automation.Host.Size(5000, 500)
+ #\$ps_credential = New-Object System.Management.Automation.PsCredential("$domain_dns_name\\$domain_username", (ConvertTo-SecureString "$domain_password" -AsPlainText -Force))
+ #try {
+ # Add-Computer -WorkgroupName VCL -Credential \$ps_credential -ErrorAction Stop
+ #}
+ #catch {
+ # Write-Host "ERROR: failed to add computer to workgroup, error: \$(\$_.Exception.Message)"
+ # exit 1
+ #}
+ #EOF
+ #
+ # my ($exit_status, $output) = $self->run_powershell_as_script($ad_powershell_script, 1, 1);
+ # if (!defined($output)) {
+ # notify($ERRORS{'WARNING'}, 0, "failed to execute PowerShell script to remove $computer_name from Active Directory domain");
+ # return;
+ # }
+ # elsif (grep(/ERROR/, @$output)) {
+ # # Computer object was already or deleted or can't be found for some reason:
+ # # This command cannot be executed on target computer('') due to following error: No mapping between account names and security IDs was done.
+ # if (grep(/No mapping between account names/, @$output)) {
+ # notify($ERRORS{'WARNING'}, 0, "failed to remove $computer_name from Active Directory domain, the computer object may have been deleted from the domain, output:\n" . join("\n", @$output));
+ # }
+ # else {
+ # notify($ERRORS{'WARNING'}, 0, "failed to remove $computer_name from Active Directory domain, output:\n" . join("\n", @$output));
+ # }
+ # return 0;
+ # }
+ #
+ # notify($ERRORS{'OK'}, 0, "removed $computer_name from Active Directory domain, output:\n" . join("\n", @$output));
+
+ $self->ad_delete_computer($computer_name, $computer_current_domain);
return 1;
}
@@ -13792,6 +13953,14 @@ EOF
Parameters : none
Returns : boolean
Description : Checks if the computer is joined to any Active Directory domain.
+ Returns the following:
+ * undefined - Error occurred, unable to determine if computer is
+ joined to a domain.
+ * 0 - Computer is not joined to a domain.
+ * string - Computer is joined to a domain. The domain name is
+ returned.
+ * 1 - Computer is joined to a domain but the domain name could
+ not be determined.
=cut
@@ -13860,7 +14029,11 @@ sub ad_search {
return;
}
- my ($ldap_filter_argument, $attempt_limit) = @_;
+ my $arguments = shift;
+
+ my $computer_name = $self->data->get_computer_short_name();
+
+ my $ldap_filter_argument = $arguments->{ldap_filter};
if (!defined($ldap_filter_argument)) {
notify($ERRORS{'WARNING'}, 0, "LDAP filter hash reference argument was not supplied");
return;
@@ -13873,11 +14046,8 @@ sub ad_search {
notify($ERRORS{'WARNING'}, 0, "empty LDAP FILTER hash reference argument was supplied");
return;
}
-
- $attempt_limit = 3 unless $attempt_limit;
-
- # Make sure objectClass was specified
- if (!defined($ldap_filter_argument->{objectClass})) {
+ elsif (!defined($ldap_filter_argument->{objectClass})) {
+ # Make sure objectClass was specified
notify($ERRORS{'WARNING'}, 0, "LDAP FILTER hash reference argument does not contain an objectClass value:\n" . format_data($ldap_filter_argument));
return;
}
@@ -13886,24 +14056,24 @@ sub ad_search {
return;
}
- # This sub handles both search and delete under very strict conditions
- # This is somewhat ugly but was done to reduce code duplication - especially with the Powershell below
- my $operation;
- my $calling_subroutine = get_calling_subroutine();
- if ($calling_subroutine =~ /(ad_delete_computer)/) {
- $operation = 'delete';
+ my $domain_dns_name;
+ my $domain_username;
+ my $domain_password;
+ if (defined($arguments->{domain_dns_name})) {
+ $domain_dns_name = $arguments->{domain_dns_name};
+ ($domain_username, $domain_password) = get_active_directory_domain_credentials($domain_dns_name);
+ if (!defined($domain_username) || !defined($domain_password)) {
+ notify($ERRORS{'WARNING'}, 0, "unable to search domain: $domain_dns_name, domain DNS name argument was specified but credentials could not be determined from existing 'addomain' table entries");
+ return;
+ }
}
else {
- $operation = 'search for';
+ $domain_dns_name = $self->data->get_image_domain_dns_name();
+ $domain_username = $self->data->get_image_domain_username();
+ $domain_password = $self->data->get_image_domain_password();
}
-
- my $computer_name = $self->data->get_computer_short_name();
-
- my $domain_dns_name = $self->data->get_image_domain_dns_name();
- my $domain_username = $self->data->get_image_domain_username();
- my $domain_password = $self->data->get_image_domain_password();
if (!defined($domain_dns_name)) {
- notify($ERRORS{'WARNING'}, 0, "unable to determine if AD object exists on $computer_name, domain DNS name is not configured");
+ notify($ERRORS{'WARNING'}, 0, "unable to determine if AD object exists on $computer_name, domain DNS name is not configured for the image and was not passed as an argument");
return;
}
elsif (!defined($domain_username)) {
@@ -13915,6 +14085,19 @@ sub ad_search {
return;
}
+ my $attempt_limit = $arguments->{attempt_limit} || 3;
+
+ # This sub handles both search and delete under very strict conditions
+ # This is somewhat ugly but was done to reduce code duplication - especially with the Powershell below
+ my $operation;
+ my $calling_subroutine = get_calling_subroutine();
+ if ($calling_subroutine =~ /(ad_delete_computer)/) {
+ $operation = 'delete';
+ }
+ else {
+ $operation = 'search for';
+ }
+
my $search_attribute_count = scalar(keys %$ldap_filter_argument);
my $ldap_filter;
@@ -14064,7 +14247,7 @@ EOF
=head2 ad_delete_computer
- Parameters : $computer_samaccountname (optional)
+ Parameters : $computer_samaccountname (optional), $domain_dns_name (optional)
Returns : boolean
Description : Deletes a computer object from the active directory domain with a
sAMAccountName attribute matching the argument. If no argument is
@@ -14084,23 +14267,34 @@ sub ad_delete_computer {
return;
}
- my $computer_samaccountname = shift || $self->data->get_computer_short_name();
+ my ($computer_samaccountname, $domain_dns_name) = @_;
+ $computer_samaccountname = $self->data->get_computer_short_name() unless $computer_samaccountname;
+
+ # Make sure computer samAccountName does not contain a trailing dollar sign
+ # A dollar sign will be present if retrieved directly from AD
$computer_samaccountname =~ s/\$*$/\$/g;
- return $self->ad_search(
- {
+ my $ad_search_arguments = {
+ 'ldap_filter' => {
'objectClass' => 'computer',
'sAMAccountName' => $computer_samaccountname,
- },
- );
+ }
+ };
+
+ # If a specific domain was specified, retrieve the username and password for that domain
+ if ($domain_dns_name) {
+ $ad_search_arguments->{domain_dns_name} = $domain_dns_name;
+ }
+
+ return $self->ad_search($ad_search_arguments);
}
#/////////////////////////////////////////////////////////////////////////////
=head2 ad_search_computer
- Parameters : $computer_samaccountname (optional)
+ Parameters : $computer_samaccountname (optional), $domain_dns_name (optional)
Returns : string
Description : Checks if a computer exists in the Active Directory domain with a
sAMAccountName attribute matching the argument. If found, a
@@ -14115,16 +14309,27 @@ sub ad_search_computer {
return;
}
- my $computer_samaccountname = shift || $self->data->get_computer_short_name();
+ my ($computer_samaccountname, $domain_dns_name) = @_;
+
+ $computer_samaccountname = $self->data->get_computer_short_name() unless $computer_samaccountname;
+ # Make sure computer samAccountName does not contain a trailing dollar sign
+ # A dollar sign will be present if retrieved directly from AD
$computer_samaccountname =~ s/\$*$/\$/g;
- my @computer_dns = $self->ad_search(
- {
+ my $ad_search_arguments = {
+ 'ldap_filter' => {
'objectClass' => 'computer',
'sAMAccountName' => $computer_samaccountname,
}
- );
+ };
+
+ # If a specific domain was specified, retrieve the username and password for that domain
+ if ($domain_dns_name) {
+ $ad_search_arguments->{domain_dns_name} = $domain_dns_name;
+ }
+
+ my @computer_dns = $self->ad_search($ad_search_arguments);
if (@computer_dns) {
return $computer_dns[0];
}
@@ -14174,8 +14379,10 @@ sub ad_search_ou {
return $self->ad_search(
{
- 'objectClass' => 'organizationalUnit',
- $attribute_name => $ou_identifier,
+ 'ldap_filter' => {
+ 'objectClass' => 'organizationalUnit',
+ $attribute_name => $ou_identifier,
+ }
}
);
}
@@ -14216,8 +14423,10 @@ sub ad_user_exists {
my @user_dns = $self->ad_search(
{
- 'objectClass' => 'user',
- 'sAMAccountName' => $user_samaccountname,
+ 'ldap_filter' => {
+ 'objectClass' => 'user',
+ 'sAMAccountName' => $user_samaccountname,
+ }
}
);
Modified: vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm?rev=1785881&r1=1785880&r2=1785881&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/Provisioning.pm Tue Mar 7 20:28:49 2017
@@ -128,14 +128,22 @@ sub node_status {
# Check if the post-load tasks have been completed
my $post_load_status = $self->os->get_post_load_status();
- if ($post_load_status) {
- notify($ERRORS{'OK'}, 0, "OS module post_load tasks have been completed on $computer_name, returning 'READY'");
- return 'READY';
- }
- else {
+ if (!$post_load_status) {
notify($ERRORS{'DEBUG'}, 0, "OS module post_load tasks have NOT been completed on $computer_name, returning 'POST_LOAD'");
return 'POST_LOAD';
}
+
+ # Check if OS module implements a node_status_os_check subroutine
+ # Currently, this is only used by the Windows module to ensure the AD configuration is correct if an image's AD configuration is changed after a computer is loaded
+ if ($self->os->can('node_status_os_check')) {
+ if (!$self->os->node_status_os_check()) {
+ notify($ERRORS{'DEBUG'}, 0, "OS module's node_status_os_check returned false, returning 'RELOAD'");
+ return 'RELOAD';
+ }
+ }
+
+ notify($ERRORS{'DEBUG'}, 0, "general node status checks all succeeded, returning 'READY'");
+ return 'READY';
}
#/////////////////////////////////////////////////////////////////////////////
Modified: vcl/trunk/managementnode/lib/VCL/utils.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/utils.pm?rev=1785881&r1=1785880&r2=1785881&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/utils.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/utils.pm Tue Mar 7 20:28:49 2017
@@ -115,6 +115,7 @@ our @EXPORT = qw(
format_data
format_hash_keys
format_number
+ get_active_directory_domain_credentials
get_affiliation_info
get_array_intersection
get_block_request_image_info
@@ -14697,6 +14698,61 @@ EOF
#/////////////////////////////////////////////////////////////////////////////
+=head2 get_active_directory_domain_credentials
+
+ Parameters : $domain_dns_name, $no_cache (optional)
+ Returns : ($username, $password)
+ Description : Attempts to retrieve the username and password for the domain
+ from the addomain table. This is used if a computer needs to be
+ removed from a domain but the reservation image is not configured
+ for Active Directory. When this occurs, the credentials are not
+ available from $self->data.
+
+=cut
+
+sub get_active_directory_domain_credentials {
+ my ($domain_dns_name, $no_cache) = @_;
+ if (!$domain_dns_name) {
+ notify($ERRORS{'WARNING'}, 0, "domain DNS name argument was not specified");
+ return;
+ }
+
+ if (!$no_cache && defined($ENV{active_directory_domain_credentials}{$domain_dns_name})) {
+ notify($ERRORS{'DEBUG'}, 0, "returning cached Active Directory credentials for $domain_dns_name domain");
+ return @{$ENV{active_directory_domain_credentials}{$domain_dns_name}};
+ }
+
+ # Construct the select statement
+ my $select_statement = <<EOF;
+SELECT DISTINCT
+username,
+password
+FROM
+addomain
+WHERE
+addomain.domainDNSName = '$domain_dns_name'
+EOF
+
+ # Call the database select subroutine
+ my @selected_rows = database_select($select_statement);
+
+ # Check to make sure 1 row was returned
+ if (scalar @selected_rows == 0) {
+ notify($ERRORS{'DEBUG'}, 0, "Active Directory domain does not exist in the database: $domain_dns_name");
+ return ();
+ }
+
+ # Get the single row returned from the select statement
+ my $row = $selected_rows[0];
+ my $username = $row->{username};
+ my $password = $row->{password};
+ notify($ERRORS{'DEBUG'}, 0, "retrieved credentials for $domain_dns_name domain from database, username: '$username', password: '$password'");
+ $ENV{active_directory_domain_credentials}{$domain_dns_name} = [$username, $password];
+ return @{$ENV{active_directory_domain_credentials}{$domain_dns_name}};
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
1;
__END__