August 19th, 2008

Hey, Scripting Guy! How Long Has My Server Been Up?

Hey, Scripting Guy! Question

Hey, Scripting Guy! Everyone at work has the idea that our network is unreliable. However, I know that our servers are seldom down. How can I prove to them that I know my stuff?

— HB

SpacerHey, Scripting Guy! Answer

Hi HB,

You are really asking two questions. The first I can help you with. However, the veracity of your correctness claim is up to you. Your initial inquiry is the age-old system administrator question, which was first postulated by the ancient Phoenician philosopher Pherecydes of Syros in 544 B.C. when he spoke about the Chronos. Ever since then, admins everywhere have engaged the downtime battle with increasing vigilance and fervor. (Interestingly enough, your second question was first verbalized by a more recent American philosopher who intoned thusly: “I gotta tell ya, I get no respect.” [Followed by a neck tie adjustment and crazy, bulging eyes.])

To approach your problem, HB, we can use the WMI class Win32_OperatingSystem which has thelastBootupTime property. This property can help us to obtain system uptime. To perform such a feat, we will need to first query the lastBootupTime property, collect the current system time, and subtract the two date time objects. Additionally, it would be nice if the script could take a text file input with a list of computers to operate against and write the results to a file we designate. The “FindSystemUpTime.ps1” script seen here will fit the bill nicely.

$aryComputer = Get-Content c:\fso\servers.txt
$now = get-date
$filePath = "C:\fso\uptime.txt"
foreach ($computer in $aryComputer)
{
 $wmi = Get-WmiObject -class win32_operatingsystem -namespace root\cimv2 `
 -computername $computer
 $lastBootTime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)
 "$computer has been up for $($now - $lastBootTime)" |  
 Tee-Object -variable uptime
 out-file -filepath $filepath -inputObject $uptime -append
} #end foreach

To begin with, HB, we use Windows PowerShell to see if we can connect on our local computer and pick out the lastBootupTime. As you can see here, we can indeed accomplish this feat.

(Get-WmiObject -Class Win32_OperatingSystem -Namespace root\cimv2 `
-computername localhost).lastbootuptime

This script returns an output similar to the following (it will be exactly the same if you run the script on August 18, 2008, in the UTC-5 time zone with daylight saving time enabled; if not, your output will be similar):

20080818084846.375199-240

The problem with this, as currently written, is that it displays the returned time value in Universal Time Coordinate (UTC) fashion which can be rather difficult to understand (for a good discussion of UTC time, refer to the Microsoft Press book, Microsoft Windows Scripting with WMI: Self-Paced Learning Guide.)

In VBScript, we had two approaches to handling UTC time. The first involved using a function written by the ScriptingGuys that did string manipulation to arrange the numbers into a more readable sequence. The other approach involved using the SwbemDateTime object (which was about as complicated as the name of the object itself).

In Windows PowerShell we have a script method that is added to all the WMI classes, which allows us to easily convert the time. This method is called ConvertToDateTime. To use this method, our script would now look like this:

$wmi = Get-WmiObject -class Win32_OperatingSystem -namespace root\cimv2 -computername localhost
$wmi.ConvertToDateTime($wmi.LastBootUpTime)

So far, we have only succeeded in displaying the time our server last booted up. Now we need to subtract this value from the current time. To do this, we create a variable that will hold the current date-time stamp. We call this variable $now as seen here.

$now = get-date

Let’s also store the last bootup time value into a variable called $lastBootTime. This line of code is shown here:

$lastBootTime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)

Now we want to print this value to the screen. At the same time, we would like to store the results into a file. To do this, we will use theTee-Object cmdlet, which allows us to store the results in either a file or a variable, while at the same time printing to the screen. We can store the results into a variable named uptime. This line of code is illustrated here.

"$computer has been up for $($now - $lastBootTime)" |  
Tee-Object -variable uptime

To write the contents of the variable uptime to a file, we use the Out-File cmdlet. This cmdlet takes two parameters: the first is the path to the file and the second is the variable containing the data we wish to write to the file. At the top of the script, we create a variable named $filepath that holds the path to our file. The Out-File command looks like this:

out-file -filepath $filepath -inputObject $uptime -append

We decided it would be nice if we could run this command against multiple computers. The first thing we need to do is get rid of the hard-coded computername parameter and replace it with a variable named $computer. Next we need to create a variable that will hold the results of reading a text file. We will call this $aryComputer. This line of code is seen here:

$aryComputer = Get-Content -path c:\fso\servers.txt

The servers.txt file is nothing special. It is just a text file with the names of servers listed on individual lines.

Now we need to implement a bit of looping behavior. This sounds like the job for the foreach statement. The foreach statement uses two things. The first is a set of parentheses that contains the variable used to keep track of where it is in the collection and the variable that contains the collection that will be operated against. The second item, the foreach statement, uses a set of curly brackets that contains the code that will be executed. This structure, although strange looking to VBScripters, behaves the same way the foreach – next statement does. After we put the foreach statement, parentheses, and curly brackets into our code, the section of our script now looks like this:

foreach ($computer in $aryComputer)
{
 $wmi = Get-WmiObject -class win32_operatingsystem -namespace root\cimv2 -computername $computer
 $lastBootTime = $wmi.ConvertToDateTime($wmi.LastBootUpTime)
 "$computer has been up for $($now - $lastBootTime)" |  Tee-Object -variable uptime
 out-file -filepath $filepath -inputObject $uptime -append
} #end foreach

HB, I hope this helps you to determine how long your servers have been up and running. In our next article, we will examine a different approach to the same problem. Yes—a cliffhanger!

Ed Wilson and Craig Liebendorfer, Scripting Guys

Author

0 comments

Discussion are closed.