Timeout in Seconds for the Power Management Action
VerboseOutput
bool
$Config/VerboseOutput$
Verbose Output
Display Verbose Output from task.
Source Code:
<WriteActionModuleType ID="Fujitsu.Servers.PRIMERGY.OutOfBand.BmcPowerManagementWriteActionModuleType" Accessibility="Internal" Batching="false">
<Configuration>
<IncludeSchemaTypes>
<SchemaType>Windows!Microsoft.Windows.PowerShellSchema</SchemaType>
</IncludeSchemaTypes>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="UserName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="Password" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="IP" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="Port" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="ServerAction" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="SkipCACheck" type="xsd:boolean"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="SkipCNCheck" type="xsd:boolean"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="0" name="UseRedfishSession" type="xsd:boolean" default="false"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="0" name="VerboseOutput" type="xsd:boolean" default="false"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="1" name="TimeoutSeconds" type="xsd:integer"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" minOccurs="0" name="StrictErrorHandling" type="xsd:boolean"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int"/>
<!--
<OverrideableParameter ID="UseRedfishSession" Selector="$Config/UseRedfishSession$" ParameterType="bool" />
-->
<OverrideableParameter ID="VerboseOutput" Selector="$Config/VerboseOutput$" ParameterType="bool"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<WriteAction ID="WA" TypeID="Windows!Microsoft.Windows.PowerShellWriteAction">
<ScriptName>PowerManagementBmc.ps1</ScriptName>
<ScriptBody><Script>
##################################################################################
# #
# NOTICE #
# #
# COPYRIGHT 2015 - 2018 FUJITSU LIMITED #
# ALL RIGHTS RESERVED #
# #
# This computer program is CONFIDENTIAL and contains TRADE SECRETS of #
# FUJITSU LIMITED. The receipt or possession of this program does #
# not convey any rights to reproduce or disclose its contents, or to #
# manufacture, use, or sell anything that it may describe, in whole or #
# in part, without the specific written consent of FUJITSU LIMITED. #
# Any reproduction of this program without the express written consent #
# of FUJITSU LIMITED is a violation of the copyright laws and may #
# subject you to civil liability and criminal prosecution. #
# #
##################################################################################
#
# This script performs Out-of-Band Power Management for Fujitsu PRIMERGY Servers via the integrated Remote Management Controller (iRMC)
#
param(
[string]$UserName,
[string]$Password,
[string]$IP,
[int] $Port,
[string]$ServerAction,
[string]$SkipCACheck = "False",
[string]$SkipCNCheck = "False",
[string]$UseRedfishSession = "False",
[string]$VerboseOutput = "False",
[int] $TimeoutSeconds = 60
)
[IPAddress]$IPAddress = $null
if ([system.net.IPAddress]::tryparse($IP,[ref]$IPAddress)) {
if ($IPAddress.AddressFamily.ToString() -eq "Internetwork") {
$HostURL = "https://" +$IPAddress.ToString() +":$($Port)"
} elseif ($IPAddress.AddressFamily.ToString() -eq "Internetworkv6") {
$HostURL = "https://[" +$IPAddress.ToString() +"]:$($Port)"
}
} else {
Write-Warning ("Fujitsu Out-Of-Band Server Power Management ($($ServerAction)): Invalid IP Address '$($IP)'")
Exit -1
}
$webRequest = [System.Net.WebRequest]::Create($url)
if ($Credentials -ne $Null) {
if ($AuthType -eq "Basic") {
# Do not wait for the 401 response, send the credentials with the initial request
$AuthData = [Convert]::ToBase64String([Text.Encoding]::Default.GetBytes($Credentials.UserName + ':' + $Credentials.Password));
$webRequest.Headers.Add('Authorization', "Basic $AuthData")
$webRequest.Timeout = ($RequestTimeout *1000)
$webRequest.ReadWriteTimeout = ($RequestTimeout *1000)
$webRequest.Method = $Method
# $webRequest.Accept = "application/json"
$webRequest.Accept = "*/*"
$webRequest.KeepAlive = $False # make sure the connection is not re-used
$webRequest.AllowAutoRedirect = $False # Do not follow redirects
$webRequest.Headers.Add("OData-Version", "4.0")
# Add only if there is no regular credential
if ($XAuthData.Length -gt 0) {
$webRequest.Headers.Add("X-Auth-Token", $XAuthData)
}
# Add only if present
if ($Etag.Length -gt 0) {
$webRequest.Headers.Add("If-Match", $Etag)
}
# Discovery and Monitoring workflow scripts typically run without a proxy configured.
# Performance Collection workflow scripts typically run with proxy configuration
$Proxy = $WebRequest.Proxy
if ($Proxy) {
$ProxyBypassed = $Proxy.IsBypassed($url)
if ($ProxyBypassed) {
Write-Verbose "$IP - '$($url)' No Proxy (direct connection) will be used"
} else {
$ProxyAddress = $Proxy.GetProxy($url).AbsoluteUri
Write-Verbose "$IP - '$($url)' Proxy '$($ProxyAddress)' will be used"
}
} else {
Write-Verbose "$IP - '$($url)' No Proxy object (direct connection)"
}
if (!$ProxyBypassed -and $ToggleProxy ) {
DebugOut "$IP - '$($url)' (Re)Trying without Proxy..."
$webRequest.Proxy = $Null
$ToggleProxy = $False
}
try {
$webRequest.ServicePoint.ConnectionLimit = 128
$webRequest.ServicePoint.ConnectionLeaseTimeout = 0 # Close Connection after servicing a request
$webRequest.ServicePoint.MaxIdleTime = 1 # in MilliSeconds, close connection afterwards
$webRequest.ServerCertificateValidationCallback = $SslCertificateValidator
# Write-Host "$IP - '$url' HASH=$($webRequest.ServicePoint.GetHashCode()) ConnectionLimit=$($webRequest.ServicePoint.ConnectionLimit)"
} catch {
Write-Warning "$IP - '$url' - Could not set extended config Exception=$_"
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = $SslCertificateValidator
}
if ($RequestData.Length -gt 0) {
$webRequest.ContentType = $ContentType
if ($bSendError) {
# If the connection gets closed during early send we retry
# this is typically within a few seconds, otherwise the configured timeout would have expired
if ($reqTime -ne $Null -and [int]$reqTime.TotalSeconds -lt 5) {
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
if ($errorMsg -ne $Null) { Write-Warning "$IP - $($Method) '$url' could not send request data within $($reqTime.TotalSeconds) seconds - will retry in $retryDelay seconds ($errorMsg)"}
else { Write-Warning "$IP - $($Method) '$url' could not send request data within $($reqTime.TotalSeconds) seconds - will retry in $retryDelay seconds (no details available)"}
if ($innerException -ne $Null) { Write-Warning "InnerException:`r`n$innerException" }
if ($stackTrace -ne $Null ) { Write-Warning "Stack Trace:`r`n$stackTrace" }
Start-Sleep -Seconds $retryDelay
continue
} else {
Write-Warning "$IP - $($Method) '$url' could not send request data within $($reqTime.TotalSeconds) seconds. Timeout was $($RequestTimeout) seconds"
return
}
}
}
[System.Net.WebHeaderCollection]$Headers = $webResponse.Headers
if ($Headers) {
[string]$Header = $Headers.Get("X-Auth-Token")
if ($Header.Length -gt 0) {
$script:AuthData = $Header
}
# POST/PUT etc. return created object in 'Location' header, also redirects
if ($Method -ne "GET") {
$Header = $Headers.Get("Location")
if ($Header.Length -gt 0) {
$script:Location = $Header
}
}
}
# If we get here some other error occurred (e.g. 503 Service unavailable)
# If this was the first attempt and a proxy was active (e.g. performance collection workflow script), try again without proxy
if (!$ProxyBypassed -and $retries -eq 0) {
DebugOut "$IP - $($Method) '$url' iRMC reported HTTP Status Code ($([int]$script:httpStatusCode) / $($script:httpStatusCode)) - will retry without proxy"
$ToggleProxy = $True
continue
}
if ($retries -lt ($MAX_REQUEST_RETRIES)) {
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
DebugOut "$IP - $($Method) '$url' iRMC reported HTTP Status Code (($([int]$script:httpStatusCode) / $($script:httpStatusCode)) - will retry in $($retryDelay) seconds"
Start-Sleep -Seconds $retryDelay
continue
} else {
DebugOut "$IP - $($Method) '$url' - iRMC reported final HTTP Status Code (($([int]$script:httpStatusCode) / $($script:httpStatusCode)), giving up after retries"
return
}
} else {
# Check for SSL related errors
if ($script:SSL_CN_ERROR -eq $True) {
Write-Warning "$IP - '$url' Certificate Name (CN) Mismatch ..."
return
} elseif ($script:SSL_CA_ERROR -eq $True) {
Write-Warning "$IP - '$url' Certificate Authority (CA) or Chain Error ..."
return
} elseif ($script:SSL_NO_CERT_ERROR -eq $True) {
Write-Warning "$IP - '$url' No Certificate present ..."
return
}
# no response (object), check for the timeout value.
# If the connection gets closed during early send/receive,
# this is typically within a few seconds, otherwise the configured timeout would have expired
if ($resTime -ne $Null -and [int]$resTime.TotalSeconds -lt 5) {
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
if ($retries -ne 0) {
if ($errorMsg -ne $Null) { Write-Warning "$IP - $($Method) '$url' no response / connection closed within $($resTime.TotalSeconds) seconds - will retry in $retryDelay seconds ($errorMsg)"}
else { Write-Warning "$IP - $($Method) '$url' no response / connection closed within $($resTime.TotalSeconds) seconds - will retry in $retryDelay seconds (no details available)"}
if ($innerException -ne $Null) { Write-Warning "InnerException:`r`n$innerException" }
if ($stackTrace -ne $Null ) { Write-Warning "Stack Trace:`r`n$stackTrace" }
}
Start-Sleep -Seconds $retryDelay
} else {
Write-Host "$IP - $($Method) '$url' no response within $($resTime.TotalSeconds) seconds. Timeout was $($RequestTimeout) seconds"
return
}
}
} catch {
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
Write-Warning "$IP - $($Method) '$url' Generic Exception. will retry in $retryDelay seconds. Exception=$_"
Start-Sleep -Seconds $retryDelay
}
}
Write-Host "$IP - $($Method) '$url' no response after $retries retries (last error: $errorMsg)"
}
Function CloseRedfishSession
{
# Close Redfish session if we opened one
if ( ![String]::IsNullOrEmpty($script:RedfishSession) ) {
try {
DebugOut "$IP - Delete Redfish session '$($script:RedfishSession)'"
(doWebRequest -url "$hostURL$($script:RedfishSession)" -Method "DELETE" -XAuthData $script:AuthData) | Out-Null
} catch {}
}
}
$ServiceRoot = $Null
DebugOut "$IP - Get Redfish Root Service Information..."
try {
# ServiceRoot is available unauthenticated
# Note: According to the specification the ServiceRoot should be available unauthenticated;
# but older firmware versions (e.g. iRMC S4 with firmware 8.4x) do not support this or do not know this URL.
# In order to properly detect these systems (and abort), use the provided/available credentials
#$ServiceRoot = (doWebRequest -url "$hostURL/redfish/v1" -Method "GET" -Credentials $Null -XAuthData $Null) | ConvertFrom-JSON
$ServiceRoot = (doWebRequest -url "$hostURL/redfish/v1" -Method "GET" -Credentials $NetworkCredential -XAuthData $Null) | ConvertFrom-JSON
if ($ServiceRoot) {
DebugOut "$IP - Name='$($ServiceRoot.Name)' Redfish Version='$($ServiceRoot.RedfishVersion)' UUID='$($ServiceRoot.UUID)'"
if ($ServiceRoot.'@odata.etag' -eq $Null -or $ServiceRoot.'@odata.etag' -eq '') {
DebugOut "$IP - Seems to be an old Redfish instrumentation, giving up..."
return
}
} else {
# Since this Monitor runs for discovered instances, this is serious
DebugWarn "$IP - Could not get Service Root, giving up."
return
}
} catch {
DebugWarn "$IP - Error getting Service Root, Exception: $_"
return
}
try {
$script:RedfishSession = $Null
if ($UseRedfishSession -eq $True) {
DebugOut "$IP - try to create a new Redfish session..."
$SessionEntry = (doWebRequest -url "$hostURL$($ServiceRoot.Links.Sessions.'@odata.id')" -Credentials $Null -XAuthData $Null -Method "POST" -RequestData "{'UserName':'$($UserName)','Password':'$($Password)'}" ) | ConvertFrom-JSON
if (!$SessionEntry) {
Write-Warning "$IP - Could not create session"
return
}
if ($SessionEntry.'@odata.id' -ne '') {
#DebugOut "`tRedfish Session for '$($SessionEntry.UserName)' Id=$($SessionEntry.Id) '$($SessionEntry.'@odata.id')'"
DebugOut "`tRedfish Session Id=$($SessionEntry.Id) '$($SessionEntry.'@odata.id')'"
$script:RedfishSession = $SessionEntry.'@odata.id'
} else {
#DebugWarn "`tRedfish Session for '$($SessionEntry.UserName)' Id=$($SessionEntry.Id) - no link"
DebugWarn "`tRedfish Session for '$($SessionEntry.UserName)' Id=$($SessionEntry.Id) - no link"
}
}
DebugOut "$IP - Get System(s) Information ..."
$Systems = $Null
if ($ServiceRoot -and $ServiceRoot.Systems.'@odata.id') {
$Systems = (doWebRequest -url "$hostURL$($ServiceRoot.Systems.'@odata.id')" -Method "GET" -Credentials $NetworkCredential -XAuthData $script:AuthData) | ConvertFrom-JSON
if ($Systems -and $Systems.'[email protected]' -gt 0) {
if ($Systems.'[email protected]' -gt 1) {
DebugOut "$IP - has Information for $($Systems.'[email protected]') Systems ..."
}
for ($SystemIdx = 0; $SystemIdx -lt $Systems.'[email protected]'; $SystemIdx++) {
# Note: Modeling multiple systems under one IP Address is currently not planned
if ($Systems.'[email protected]' -gt 1) {
DebugOut "$IP - System $($SystemIdx) : Information..."
} else {
DebugOut "$IP - System Information..."
}
$SystemRoot = (doWebRequest -url "$hostURL$($Systems.Members[$SystemIdx].'@odata.id')" -Method "GET" -Credentials $NetworkCredential -XAuthData $script:AuthData) | ConvertFrom-JSON
if ($SystemRoot) {
# Get iRMC Firmware Version info
if ($SystemRoot.Oem.ts_fujitsu) {
$FirmwareInventory = $SystemRoot.Oem.ts_fujitsu.FirmwareInventory
if ($FirmwareInventory -ne $Null -and $FirmwareInventory.'@odata.id' -ne '') {
$Inventory = (doWebRequest -url "$hostURL$($FirmwareInventory.'@odata.id')" -Method "GET" -Credentials $NetworkCredential -XAuthData $script:AuthData) | ConvertFrom-JSON
if ($Inventory) {
DebugOut "$IP - iRMC Firmware='$($Inventory.BMCFirmware)' BuildDate='$($Inventory.BMCFirmwareBuildDate)' Running='$($Inventory.BMCFirmwareRunning)' SDR Version='$($Inventory.SDRRVersion)' SDRRId='$($Inventory.SDRRId)' BIOS='$($Inventory.SystemBIOS)'"
}
}
}
DebugOut "$IP - is a $($SystemRoot.Manufacturer) '$($SystemRoot.Model)' Description='$($SystemRoot.Description)' AssetTag='$($SystemRoot.AssetTag)' Serial='$($SystemRoot.SerialNumber)' PartNumber='$($SystemRoot.PartNumber)'"
DebugOut "$IP - System HostName='$($SystemRoot.HostName)' UUID='$($SystemRoot.UUID)' Indicator LED is '$($SystemRoot.IndicatorLED)'"
DebugOut "$IP - System Status: Health='$($SystemRoot.Status.Health)' State='$($SystemRoot.Status.State)' HealthRollup='$($SystemRoot.Status.HealthRollup)' PowerState='$($SystemRoot.PowerState)'"
Write-Host "$IP - Current System Power State is '$($SystemRoot.PowerState)'"
# We need to validate the action against the currently allowed ones.
# See ResetType in Resource.json (Note: No PowerCycle defined in Standard Redfish enum)
<#
"ResetType": {
"type": "string",
"enum": [
"On",
"ForceOff",
"GracefulShutdown",
"GracefulRestart",
"ForceRestart",
"Nmi",
"ForceOn",
"PushPowerButton"
],
"enumDescriptions": {
"On": "Turn the system on.",
"ForceOff": "Turn the system off immediately (non-graceful) shutdown.",
"GracefulShutdown": "Perform a graceful system shutdown and power off.",
"GracefulRestart": "Perform a graceful system shutdown followed by a restart of the system.",
"ForceRestart": "Perform an immediate (non-graceful) shutdown, followed by a restart of the system.",
"Nmi": "Generate a Diagnostic Interrupt (usually an NMI on x86 systems) to cease normal operations, perform diagnostic actions and typically halt the system.",
"ForceOn": "Turn the system on immediately.",
"PushPowerButton": "Simulate the pressing of the physical power button on this system."
}
#>
# PowerCycle is available only as OEM action, not as standard Redfish action
$OemResetAction = $SystemRoot.Actions.Oem.'http://ts.fujitsu.com/redfish-schemas/v1/FTSSchema.v1_0_0#FTSComputerSystem.Reset'
if ($OemResetAction) {
DebugOut "$IP - OEM Reset Action '$($OemResetAction.Title)' Target='$($OemResetAction.Target)' "
$tmpStr = "$IP - Allowable OEM System Reset Action(s): "
for ($i=0; $i -lt $OemResetAction.'[email protected]'.Count; $i++) {
if ($i -ne 0) {$tmpStr += ", " }
$tmpStr += "'$($OemResetAction.'[email protected]'[$i])'"
}
DebugOut $tmpStr
CloseRedfishSession
exit 0
}
}
# if we get here, the PowerCycle was not allowed in the current state
Write-Host "$IP - $($ServerAction) is not an allowed OEM System ResetAction in the current state`r$($tmpStr)"
CloseRedfishSession
exit -1
}
}
$ResetAction = $SystemRoot.Actions.'#ComputerSystem.Reset'
if ($ResetAction) {
DebugOut "$IP - Standard Reset Action '$($ResetAction.Title)' Target='$($ResetAction.Target)' "
$tmpStr = "$IP - Allowable System Reset Action(s): "
for ($i=0; $i -lt $ResetAction.'[email protected]'.Count; $i++) {
if ($i -ne 0) {$tmpStr += ", " }
$tmpStr += "'$($ResetAction.'[email protected]'[$i])'"
}
DebugOut $tmpStr
}
CloseRedfishSession
exit 0
}
}
# if we get here, the ResetAction was not allowed in the current state
Write-Host "$IP - $($ServerAction) is not an allowed Standard System ResetAction in the current state`r$($tmpStr)"
CloseRedfishSession
exit -1
}
} else {
DebugOut "$IP - Could not get System Root"
CloseRedfishSession
return
}
}
}
}
if ($ret -ne $Null) {
try {
[xml]$x = [xml]$ret
if ($x -ne $Null -and $x.HasChildNodes) {
if ($x.Status.Value -eq 0) {
Write-Host "Success`rDetails:`r$ret"
exit 0
} else {
Write-Warning "Failed to $($ServerAction) $IP (returned status is not OK)`rDetails:`r$ret"
}
} else {
Write-Warning "Failed`rThe returned response from the iRMC is not a valid XML document`rDetails:`r$ret"
}
} catch {
Write-Warning "Failed`rThe returned response from the iRMC is not a valid XML document`rDetails:`r$ret"
}
} else {
Write-Host -ForegroundColor Red "$IP - Failed to $($ServerAction) the server (no response)"
}
# Throw "Failed to $($ServerAction) $IP"
exit -1
}