<ProbeActionModuleType ID="Fujitsu.Servers.PRIMERGY.OutOfBand.iRMC.ScanForDevices.Probe" Accessibility="Public">
<Configuration>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="IPList" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Port" type="xsd:int"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SkipCACheck" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="SkipCNCheck" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="UserName" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Password" type="xsd:string"/>
<xsd:element xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="TimeoutSeconds" type="xsd:int"/>
</Configuration>
<OverrideableParameters>
<OverrideableParameter ID="IPList" Selector="$Config/IPList$" ParameterType="string"/>
<OverrideableParameter ID="Port" Selector="$Config/Port$" ParameterType="string"/>
<OverrideableParameter ID="SkipCACheck" Selector="$Config/SkipCACheck$" ParameterType="string"/>
<OverrideableParameter ID="SkipCNCheck" Selector="$Config/SkipCNCheck$" ParameterType="string"/>
<OverrideableParameter ID="UserName" Selector="$Config/UserName$" ParameterType="string"/>
<OverrideableParameter ID="Password" Selector="$Config/Password$" ParameterType="string"/>
<OverrideableParameter ID="TimeoutSeconds" Selector="$Config/TimeoutSeconds$" ParameterType="int"/>
</OverrideableParameters>
<ModuleImplementation Isolation="Any">
<Composite>
<MemberModules>
<ProbeAction ID="PA" TypeID="Windows!Microsoft.Windows.PowerShellPropertyBagProbe">
<ScriptName>iRMC.ScanForDevices.ps1</ScriptName>
<ScriptBody><Script>
##################################################################################
# #
# NOTICE #
# #
# COPYRIGHT 2015 - 2017 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. #
# #
##################################################################################
# set CONSTANT variables (can not be changed):
set-variable -name PoShScriptName -value $ScriptName -option constant
[int]$ERROR_UNSUPPORTED_FIRMWARE = 8009 # a prime
[int]$SCRIPT_EVENT_NUMBER = 8117 # a prime
[int]$ERROR_NO_STATUS_INFORMATION = 8099 # not a prime
[int]$ERROR_NO_USERNAME = 8199 # not a prime
$global:scriptMG = "."
$global:scriptWorkflow = "Fujitsu.Servers.PRIMERGY.OutOfBand.iRMC.ScanForDevices.Task"
# Pool Instance this task is running on
$global:scriptInstanceName = "$Target/Property[Type='System!System.Entity']/DisplayName$"
$global:scriptInstanceId = "$Target/Id$"
$script:ErrorActionPreference = "SilentlyContinue"
trap {
LogWorkflowEvent $WARNING_LEVEL 19001 ("iRMC Template exception while scanning IP list.`r`nIP List: '$($IPList)'`r`nException: [$($_.Exception.Message)]")
continue
}
# we need to make multiple authenticated HTTP(s) requests...
[System.Net.NetworkCredential]$NetworkCredential = New-Object System.Net.NetworkCredential ($username, $password)
[System.Net.ServicePointManager]::DefaultConnectionLimit = 1000
[xml]$xml = New-Object XML
# Strip any potential garbage at the and of the XML string
Function Clean-XmlString ([string]$xmlString, [string]$endTag) {
if ($xmlString.contains( $endTag )) {
$xmlString.TrimEnd( $xmlString.Substring( ($xmlString.IndexOf($endTag) +$endTag.Length) ))
} else {
$xmlString
}
}
# Due to various problems we support iRMC S4 firmware 8.2x and later (e.g. 8.23F)
# Note: internal pre-releases are marked as 96.xx / 97.xx / 98.xx / 99.xx and are considered unsupported
Function FirmwareVersionSupported ([string]$Version)
{
# iRMC S4
if ( ($Version -match "^8.([2-9][0-9])") -or ($Version -match "^9.([0-9][0-9])") -or ($Version -match "^[1-8][0-9].([0-9][0-9])") ) {
return $True
}
# Interim iRMC S5 Version without access to the System Report at the defined URL
if ( $Version -match "^1.03" ) {
return $False
}
# iRMC S5 re-started version numbering from 1.xx
if ( $Version -match "^1.([0-9][0-9])" ) {
return $True
}
# Form up the URL
[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 {
DebugOut "$IP - iRMC Template Discovery: Invalid IP Address '$($IP)'"
continue
}
$ServerUuid = "" # from the HP-SIM Integration data
$ServerAssetTag = "" # from exported configuration
$ServerType = 'N/A'
$SerialNumber = 'N/A'
$BmcFirmwareVersion = 'N/A'
$BmcType = 'N/A'
$global:WebServer = ""
# No HP-SIM Response - Get overall Status / System Report
$Response = DoWebRequest -url ($hostURL + "/report.xml?Item=System/Status") -Method "GET" -Credentials $NetworkCredential -RequestTimeout $DEFAULT_REQUEST_TIMEOUT
if ($global:WebServer -match "iRMC") {
$iRMCDevicesCount++
if (($global:WebServer -eq "iRMC Webserver") -or ($global:WebServer -match "iRMC S[2,3]")) {
DebugOut "$IP - Skipping unsupported iRMC Generation / Web Server '$($global:WebServer)'"
continue
}
$BmcType = ($global:WebServer).SubString(0,7)
} else {
DebugOut "$IP - Unsupported Web Server '$($global:WebServer)' or no response - skipping"
continue
}
if ($Response -ne $Null -and $Response.Contains("<Root") ) {
try {
$xml = [xml] ( Clean-XmlString -xmlString $Response -endTag "</Root>" )
if ($xml -ne $Null -and $xml.HasChildNodes ) {
$BmcFirmwareVersion = $xml.Root.GetAttribute("Version")
if ( (FirmwareVersionSupported -Version $BmcFirmwareVersion) -eq $False ) {
LogScriptEventWithEventSource -EventSource $IP -EventLevel $INFO_LEVEL -EventNumber $ERROR_UNSUPPORTED_FIRMWARE -Message ("$($BmcType) Unsupported Firmware $($BmcFirmwareVersion)")
DebugOut "$IP - iRMC Template Discovery: Unsupported Firmware $($BmcFirmwareVersion)"
continue
}
}
} catch {
DebugErr $SCRIPT_EVENT_NUMBER "$IP - iRMC Template Discovery: Could not process System Report Status information, skipping IP. Exception=$_"
continue
}
} else {
# No System Report Status response - skip IP (unsupported iRMC or invalid credentials)
DebugOut "$IP - iRMC Template Discovery: no System Report Response StatusCode=$([int]$global:httpStatusCode) - skipping..."
if ( [int]$global:httpStatusCode -eq $HTTP_RC_UNAUTHORIZED ) {
$NoAccessDevices++
}
continue
}
# Add this iRMC to the return List
if($returnIPList -ne "") { $returnIPList += ";" }
$returnIPList += "$($IP),$($BmcType),$($BmcFirmwareVersion),$($ServerType),$($SerialNumber),$($ServerAssetTag),$($ServerUuid)"
}
IP Addresses probed: [$DevicesProbedCount]
Identified as Fujitsu iRMC: [$iRMCDevicesCount]
Supported iRMC devices: [$SupportedDevices]
No Access / invalid credentials: [$NoAccessDevices]
Not responding / unknown: [$UnknownCount]
"@
}
# This script part contains helper functions to perform web based requests
# and is embedded into the final script via Visual Studio Authoring Extensions
[int]$ERROR_SSL_CA_ERROR = 8399 # is not a prime
[int]$ERROR_SSL_CN_ERROR = 8499 # is not a prime
[int]$ERROR_BMC_NO_RESPONSE = 8599 # is a prime
[int]$ERROR_BMC_NO_ACCESS = 8699 # is a prime
[int]$ERROR_BMC_TIMEOUT = 8799 # is not a prime
[int]$ERROR_BMC_NO_PERMISSION = 8899 # is not a prime
[int]$ERROR_BMC_BUSY = 8999 # is a prime
$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")
# If we get here some other error occurred
# 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 - '$url' iRMC reported HTTP Status Code ($([int]$global:httpStatusCode) / $($global:httpStatusCode)) - will retry without proxy"
$ToggleProxy = $True
continue
}
if ($retries -lt ($MAX_REQUEST_RETRIES)) {
DebugOut "$IP - '$url' iRMC reported HTTP Status Code (($([int]$global:httpStatusCode) / $($global:httpStatusCode)) - will retry in $($retryDelay) seconds"
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
Start-Sleep -Seconds $retryDelay
continue
} else {
DebugErr $ERROR_BMC_BUSY "$IP - '$url' - iRMC reported final HTTP Status Code (($([int]$global:httpStatusCode) / $($global:httpStatusCode)), giving up after retries"
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) {
# LogScriptEventWithEventSource -EventSource $IP -EventLevel $WARNING_LEVEL -EventNumber $ERROR_BMC_NO_RESPONSE -Message ("'$url' no response / connection closed ")
if ($errorMsg -ne $Null) { DebugWarn "$IP - '$url' no response / connection closed within $($resTime.TotalSeconds) seconds - will retry in $retryDelay seconds ($errorMsg)"}
else { DebugWarn "$IP - '$url' no response / connection closed within $($resTime.TotalSeconds) seconds - will retry in $retryDelay seconds (no details available)"}
if ($innerException -ne $Null) { DebugWarn "InnerException:`r`n$innerException" }
if ($stackTrace -ne $Null ) { DebugWarn "Stack Trace:`r`n$stackTrace" }
}
Start-Sleep -Seconds $retryDelay
} else {
# Note: do no report actual timeout seconds here, or alert suppression will not fully work due to different text
if (!$IgnoreTimeout.IsPresent) {
LogScriptEventWithEventSource -EventSource $IP -EventLevel $WARNING_LEVEL -EventNumber $ERROR_BMC_TIMEOUT -Message ("'$url' no response. Timeout was $RequestTimeout seconds")
}
DebugOut "$IP - '$url' no response within $($resTime.TotalSeconds) seconds. Timeout was $RequestTimeout seconds"
return
}
}
} catch {
# LogScriptEventWithEventSource -EventSource $IP -EventLevel $WARNING_LEVEL -EventNumber $SCRIPT_EVENT_NUMBER -Message ("'$url' generic Exception=$_")
[int]$retryDelay = (($retries +1) * $DEFAULT_RETRY_INCREMENT)
DebugWarn "$IP - '$url' generic Exception. will retry in $retryDelay seconds. Exception=$_"
Start-Sleep -Seconds $retryDelay
}
}
if (!$IgnoreTimeout.IsPresent) {
LogScriptEventWithEventSource -EventSource $IP -EventLevel $WARNING_LEVEL -EventNumber $ERROR_BMC_NO_RESPONSE -Message ("'$url' no response after $retries retries (last error: $errorMsg)")
}
DebugOut "$IP - '$url' no response after $retries retries (last error: $errorMsg)"
}
# This script part contains helper functions to perform various logging activities
# and is embedded into the final script via Visual Studio Authoring Extensions
[int]$ERROR_TRACEFILE_XML_PARSE_ERROR = 8299 # is not a prime
# Log an event into the registry, Source will be 'Health Service Script'
# See https://msdn.microsoft.com/en-us/library/bb437630.aspx
[int]$WARNING_LEVEL = 2
[int]$ERROR_LEVEL = 1
[int]$INFO_LEVEL = 0
$LOGFILE_VERSION = "8.3.1.0"
# Generic version
Function RaiseEvent {
Param (
[parameter(Mandatory=$true)]
[string]$EventSource = "Fujitsu Out-Of-Band",
[parameter(Mandatory=$true)]
[int]$EventLevel,
[parameter(Mandatory=$true)]
[int]$EventNumber,
[parameter(Mandatory=$true)]
[string]$Message
)
# Backwards compatible wrapper
Function LogScriptEvent {
Param (
[parameter(Mandatory=$true)]
[ValidateRange(0,2)]
[int]$EventLevel,
[parameter(Mandatory=$true)]
[int]$EventNumber,
[parameter(Mandatory=$true)]
[string]$Message
)
# Note: Log will be written always with Event Source 'Health Service Script'
if ($ScriptApi -ne $Null) {
# Note: do not use actual script name to consolidate Alert Suppression from parallel scripts
$ScriptApi.LogScriptEvent("Fujitsu Out-Of-Band", $EventNumber, $EventLevel, $Message)
# $ScriptApi.LogScriptEvent($PoShScriptName, $EventNumber, $EventLevel, $Message)
}
}
# Similar, but with different Source
Function LogWorkflowEvent($eventType, $eventNumber, $message)
{
$channel = "Operations Manager"
$source = "Health Service Modules Ex"
$eventLog = new-object System.Diagnostics.EventLog -ArgumentList @($channel)
$eventLog.Source = $source
# --------------------------------------------------------------------------
# Global variables = variables, which are changed in different functions ...
# ... and the changed value shall be available in the calling function
# --------------------------------------------------------------------------
$global:DebugMode = $False
$global:DebugFile = $False
$global:OverWrite = $True
$global:DebugHosts = ""
$global:DebugForHost = "$False"
$global:ErrFilePrefix = "ERRORTrace"
$global:WarnFilePrefix = "WARNINGTrace"
$global:LogTargetName = ""
$global:LogFilePrefix = ""
$global:LogFilePath = "$Env:TEMP\SVISCOM\SVISCOM-OutOfBand"
$global:LogFileName = "$LogFilePath\$($LogFilePrefix).log"
# create the target directory, if it does not exist
if ( ! (Test-Path -Path $global:LogFilePath)) {
New-Item -ItemType directory -Path $global:LogFilePath | Out-Null
}
if (Test-Path -Path $global:LogFilePath) {
$pathOK = $True
}
if (Test-Path -Path $Xm_FileName) {
$txt = Get-Content $Xm_FileName
foreach ($line in $txt) {
if ($line.contains($LOGFILE_VERSION)) {
$fileOK = $True
break
}
}
}
if (($pathOK -eq $True) -and ($fileOK -eq $False)) {
# we write a new SVISCOM-OutOfBand.xm_ file every time the MP is changed to make sure all
# INI-Values are documented for use by the customer if anything changes.
if (Test-Path -Path $Xm_FileName) {
Remove-Item -Path $Xm_FileName -Force | Out-Null
}
#Create xm_ file
New-Item -Path $Xm_FileName -ItemType File | Out-Null
Add-Content -Path $Xm_FileName -Value @"
<$SectionRoot>
<!--
$($SVISCOMLogXmlName) Debug XML file Version $($LOGFILE_VERSION)
With this file logging for PowerShell scripts within the
- Fujitsu Out-Of-Band Management Pack and
- Optional Extension Management Packs for the Fujitsu Out-Of-Band Management Pack
can be enabled.
Rename the file type from '.xm_' to '.xml' to enable reading this file.
Note: You have to enable debug for a script and also select the server in
the '<$TagHostsDiscovery>' or '<$TagHostsMonitoring>' section to generate traces (see below).
The following sections specify for which PowerShell scripts the traces will be generated;
Each of the sections represents a single PowerShell script.
'<$TagDebugMode>' enables logging (yes) or disables logging (no)
'<$TagOverWrite>' defines continuous logging (no) or single script run logging (yes)
-->
<!-- DISCOVERIES -->
<!-- The following section enables trace files for the Out-Of-Band iRMC Device discovery script -->
<$SectioniRMCDiscovery>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectioniRMCDiscovery>
<!-- The following section enables trace files for the Out-Of-Band Server discovery script -->
<$SectionServerDiscovery>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectionServerDiscovery>
<!-- MONITORS -->
<!-- The following section enables trace files for basic iRMC monitoring script -->
<$SectioniRMCMonitor>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectioniRMCMonitor>
<!-- The following section enables trace files for the main server Hardware Components (CPU/Memory/Fan/PowerSupply) monitoring script -->
<$SectionHardwareComponentMonitor>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectionHardwareComponentMonitor>
<!-- The following section enables trace files for the 'Component Status' monitoring script -->
<$SectionComponentStatusMonitor>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectionComponentStatusMonitor>
<!-- The following section enables trace files for the Performance monitoring script -->
<$SectionPerformanceMonitor>
<$TagDebugMode>yes</$TagDebugMode>
<$TagOverWrite>no</$TagOverWrite>
</$SectionPerformanceMonitor>
<!--
The following sections specify for which servers the traces will be generated:
In the '<$TagHostsDiscovery>' and '<$TagHostsMonitoring>' sections
single or multiple servers can be specified for verbose debug output
during the discovery and/or during monitoring.
Use '<$TagHostsDiscovery>' for selecting hosts for the discovery trace.
Use '<$TagHostsMonitoring>' for selecting hosts for the monitoring trace.
Use 'all' (without quote signs) for all Fujitsu iRMC / Out-Of-Band Servers monitored by SCOM.
Use a single IP address or a comma separated list to select multiple single servers
Example:
<$TagHostsDiscovery>all</$TagHostsDiscovery>
<$TagHostsMonitoring>192.168.1.100,192.168.1.101,192.168.1.102</$TagHostsMonitoring>
will generate discovery traces for all Fujitsu iRMC / Out-Of-Band Servers and
will generate monitoring traces only for servers with the IP address
192.168.1.100 192.168.1.101 and 192.168.1.102
-->
<$TagHostsDiscovery>all</$TagHostsDiscovery>
<$TagHostsMonitoring>all</$TagHostsMonitoring>
</$SectionRoot>
"@
}
}
if ($Node.Name -eq $section) {
if ($xmlDoc.$SectionRoot.$section.$TagDebugMode -ne $null) {
if ($($xmlDoc.$SectionRoot.$Section.$TagDebugMode).ToUpper() -eq "YES") {
$global:DebugMode = $True
$global:DebugFile = $True
}
}
if ($xmlDoc.$SectionRoot.$section.$TagOverWrite -ne $null) {
if ($($xmlDoc.$SectionRoot.$Section.$TagOverWrite).ToUpper() -eq "NO") {
$global:OverWrite = $False
}
}
break
}
}
}
if ($xmlDoc.$SectionRoot.$HostTag -ne $null) {
$global:DebugHosts = $($xmlDoc.$SectionRoot.$HostTag).ToLower()
}
# Check if DEBUG shall run for this server.
# There are two possibilities to check: "all" server DEBUG is on or this server is in the list.
if ($global:DebugHosts -eq "all") {
$global:DebugForHost = $True
} else {
# Check if this host is in the list of DebugHosts
$ListOfHosts = $($global:DebugHosts).split(',')
DebugOut "Searching for host: $ServerName"
DebugOut "in list of DebugHosts: $global:DebugHosts"
if ($ServerName.ToLower() -in $ListOfHosts) {
$global:DebugForHost = $True
}
}
} # else file does not exist
} # else directory does not exist
}
Function CreateLogFile
{
# For some reason checking for "$True" with "if ($global:a -and $global:b)" is not evaluated correctly!!!
# It works OK in a test with a simple PS script ... no idea why ... Thus we use:
if ($global:DebugForHost -eq $True) {
if ($global:DebugFile -eq $True) {
# Create the target directory, if it does not exist
if (!(Test-Path -Path $global:LogFilePath)) {
New-Item -ItemType directory -Path $global:LogFilePath | Out-Null
}
# Check if file exists and delete if it does and OverWrite is set to TRUE
if (Test-Path -Path $global:LogFileName) {
DebugOut ""
DebugOut "Log file already exists at: $global:LogFileName"
if ($global:OverWrite -eq $True) {
Remove-Item -Path $global:LogFileName -Force | Out-Null
}
}
# If the file has just been removed (OverWrite = YES) or the file does not exist: create it
if (!(Test-Path -Path $global:LogFileName)) {
#Create log file
New-Item -Path $global:LogFileName -ItemType File | Out-Null
}