Продолжительность безотказной работы компьютеров — важный статистический показатель, который помогает администратору обеспечивать эффективное управление системами. Существует несколько способов определения времени безотказной работы вычислительных систем (хочу отметить, что данный список ни в коем случае нельзя считать исчерпывающим):

  1. Продолжительность безотказной работы компьютера в днях, часах, минутах и секундах отображается на вкладке Performance диспетчера задач.
  2. Утилита командной строки Systeminfo показывает время последней перезагрузки компьютера.
  3. Время последней перезагрузки компьютера фиксируется в системном журнале регистрации событий под идентификатором события ID 6005.
  4. Класс Win32_OperatingSystem инструментария управления Windows имеет свойство LastBootUpTime, содержащее время последней перезагрузки компьютера.

Каждый из перечисленных приемов имеет свои достоинства и недостатки. Так, диспетчер задач обеспечивает возможность быстрого просмотра времени безотказной работы одного компьютера, а журнал регистрации событий содержит дополнительную информацию о событии 6005, которая может дать представление о характере отказа системы. Пожалуй, первые три варианта не могут использоваться в целях автоматизации (скажем, для организации опроса группы серверов с целью выявления времени их безотказной работы), так что давайте обратимся к WMI. Но первым делом нам нужно разобраться с тем, как WMI возвращает информацию по времени безотказной работы.

Выявление продолжительности безотказной работы системы с помощью WMI

Существует несколько способов извлечения сведений о времени безотказной работы компьютера средствами WMI. До появления на рынке технологии Windows PowerShell один из наиболее очевидных способов состоял в использовании команды WMIC. Например, таким образом:

wmic path Win32_OperatingSystem
   get LastBootUpTime

Эта команда считывает свойство LastBootUpTime размещенного на соответствующем компьютере экземпляра класса Win32_OperatingSystem. В среде PowerShell мы используем не WMIC, а команду Get-WmiObject:

Get-WmiObject Win32_OperatingSystem
   | Select-Object LastBootUpTime

Попробуйте запустить обе команды, и вы увидите, что результат не обязательно будет «удобочитаемым». Дело в том, что дата и время, выраженные в свойстве LastBootUpTime, представлены в виде строки даты и времени по модели CIM (Common Information Model). Например:

20160512154836.125599=360

Эта строка даты и времени читается так: 12 мая 2016 года, 15:48:36. Фрагмент 125599 — это число микросекунд (его мы игнорируем), а -360 представляет выраженное в минутах отклонение от стандартного времени по Гринвичскому меридиану (GMT). В нашем примере -360 означает «по времени GMT + 6 часов». Аналогичным образом +180 означало бы «на 3 часа раньше, чем по Гринвичу».

Чтобы пользоваться строкой даты и времени по CIM было удобнее, нам придется конвертировать ее в другой формат (объект DateTime). В среде PowerShell это сделать просто: статический метод ToDate

Time класса System.Management.ManagementDateTimeConverter.NET преобразует строку даты и времени по CIM в объект DateTime, который мы можем с легкостью использовать в оболочке PowerShell. Пример такого преобразования представлен на экране 1. Здесь переменная $dateTime содержит дату и время последней загрузки текущего компьютера. Чтобы получить время бесперебойной работы (вместо даты и времени последней загрузки), мы можем вычесть значение этой переменной из значения текущего времени (Get-Date), в результате чего получим объект TimeSpan, содержащий искомую разность. На экране 2 показан пример такой операции. Здесь первая команда создает объет TimeSpan, содержащий разность между текущей датой и временем, с одной стороны, и временем последней перезагрузки — с другой (объект $dateTime, созданный нами на экране 1). В результате выполнения второй команды мы получаем объект TimeSpan, и последняя команда с помощью оператора -f преобразует TimeSpan в удобочитаемую строку, извещающую нас о том, что данный компьютер бесперебойно функционирует в течение 4 дней 19 часов 56 минут и 44 секунд.

 

Преобразование времени последней перезагрузки компьютера в формат DateTime
Экран 1. Преобразование времени последней перезагрузки компьютера в формат DateTime

 

Преобразование времени последней загрузки компьютера в удобочитаемое время бесперебойной работы
Экран 2. Преобразование времени последней загрузки компьютера в удобочитаемое время бесперебойной работы

Определение времени последней перезагрузки системы с удаленного компьютера

Примеры на экранах 1 и 2 касаются локального компьютера. А как получить соответствующие показатели для удаленной системы? Инструментарий WMI дает нам возможность считывать информацию об удаленном компьютере с помощью параметра -ComputerName. Кроме того, WMI позволит нам указывать альтернативные учетные данные для удаленного компьютера в объекте PSCredential. На экране 3 представлен пример получения времени последней перезагрузки компьютера с именем server1. Первая команда на экране 3 передает объект PSCredential переменной $cred, а вторая команда получает строку даты и времени по CIM, содержащую время последней перезагрузки компьютера server1 (обратите внимание на параметры -ComputerName и -Credential). Третья команда преобразует время последней перезагрузки в объект DateTime, а последняя команда выдает время последней перезагрузки данного компьютера.

 

Получение времени последней перезагрузки удаленного компьютера
Экран 3. Получение времени последней перезагрузки удаленного компьютера

Соединяем все в сценарии Get-Uptime.ps1

Примеры на экранах 1 и 2 показывают, как получать время последней перезагрузки компьютера и преобразовывать его в удобочитаемую строку, а экран 3 демонстрирует, как получать время последней перезагрузки удаленного компьютера. Разумеется, нет нужды запоминать все нюансы, если мы инкапсулируем логику в удобном для чтения сценарии. Сценарий Get-Uptime.ps1 (см. листинг) делает за нас всю тяжелую работу. Его синтаксис таков:

Get-Uptime [-ComputerName
   [] [[-Credential] ]

Параметр -ComputerName используется факультативно и обозначает имена компьютера или компьютеров, сведения о времени бесперебойной работы которых вы хотите получить. Этот параметр обеспечивает взаимодействие с несколькими объектами, конвейерный ввод и использование объектов, обладающих свойством ComputerName. Имя параметра -ComputerName используется факультативно. Если вы опустите его, по умолчанию система будет предоставлять информацию о времени бесперебойной работы текущего компьютера.

Воспользовавшись параметром -Credential, вы получите альтернативные учетные данные на случай, если вам потребуется информация об одном или нескольких удаленных компьютерах, а учетная запись, используемая вами в данный момент, не предоставляет соответствующих прав доступа. Если запрос о продолжительности бесперебойной работы адресуется локальному компьютеру, вы можете не обладать правами администратора. Но если речь идет об удаленных компьютерах, такие права необходимы.

Сценарий генерирует объекты с тремя свойствами: ComputerName (имя компьютера), LastBootTime (объект DateTime, содержащий время последней перезагрузки компьютера) и Uptime (удобную для чтения строку, отображающую продолжительность бесперебойного функционирования).

Определение продолжительности безотказной работы нескольких компьютеров

На экране 4 показано, как определяется продолжительность безотказной работы трех удаленных компьютеров двумя различными способами. Первая команда определяет три компьютера как параметр для сценария Get-Uptime, а вторая определяет эти три компьютера как входные данные для конвейера. Отмечу, что результаты выполнения обеих команд идентичны (с тем лишь отличием, что содержащие данные о времени бесперебойной работы строки, сгенерированные второй командой, появляются чуть позже).

 

Определение продолжительности безотказной работы трех удаленных компьютеров двумя способами
Экран 4. Определение продолжительности безотказной работы трех удаленных компьютеров двумя способами

Поскольку сценарий допускает возможность конвейерного ввода, мы можем даже использовать выходные данные команды Get-ADComputer для вычисления продолжительности бесперебойной работы всех компьютеров организационного блока. Рассмотрим для примера следующую команду:

Get-ADComputer -Filter { Name -like "*" } `
  -SearchBase "OU=Servers,
  DC=fabrikam,DC=com" |
  Select-Object -ExpandProperty Name |
  Sort-Object |
  Get-Uptime

Я разместил команду на нескольких строках для удобства чтения. Эта команда извлекает имена всех компьютеров в организационной единице Servers OU, из данных о каждом компьютере отбирает только свойство Name (Select-Object -ExpandProperty), сортирует полученный список имен компьютеров и в итоге получает продолжительность бесперебойной работы для каждого компьютера.

Задача решается проще

Продолжительность бесперебойной работы вычислительной системы — важный показатель. Получить его можно различными способами, но простой способ в среде PowerShell не предусмотрен. Сценарий Get-Uptime.ps1 заполняет этот пробел и облегчает определение продолжительности бесперебойной работы компьютера вне зависимости от того, идет ли речь о данном компьютере или об удаленных системах.

Листинг. Сценари?й Get-Uptime.ps1
#requires -version 2

<#
.SYNOPSIS
Outputs the last bootup time and uptime for one or more computers.

.DESCRIPTION
Outputs the last bootup time and uptime for one or more computers.

.PARAMETER ComputerName
One or more computer names. The default is the current computer. Wildcards are not supported.

.PARAMETER Credential
Specifies credentials that have permission to connect to the remote computer. This parameter is ignored for the current computer.

.OUTPUTS
PSObjects containing the computer name, the last bootup time, and the uptime.
#>

[CmdletBinding()]
param(
  [parameter(ValueFromPipeline=$true,ValueFromPipelineByProperty
  Name=$true)]
    $ComputerName,
  [System.Management.Automation.PSCredential]
    $Credential
)

begin {
  function Out-Object {
    param(
      [System.Collections.Hashtable[]] $hashData
    )
    $order = @()
    $result = @{}
    $hashData | ForEach-Object {
      $order += ($_.Keys -as [Array])[0]
      $result += $_
    }
    New-Object PSObject -Property $result | Select-Object $order
  }
  function Format-TimeSpan {
    process {
      "{0:00} d {1:00} h {2:00} m {3:00} s" -f $_.Days,$_.Hours,
      $_.Minutes,$_.Seconds
    }
  }
  function Get-Uptime {
    param(
      $computerName,
      $credential
    )
    # In case pipeline input contains ComputerName property
    if ( $computerName.ComputerName ) {
      $computerName = $computerName.ComputerName
    }
    if ( (-not $computerName) -or ($computerName -eq ".") ) {
      $computerName = [Net.Dns]::GetHostName()
    }
    $params = @{
      "Class" = "Win32_OperatingSystem"
      "ComputerName" = $computerName
      "Namespace" = "root\CIMV2"
    }
    if ( $credential ) {
      # Ignore -Credential for current computer
      if ( $computerName -ne [Net.Dns]::GetHostName() ) {
        $params.Add("Credential", $credential)
      }
    }
    try {
      $wmiOS = Get-WmiObject @params -ErrorAction Stop
    }
    catch {
      Write-Error -Exception (New-Object $_.Exception.GetType().FullName `
        ("Cannot connect to the computer ‘$computerName’ due to the
        following error: ‘$($_.Exception.Message)’",
        $_.Exception))
      return
    }
    $lastBootTime = [Management.ManagementDateTimeConverter]::
    ToDateTime($wmiOS.LastBootUpTime)
    Out-Object `
      @{"ComputerName" = $computerName},
      @{"LastBootTime" = $lastBootTime},
      @{"Uptime"       = (Get-Date) - $lastBootTime | Format-TimeSpan}
  }
}

process {
  if ( $ComputerName ) {
    foreach ( $computerNameItem in $ComputerName ) {
      Get-Uptime $computerNameItem $Credential
    }
  }
  else {
    Get-Uptime "."
  }
}