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/08/04 18:22:34 UTC

svn commit: r1804148 - in /vcl/trunk/managementnode/lib/VCL/Module/OS: Windows.pm Windows/Version_6.pm

Author: arkurth
Date: Fri Aug  4 18:22:33 2017
New Revision: 1804148

URL: http://svn.apache.org/viewvc?rev=1804148&view=rev
Log:
VCL-1069
Added call to 'w32tm.exe /unregister' early in Windows.pm::configure_time_synchronization. This seems to have fixed intermittent odd behavior.

Added optional message argument to Windows.pm::get_current_computer_time for debugging.

Added Version_6.pm::get_time_service_events. This was used for debugging but isn't actually called from the code.

Modified:
    vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm
    vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm

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=1804148&r1=1804147&r2=1804148&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows.pm Fri Aug  4 18:22:33 2017
@@ -8870,7 +8870,7 @@ sub configure_time_synchronization {
 		return;
 	}
 	
-	my $start_time = time;
+	my $time_string_before = $self->get_current_computer_time('before configure_time_synchronization');
 	
 	my $system32_path = $self->get_system32_path() || return;
 	my $computer_name = $self->data->get_computer_node_name();
@@ -8888,13 +8888,11 @@ sub configure_time_synchronization {
 		$time_source_variable = 'pool.ntp.org';
 	}
 	
-	$self->get_current_computer_time();
-	
 	my $key = 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers';
 	
 	# Delete existing key
 	$self->reg_delete($key);
-	
+
 	# Update the registry
 	my $index = 1;
 	my @time_sources = split(/[,;\s]/, $time_source_variable);
@@ -8913,19 +8911,29 @@ sub configure_time_synchronization {
 		$manual_peer_list .= $time_source;
 	}
 	
-	#my $debug_command = "$system32_path/w32tm.exe /debug /enable /file:C:/w32tm.log /size:1024000000 /entries:0-300";
-	#my ($debug_exit_status, $debug_output) = $self->execute({command => $debug_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
-	#if (!defined($debug_output)) {
-	#	notify($ERRORS{'WARNING'}, 0, "failed to execute command to enable W32Time debugging on $computer_name: $debug_command");
-	#	return;
-	#}
-	#elsif ($debug_exit_status ne '0') {
-	#	notify($ERRORS{'WARNING'}, 0, "failed to enable W32Time debugging on $computer_name, exit status: $debug_exit_status, command:\n$debug_command\noutput:\n" . join("\n", @$debug_output));
-	#}
-	#else {
-	#	notify($ERRORS{'OK'}, 0, "enabled W32Time debugging on $computer_name, output:\n" . join("\n", @$debug_output));
-	#}
+	# Make sure the service is stopped before calling /unregister or it will fail
+	$self->stop_service('w32time');
+
+	# Call /unregister the time service first - calling only /register doesn't seem to reset everything
+	# Unexpected results may occur if this isn't done
+	$self->get_current_computer_time('before unregister');
+	my $unregister_command = "$system32_path/w32tm.exe /unregister";
+	my ($unregister_exit_status, $unregister_output) = $self->execute({command => $unregister_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
+	if (!defined($unregister_output)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute command to unregister W32Time on $computer_name: $unregister_command");
+		return;
+	}
+	elsif ($unregister_exit_status ne '0') {
+		notify($ERRORS{'WARNING'}, 0, "failed to unregister W32Time on $computer_name, exit status: $unregister_exit_status, command:\n$unregister_command\noutput:\n" . join("\n", @$unregister_output));
+	}
+	else {
+		notify($ERRORS{'OK'}, 0, "unregistered W32Time on $computer_name");
+	}
+	
+	# Make sure no remnants are leftover
+	$self->reg_delete('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time');
 	
+	# Call /register - registers w32time service and sets default configuration to the registry
 	my $register_command = "$system32_path/w32tm.exe /register";
 	my ($register_exit_status, $register_output) = $self->execute({command => $register_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
 	if (!defined($register_output)) {
@@ -8939,15 +8947,35 @@ sub configure_time_synchronization {
 		notify($ERRORS{'OK'}, 0, "registered W32Time on $computer_name");
 	}
 	
+	# Uncomment the next few lines for for debugging
+	#my $debug_command = "$system32_path/w32tm.exe /debug /enable /file:C:/Windows/Debug/w32tm.log /size:1024000000 /entries:0-300";
+	#my ($debug_exit_status, $debug_output) = $self->execute({command => $debug_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
+	#if (!defined($debug_output)) {
+	#	notify($ERRORS{'WARNING'}, 0, "failed to execute command to enable W32Time debugging on $computer_name: $debug_command");
+	#	return;
+	#}
+	#elsif ($debug_exit_status ne '0') {
+	#	notify($ERRORS{'WARNING'}, 0, "failed to enable W32Time debugging on $computer_name, exit status: $debug_exit_status, command:\n$debug_command\noutput:\n" . join("\n", @$debug_output));
+	#}
+	#else {
+	#	notify($ERRORS{'OK'}, 0, "enabled W32Time debugging on $computer_name, output:\n" . join("\n", @$debug_output));
+	#}
+
+	# Only care about w32tm.exe config and sync duration
+	my $start_time = time;
+	
 	# By default, Windows time service will only allow the time to be changes by 15 hours (54,000 seconds) or less
 	# Set the following keys to allow any time adjustment
 	# This must be done after w32tm.exe /register because that command will reset the values to the defaults
 	$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxPosPhaseCorrection', 'REG_DWORD', '0xFFFFFFFF');
 	$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxNegPhaseCorrection', 'REG_DWORD', '0xFFFFFFFF');
 	
+	# Start the service before calling /config or else /config will fail
 	$self->start_service('w32time') || return;
-	
-	my $config_command = "$system32_path/w32tm.exe /config /manualpeerlist:\"$manual_peer_list\" /syncfromflags:manual /update";
+
+	my $config_command = "$system32_path/w32tm.exe /config /manualpeerlist:\"$manual_peer_list\" /syncfromflags:manual";
+	# The following argument shouldn't be necessary if the service is later restarted:
+	$config_command .= " /update";
 	my ($config_exit_status, $config_output) = $self->execute({command => $config_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
 	if (!defined($config_output)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to execute command to configure W32Time on $computer_name: $config_command");
@@ -8957,24 +8985,30 @@ sub configure_time_synchronization {
 		notify($ERRORS{'WARNING'}, 0, "failed to configure W32Time on $computer_name to use time source(s): $manual_peer_list, exit status: $config_exit_status, command:\n$config_command\noutput:\n" . join("\n", @$config_output));
 	}
 	else {
-		notify($ERRORS{'OK'}, 0, "configured W32Time on $computer_name to use time source(s): $manual_peer_list");
+		notify($ERRORS{'DEBUG'}, 0, "configured W32Time on $computer_name to use time source(s): $manual_peer_list");
 	}
 	
-	$self->restart_service('w32time') || return;
+	# Restart the service
+	# This shouldn't be necessary if the /config command above includes /update
+	#$self->restart_service('w32time') || return;
 	
+	# Call /resync
 	my $resync_command = "$system32_path/w32tm.exe /resync";
+	$resync_command .= " /force";
 	my ($resync_exit_status, $resync_output) = $self->execute({command => $resync_command, timeout_seconds => 20, max_attempts => 1, display_output => 0});
 	if (!defined($resync_output)) {
 		notify($ERRORS{'WARNING'}, 0, "failed to execute command to resync W32Time on $computer_name: $resync_command");
 		return;
 	}
-	elsif ($resync_exit_status ne '0') {
-		notify($ERRORS{'WARNING'}, 0, "failed to resync W32Time on $computer_name, exit status: $resync_exit_status, command:\n$resync_command\noutput:\n" . join("\n", @$resync_output));
+	elsif (grep(/success/, @$resync_output)) {
+		notify($ERRORS{'DEBUG'}, 0, "resynchronized W32Time on $computer_name, output:\n" . join("\n", @$resync_output));
 	}
 	else {
-		notify($ERRORS{'OK'}, 0, "resync'd W32Time on $computer_name, output:\n" . join("\n", @$resync_output));
+		notify($ERRORS{'DEBUG'}, 0, "W32Time resynchronization may have failed on $computer_name:\n" . join("\n", @$resync_output));
 	}
 	
+	my $duration = (time - $start_time);
+	
 	# Set the w32time service startup mode to auto
 	if ($self->set_service_startup_mode('w32time', 'auto')) {
 		notify($ERRORS{'DEBUG'}, 0, "set w32time service startup mode to auto");
@@ -8984,14 +9018,19 @@ sub configure_time_synchronization {
 		return;
 	}
 	
+	# For debugging:
+	#$self->get_time_service_events() if $self->can('get_time_service_events');
+	
 	# Set the maximum time change parameters back to the defaults for security
-	#$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxPosPhaseCorrection', 'REG_DWORD', 50000000);
-	#$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxNegPhaseCorrection', 'REG_DWORD', 50000000);
+	$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxPosPhaseCorrection', 'REG_DWORD', 50000000);
+	$self->reg_add('HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\Config', 'MaxNegPhaseCorrection', 'REG_DWORD', 50000000);
 	
-	$self->get_current_computer_time();
+	my $time_string_after = $self->get_current_computer_time('after configure_time_synchronization');
 	
-	my $duration = (time - $start_time);
-	notify($ERRORS{'DEBUG'}, 0, "configure time synchronization duration: $duration seconds");
+	notify($ERRORS{'DEBUG'}, 0, "configured time synchronization duration: $duration seconds\n" .
+		"time before configuration : $time_string_before\n" .
+		"time after configuration  : $time_string_after"
+	);
 	return 1;
 }
 
@@ -8999,7 +9038,7 @@ sub configure_time_synchronization {
 
 =head2 get_current_computer_time
 
- Parameters  : none
+ Parameters  : $message (optional)
  Returns     : string
  Description : Used for debugging and troubleshooting purposes. Simply displays
                the current date, time, and timezone offset according to the
@@ -9015,6 +9054,8 @@ sub get_current_computer_time {
 		return;
 	}
 	
+	my $message = shift;
+	
 	my $computer_name = $self->data->get_computer_node_name();
 	
 	#my $command = 'cmd.exe /c "echo %date% %time%"';
@@ -9031,7 +9072,7 @@ sub get_current_computer_time {
 	
 	my ($current_time) = grep(/\d:/, @$output);
 	if ($current_time) {
-		notify($ERRORS{'OK'}, 0, "retrieved current time on $computer_name: $current_time");
+		notify($ERRORS{'OK'}, 0, "retrieved current time on $computer_name: $current_time ($message)");
 		return $current_time;
 	}
 	else {

Modified: vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm
URL: http://svn.apache.org/viewvc/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm?rev=1804148&r1=1804147&r2=1804148&view=diff
==============================================================================
--- vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm (original)
+++ vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Version_6.pm Fri Aug  4 18:22:33 2017
@@ -2121,7 +2121,7 @@ sub disable_sleep {
 
 =head2 query_event_log
 
- Parameters  : $event_log_name, $xpath_query, $event_count_limit (optional)
+ Parameters  : $event_log_name, $xpath_query, $event_count_limit (optional), $display_output (optional)
  Returns     : array
  Description : Queries the event log on the computer. The $event_log_name
                argument refers to the 'Channel' property of the events to be
@@ -2133,6 +2133,7 @@ sub disable_sleep {
                *
                *[System[Provider[@Name="Microsoft-Windows-Security-Auditing"] and Task=12544 and EventID=4624] and EventData[Data[@Name="LogonType"]="10"]]
                *[System[TimeCreated[timediff(@SystemTime) <= $milliseconds]]]
+               *[System[Provider[@Name="Microsoft-Windows-Time-Service"]]]
                
                Constructs an array of hashes based on the XML output from
                wevtutil.exe.
@@ -2146,7 +2147,7 @@ sub query_event_log {
 		return;
 	}
 	
-	my ($event_log_name, $xpath_query, $event_count_limit) = @_;
+	my ($event_log_name, $xpath_query, $event_count_limit, $display_output) = @_;
 	if (!$event_log_name) {
 		notify($ERRORS{'WARNING'}, 0, "event log name argument was not specified");
 		return;
@@ -2206,7 +2207,26 @@ sub query_event_log {
 	my @events = @{$xml_hash->{Event}};
 	my $event_count = scalar(@events);
 	
-	notify($ERRORS{'DEBUG'}, 0, "retrieved $event_count $event_log_name event" . ($event_count == 1 ? '' : 's') . " from $computer_node_name, command:\n$command\n" . format_data(\@events));
+	my $levels = {
+		0 => 'Undefined',
+		1 => 'Critical',
+		2 => 'Error',
+		3 => 'Warning',
+		4 => 'Information',
+		5 => 'Verbose',
+	};
+	
+	for my $event (@events) {
+		my $level = $event->{System}{Level};
+		if (defined($level) && $levels->{$level}) {
+			$event->{System}{LEVEL_NAME} = $levels->{$level};
+		}
+		else {
+			$event->{System}{LEVEL_NAME} = '<unknown>';
+		}
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved $event_count $event_log_name event" . ($event_count == 1 ? '' : 's') . " from $computer_node_name, command:\n$command\n" . format_data(\@events)) if $display_output;
 	return @events;
 }
 
@@ -2271,6 +2291,81 @@ sub query_event_log_helper {
 }
 
 #//////////////////////////////////////////////////////////////////////////////
+
+=head2 get_time_service_events
+
+ Parameters  : none
+ Returns     : array
+ Description : Retrieves event log entries generated by the Windows Time
+               (w32time) service. Returns an array of hash references:
+                  [
+                    {
+                      "EventData" => {
+                        "Data" => {
+                          "TimeSource" => "pool.ntp.org (ntp.m|0x0|0.0.0.0:123-><IP Address>:123)"
+                        },
+                        "Name" => "TMP_EVENT_TIME_SOURCE_REACHABLE"
+                      },
+                      "System" => {
+                        "Channel" => "System",
+                        "Computer" => "win2012r2",
+                        "Correlation" => {},
+                        "EventID" => 37,
+                        "EventRecordID" => 3242,
+                        "Execution" => {
+                          "ProcessID" => 796,
+                          "ThreadID" => 1204
+                        },
+                        "Keywords" => "0x8000000000000000",
+                        "Level" => 4,
+                        "Opcode" => 0,
+                        "Provider" => {
+                          "Guid" => "{06EDCFEB-0FD0-4E53-ACCA-A6F8BBF81BCB}",
+                          "Name" => "Microsoft-Windows-Time-Service"
+                        },
+                        "Security" => {
+                          "UserID" => "S-1-5-19"
+                        },
+                        "Task" => 0,
+                        "TimeCreated" => {
+                          "SystemTime" => "2017-07-28T12:08:12.195010600Z"
+                        },
+                        "Version" => 0
+                      },
+                      "xmlns" => "http://schemas.microsoft.com/win/2004/08/events/event"
+                    },
+                    {
+                      ...
+                    },
+                  ]
+
+=cut
+
+sub get_time_service_events {
+	my $self = shift;
+	if (ref($self) !~ /VCL::Module/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 @events = $self->query_event_log('System', '*[System[Provider[@Name="Microsoft-Windows-Time-Service"]]]', 100, 0);
+	
+	my $info_string;
+	for my $event (@events) {
+		my $level = $event->{System}{LEVEL_NAME};
+		my $system_time = $event->{System}{TimeCreated}{SystemTime};
+		my $event_name = $event->{EventData}{Name};
+		my $event_data = $event->{EventData}{Data};
+		$info_string .= "($level) $system_time - $event_name\n" . format_data($event_data) . "\n\n";
+	}
+	
+	notify($ERRORS{'DEBUG'}, 0, "retrieved time service entries from event log on $computer_name:\n$info_string");
+	return @events;
+}
+
+#//////////////////////////////////////////////////////////////////////////////
 
 =head2 get_logon_events