I had an issue recently where a single ESXi host’s clock was incorrect. The administrator had never set the clock initially, so NTP never kept it in sync cause it was too far off to begin.
Since I’ve got a large number of hosts and the idea of clicking to each one through VI Client and checking the configuration tab, I immediately turned to PowerCLI. Naturally, one of Luc‘s scripts was the top search result.
That solved my immediate need to check the hosts, but I also wanted to setup some general monitoring. Since my monitoring infrastructure is compromised, primarily, of a linux Nagios host, that means PowerCLI couldn’t help. So, I did the next best thing and ported Luc’s script to perl.
Below is the result of that porting. It can also be run from vMA for reporting via email or another mechanism.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
#!/usr/bin/perl # # vi-check-host-time.pl - written by Andrew Sullivan, 2011-08-17 # # Please report bugs and request improvements at http://get-admin.com/blog/uncategorized/perl-toolkit-check-esxi-host-time/ # # A perl port of LucD's script for checking the time of an ESX(i) host against # the localhost to check for drift. The original can be found here: # http://communities.vmware.com/message/1732818#1732818 # # Examples: # Check all hosts connected to a vCenter server # ./vi-check-host-time.pl --server your.vi.server # # Check a specific host # ./vi-check-host-time.pl --server some.esxi.host # use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../"; use VMware::VIRuntime; use Time::Local; Opts::parse(); Opts::validate(); Util::connect(); # number of seconds that is tolerable for drift my $allowedDrift = 20; # get only hosts that are connected to vCenter, get only the name and dateTimeSystem MOR my $hosts = Vim::find_entity_views( view_type => 'HostSystem', filter => { 'summary.runtime.connectionState' => "connected" }, properties => [ 'summary.config.name', 'configManager.dateTimeSystem' ] ); foreach my $host (@$hosts) { # get the date of the system and all the parts so we can convert to epoch time my $dts = Vim::get_view( mo_ref => $host->get_property('configManager.dateTimeSystem') ); my $hosttime = $dts->QueryDateTime(); my ($year, $month, $day, $hour, $minutes, $seconds) = $hosttime =~ /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}).*/; # do the convertion to epoch time my $hostepoch = timegm($seconds, $minutes, $hour, $day, $month - 1, $year - 1900); # get the epoch time for the local system my $localtime = time(); # check for drift, output something if ( abs($hostepoch - $localtime) > $allowedDrift ) { print $host->get_property('summary.config.name') . " is " . ($hostepoch - $localtime) . " seconds off! n"; } else { print $host->get_property('summary.config.name') . " is within allowable drift n"; } } Util::disconnect(); |
nice script. A little patch though (probably caused by twordpress messing with your code). In the for loop of the esx hosts when parsing the hosttime variable to pieces the regex misses the ‘\’ before the ‘d{x}’ matches, so it must be ‘\d{x}’ instead.
Oxtan,
Thank you for catching that! You were absolutely correct, WordPress did not like the backslash. Should be all fixed up now.
Thanks again.
Andrew
I found another edge case. If the esx host clock is off on the early side (so the esx host clock is 17:00 whereas the actual clock is 17:03, for instance) then ( $hostepoch – $localtime) will always be lower than $allowedDrift because ( $hostepoch – $localtime) is a negative integer.
In that case you need to add an extra elsif block like this:
elsif ( ( $localtime – $hostepoch ) > $allowedDrift ) {
print $host->get_property(‘summary.config.name’) . ” is ” . ($hostepoch – $localtime) . ” seconds off! n”;
}
For my nagios check I added two extra variables:
# save here info for esx hosts whose clocks are not synchronized
my %drift_ok;
my %drift_warn;
and in the check for drift if loop and end of script is like this:
# check for drift, output something
if ( ( $hostepoch – $localtime ) > $allowedDrift ) {
$drift_warn{$hostname} = $drifted_time;
}
elsif ( ( $localtime – $hostepoch ) > $allowedDrift ) {
$drift_warn{$hostname} = $drifted_time;
}
else {
$drift_ok{$hostname} = $drifted_time;
}
}
# this will be > 0 if more than 0 hosts have a bigger drift than allowed
my $warning = scalar ( keys %drift_warn );
if ( $warning > 0 ) {
while ( my ( $hostname, $drift ) = each %drift_warn ) {
print
“$hostname has drifted by $drift seconds relative to the ntp server\n”;
}
exit 1;
}
else {
print “OK: all connected esx servers are properly in sync with our ntp server\n”;
exit 0;
}
Thanks for your work, it saved me some leg work 🙂
and right after posting all this I remember about the abs builtin Perl funtion 🙂
So much simpler:
if ( abs ( $hostepoch – $localtime ) > $allowedDrift ) {
$drift_warn{$hostname} = $drifted_time;
}
no elsif block, the rest remains the same. I should post my script to github …
Thank you again for catching my errors! I’ve modified to use the absolute value of the subtraction operation in the comparison.
Andrew