Skip to content

Commit 8063ffe

Browse files
alex-harvey-z3qJeff McCune
authored andcommitted
(#13535) Fix uptime; use uptime instead of who -b
Without this patch applied the uptime fact value is often wrong on some flavors of Unix. In particular, the value may be negative or simply incorrect. This is a problem because the Facter value does not contained the truth of our reality. This makes it difficult to model reality inside of Puppet. This patch fixes the problem by replacing the unreliable who -b method and the Solaris kstat method with a simple call to the "uptime" command. This patch also adds RSpec tests for new method and remove RSpec tests for removed methods. Delete now-unused fixture file who_b_boottime. Reviewed-by: Jeff McCune <jeff@puppetlabs.com>
1 parent db3e2a5 commit 8063ffe

File tree

3 files changed

+87
-36
lines changed

3 files changed

+87
-36
lines changed

lib/facter/util/uptime.rb

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
module Facter::Util::Uptime
66
def self.get_uptime_seconds_unix
7-
uptime_proc_uptime or uptime_sysctl or uptime_kstat or uptime_who_dash_b
7+
uptime_proc_uptime or uptime_sysctl or uptime_executable
88
end
99

1010
def self.get_uptime_seconds_win
@@ -31,15 +31,30 @@ def self.uptime_sysctl
3131
end
3232
end
3333

34-
def self.uptime_kstat
35-
if output = Facter::Util::Resolution.exec("#{uptime_kstat_cmd} 2>/dev/null")
36-
compute_uptime(Time.at(output.chomp.split(/\s/).last.to_i))
37-
end
38-
end
39-
40-
def self.uptime_who_dash_b
41-
if output = Facter::Util::Resolution.exec("#{uptime_who_cmd} 2>/dev/null")
42-
compute_uptime(Time.parse(output))
34+
def self.uptime_executable
35+
if output = Facter::Util::Resolution.exec("#{uptime_executable_cmd} 2>/dev/null")
36+
up=0
37+
if output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+):(\d+)/
38+
# Regexp handles Solaris, AIX, HP-UX, and Tru64.
39+
# 'day(?:s|\(s\))?' says maybe 'day', 'days',
40+
# or 'day(s)', and don't set $2.
41+
up=86400*$1.to_i + 3600*$2.to_i + 60*$3.to_i
42+
elsif output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) hr(?:s|\(s\))?,/
43+
up=86400*$1.to_i + 3600*$2.to_i
44+
elsif output =~ /(\d+) day(?:s|\(s\))?,\s+(\d+) min(?:s|\(s\))?,/
45+
up=86400*$1.to_i + 60*$2.to_i
46+
elsif output =~ /(\d+) day(?:s|\(s\))?,/
47+
up=86400*$1.to_i
48+
elsif output =~ /up\s+(\d+):(\d+),/
49+
# must anchor to 'up' to avoid matching time of day
50+
# at beginning of line.
51+
up=3600*$1.to_i + 60*$2.to_i
52+
elsif output =~ /(\d+) hr(?:s|\(s\))?,/
53+
up=3600*$1.to_i
54+
elsif output =~ /(\d+) min(?:s|\(s\))?,/
55+
up=60*$1.to_i
56+
end
57+
up
4358
end
4459
end
4560

@@ -55,11 +70,7 @@ def self.uptime_sysctl_cmd
5570
'sysctl -n kern.boottime'
5671
end
5772

58-
def self.uptime_kstat_cmd
59-
'kstat -p unix:::boot_time'
60-
end
61-
62-
def self.uptime_who_cmd
63-
'who -b'
73+
def self.uptime_executable_cmd
74+
"uptime"
6475
end
6576
end

spec/fixtures/unit/util/uptime/who_b_boottime

Lines changed: 0 additions & 1 deletion
This file was deleted.

spec/unit/util/uptime_spec.rb

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,31 +41,72 @@
4141

4242
describe "nor is 'sysctl kern.boottime'" do
4343
before :each do
44-
Facter::Util::Uptime.stubs(:uptime_sysctl_cmd).returns("cat \"#{@nonexistent_file}\"")
44+
Facter::Util::Uptime.stubs(:uptime_proc_uptime).returns(false)
45+
Facter::Util::Uptime.stubs(:uptime_sysctl).returns(false)
46+
Facter.fact(:kernel).stubs(:value).returns('SunOS')
4547
end
4648

47-
it "should use 'kstat -p unix:::boot_time'" do
48-
kstat_output_file = my_fixture('kstat_boot_time') # unix:0:system_misc:boot_time 1236919980
49-
Facter::Util::Uptime.stubs(:uptime_kstat_cmd).returns("cat \"#{kstat_output_file}\"")
50-
Time.stubs(:now).returns Time.at(1236923580) #one hour later
51-
Facter::Util::Uptime.get_uptime_seconds_unix.should == 60 * 60
52-
end
53-
54-
describe "nor is 'kstat -p unix:::boot_time'" do
55-
before :each do
56-
Facter::Util::Uptime.stubs(:uptime_kstat_cmd).returns("cat \"#{@nonexistent_file}\"")
57-
end
49+
describe "should use 'uptime' command" do
50+
# Note about uptime variations.
51+
# Solaris (I have examined 5.6, 5.7, 5.8, 5.9, 5.10 & 5.11) and HP-UX (11.00, 11.11, 11.23, 11.31) have time
52+
# right justified at at 8 characters, and two spaces before 'up'.
53+
# Solaris differs from all other Unices (and Linux) in that the plural/singular case of minutes/hours/days are
54+
# written min(s)/hr(s)/day(s) instead of min/mins/hr/hrs etc., e.g. 1 min(s), 2 min(s) as opposed to
55+
# 1 min, 2 mins, etc.
56+
# AIX (4.3.3, 5.2, 5.3, 6.1) differs from other SysV Unices in that times are padded with a leading 0 in the
57+
# hour column where necessary, and have AM/PM in uppercase, and there are three spaces before 'up'.
58+
# Tru64 (4.0, 5.1) differs from other SysV Unices in that times are in 24 hour format, and there are no
59+
# leading spaces.
60+
# Linux (RHEL, uptime version 2.0.7) differs from the SysV Unices in that only minutes before the first hour
61+
# are written as 1 min, 2 min, 3 min etc. and after that full hours are rendered 1:00 or 21:00. Time of
62+
# day was written 5:37pm and right-justified at 8 characters, with 2 spaces before 'up'. A figure in
63+
# whole days was written as 3 days, 0 min.
64+
# By version 2.0.17 the time of day was rewritten in 24hr time with seconds, right-justified at 9 characters.
65+
# By version 3.2.7 one of the spaces before 'up' had been removed.
66+
test_cases = [
67+
[' 4:42pm up 1 min(s), 0 users, load average: 0.95, 0.25, 0.09', 1*60],
68+
['13:16 up 58 mins, 2 users, load average: 0.00, 0.02, 0.05', 58*60],
69+
['13:18 up 1 hr, 1 user, load average: 0.58, 0.23, 0.14', 1*60*60 ],
70+
[' 10:14pm up 3 hr(s), 0 users, load average: 0.00, 0.00, 0.00', 3*60*60 ],
71+
['14:18 up 2 hrs, 0 users, load average: 0.33, 0.27, 0.29', 2*60*60 ],
72+
[' 9:01pm up 1:47, 0 users, load average: 0.00, 0.00, 0.00', 1*60*60 + 47*60],
73+
['13:19 up 1:01, 1 user, load average: 0.10, 0.26, 0.21', 1*60*60 + 1*60],
74+
['10:49 up 22:31, 0 users, load average: 0.26, 0.34, 0.27', 22*60*60 + 31*60],
75+
['12:18 up 1 day, 0 users, load average: 0.74, 0.20, 0.10', 1*24*60*60 ],
76+
[' 2:48pm up 1 day(s), 0 users, load average: 0.21, 0.20, 0.17', 1*24*60*60 ],
77+
['12:18 up 2 days, 0 users, load average: 0.50, 0.27, 0.16', 2*24*60*60 ],
78+
[' 1:56pm up 25 day(s), 2 users, load average: 0.59, 0.56, 0.50', 25*24*60*60 ],
79+
[' 1:29pm up 485 days, 0 users, load average: 0.00, 0.01, 0.01', 485*24*60*60 ],
80+
[' 18:11:24 up 69 days, 0 min, 0 users, load average: 0.00, 0.00, 0.00', 69*24*60*60 ],
81+
['12:19 up 1 day, 1 min, 0 users, load average: 0.07, 0.16, 0.13', 1*24*60*60 + 1*60],
82+
[' 3:23pm up 25 day(s), 27 min(s), 2 users, load average: 0.49, 0.45, 0.46', 25*24*60*60 + 27*60],
83+
[' 02:42PM up 1 day, 39 mins, 0 users, load average: 1.49, 1.74, 1.80', 1*24*60*60 + 39*60],
84+
[' 18:13:13 up 245 days, 44 min, 1 user, load average: 0.00, 0.00, 0.00', 245*24*60*60 + 44*60],
85+
[' 6:09pm up 350 days, 2 min, 1 user, load average: 0.02, 0.03, 0.00', 350*24*60*60 + 2*60],
86+
[' 1:07pm up 174 day(s), 16 hr(s), 0 users, load average: 0.05, 0.04, 0.03', 174*24*60*60 + 16*60*60 ],
87+
[' 02:34PM up 621 days, 18 hrs, 0 users, load average: 2.67, 2.52, 2.56', 621*24*60*60 + 18*60*60 ],
88+
[' 3:30am up 108 days, 1 hr, 31 users, load average: 0.39, 0.40, 0.41', 108*24*60*60 + 1*60*60 ],
89+
['13:18 up 1 day, 1 hr, 0 users, load average: 0.78, 0.33, 0.18', 1*24*60*60 + 1*60*60 ],
90+
['14:18 up 1 day, 2 hrs, 0 users, load average: 1.17, 0.48, 0.41', 1*24*60*60 + 2*60*60 ],
91+
['15:56 up 152 days, 17 hrs, 0 users, load average: 0.01, 0.06, 0.07', 152*24*60*60 + 17*60*60 ],
92+
[' 5:37pm up 25 days, 21:00, 0 users, load average: 0.01, 0.02, 0.00', 25*24*60*60 + 21*60*60 ],
93+
[' 8:59pm up 94 day(s), 3:17, 46 users, load average: 0.66, 0.67, 0.70', 94*24*60*60 + 3*60*60 + 17*60],
94+
[' 3:01pm up 4496 day(s), 21:19, 32 users, load average: 0.61, 0.62, 0.62', 4496*24*60*60 + 21*60*60 + 19*60],
95+
[' 02:42PM up 41 days, 2:38, 0 users, load average: 0.38, 0.70, 0.55', 41*24*60*60 + 2*60*60 + 38*60],
96+
[' 18:13:29 up 25 days, 21:36, 0 users, load average: 0.00, 0.00, 0.00', 25*24*60*60 + 21*60*60 + 36*60],
97+
[' 13:36:05 up 118 days, 1:15, 1 user, load average: 0.00, 0.00, 0.00', 118*24*60*60 + 1*60*60 + 15*60]
98+
]
5899

59-
it "should use 'who -b'" do
60-
who_b_output_file = my_fixture('who_b_boottime') # Aug 1 14:13
61-
Facter::Util::Uptime.stubs(:uptime_who_cmd).returns("cat \"#{who_b_output_file}\"")
62-
Time.stubs(:now).returns Time.parse("Aug 01 15:13") # one hour later
63-
Facter::Util::Uptime.get_uptime_seconds_unix.should == 60 * 60
100+
test_cases.each do |uptime, expected|
101+
it "should return #{expected} for #{uptime}" do
102+
Facter::Util::Resolution.stubs(:exec).with('uptime 2>/dev/null').returns(uptime)
103+
Facter.fact(:uptime_seconds).value.should == expected
104+
end
64105
end
65106

66-
describe "nor is 'who -b'" do
107+
describe "nor is 'uptime' command" do
67108
before :each do
68-
Facter::Util::Uptime.stubs(:uptime_who_cmd).returns("cat \"#{@nonexistent_file}\"")
109+
Facter::Util::Uptime.stubs(:uptime_executable_cmd).returns("cat \"#{@nonexistent_file}\"")
69110
end
70111

71112
it "should return nil" do

0 commit comments

Comments
 (0)