common/Common.ps1
# Copyright (C) 2020 Huawei Technologies Co., Ltd. All rights reserved. # This program is free software; you can redistribute it and/or modify # it under the terms of the MIT License # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MIT License for more detail <# NOTE: Common Utilities #> # . $PSScriptRoot/I18n.ps1 # . $PSScriptRoot/Logger.ps1 # . $PSScriptRoot/Threads.ps1 function Write-Input { param($input) return $input } function Convert-IPV4Segment($IPSegment) { <# .DESCRIPTION Convert a specified ip segment expression to all possible int ip segment array .EXAMPLE PS C:\> Convert-IPV4Segment 3-4,5,10 PS C:\> 3 4 5 10 #> $result = @() $IPSegment.Split(',') | ForEach-Object { $split = $_.Split('-') $result += $($([int]$split[0])..$([int]$split[-1])) } return $result } function ConvertFrom-IPRangeString { param ( [String][parameter(Mandatory = $true)] $IPRangeString ) $port_regex = ':([1-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])' $hostnameSection = "([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])" [regex] $hostnameRegex = "^$hostnameSection(\.$hostnameSection)+($port_regex)?`$" # $ipv4Section = '(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])' # $ipv4RangedSection = "$ipv4Section(-$ipv4Section)?" # $ipv4RangeSectionWithComma = "$ipv4RangedSection(,$ipv4RangedSection)*" # [regex] $ipv4_regex = "^($ipv4RangeSectionWithComma(\.$ipv4RangeSectionWithComma){3})($port_regex)?`$" # TODO add ipv6 range support # $ipv6Section='[0-9A-Fa-f]{1,4}' # $ipv6RangedSection="$ipv6Section(-$ipv6Section)?" # $ipv6RangedSectionWithComma="$ipv6RangedSection(,$ipv6RangedSection)*" # try to treat it as ipv4 $IPV4 = ConvertFrom-IPV4RangeString $IPRangeString if ($IPV4 -ne $false) { return ,$IPV4 } # try to treat it as hostname if ($IPRangeString -match $hostnameRegex) { return ,@($IPRangeString) } # try to treat it as ipv6 $IPV6 = ConvertFrom-IPV6RangeString $IPRangeString if ($IPV6 -ne $false) { return ,$IPV6 } throw $([string]::Format($(Get-i18n ERROR_ILLEGAL_ADDR), $IPRangeString)) } function ConvertFrom-IPV4RangeString { param( [String][parameter(Mandatory = $false)] $IPRangeString ) $port_regex = ':([1-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])' $ipv4Section = '(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])' $ipv4RangedSection = "$ipv4Section(-$ipv4Section)?" $ipv4RangeSectionWithComma = "$ipv4RangedSection(,$ipv4RangedSection)*" [regex] $ipv4_regex = "^($ipv4RangeSectionWithComma(\.$ipv4RangeSectionWithComma){3})($port_regex)?`$" $matches = $ipv4_regex.Matches($IPRangeString) if ($matches.Count -eq 1) { $singleIpRange = $matches[0].Groups[1].Value $port = $IPRangeString -replace $singleIpRange, '' $segments = $singleIpRange.Split('.') $segment1 = Convert-IPV4Segment $segments[0] $segment2 = Convert-IPV4Segment $segments[1] $segment3 = Convert-IPV4Segment $segments[2] $segment4 = Convert-IPV4Segment $segments[3] $IPArray = New-Object System.Collections.ArrayList foreach ($s1 in $segment1) { foreach ($s2 in $segment2) { foreach ($s3 in $segment3) { foreach ($s4 in $segment4) { [Void] $IPArray.Add("$(@($s1, $s2, $s3, $s4) -join '.')$port") } } } } return ,$IPArray.ToArray() } return $false } function ConvertFrom-IPV6RangeString { param( [String][parameter(Mandatory = $false)] $IPRangeString ) try { $Zone = '' $Suffix = '' $Prefix = '' # handle [] if ($IPRangeString.StartsWith('[')) { $Prefix = '[' $Suffix = $IPRangeString.Substring($IPRangeString.IndexOf(']')) $IPRangeString = $IPRangeString.Substring(1, $IPRangeString.IndexOf(']') - 1) } # handle %eth0 if ($IPRangeString.IndexOf('%') -gt 0) { $Zone = $IPRangeString.Substring($IPRangeString.IndexOf('%')) $IPRangeString = $IPRangeString.Substring(0, $IPRangeString.IndexOf('%')) } # if ($IPRangeString.StartsWith("::")) { # $IPRangeString = "0$IPRangeString" # } $segments = New-Object System.Collections.ArrayList $split = $IPRangeString -split ':' $split | ForEach-Object { [void] $segments.Add($(Convert-IPV6Segment $_)) } $IPV6Array = $(Merge-IPSegments $segments.ToArray() ':') $Results = New-Object System.Collections.ArrayList for ($idx = 0; $idx -lt $IPV6Array.Count; $idx++) { $IsIPV6 = $false [IPAddress]$ipv6 = $null if ([IPAddress]::TryParse($IPV6Array[$idx], [ref]$ipv6)) { if ($ipv6.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6) { $IsIPV6 = $true } } if (-not $IsIPV6) { throw $([string]::Format($(Get-i18n ERROR_ILLEGAL_ADDR), $IPRangeString)) } [void] $Results.add("$Prefix$($IPV6Array[$idx])$Zone$Suffix") } return ,$Results.ToArray() } catch { throw $([string]::Format($(Get-i18n ERROR_ILLEGAL_ADDR), $IPRangeString)) } } function Merge-IPSegments { [CmdletBinding()] param( [String[][]]$segments, [String]$join ) if ($segments.Length -gt 2) { $results = New-Object System.Collections.ArrayList $First, $Rest = $segments $merged = $(Merge-IPSegments $Rest ':') foreach ($s1 in $First) { foreach ($s2 in $merged) { [Void] $results.Add("$s1$join$s2") } } return ,$results.ToArray() } if ($segments.Length -eq 2) { $results = New-Object System.Collections.ArrayList foreach ($s1 in $segments[0]) { foreach ($s2 in $segments[1]) { [Void] $results.Add("$s1$join$s2") } } return ,$results.ToArray() } } function Convert-IPV6Segment { <# .DESCRIPTION Convert a specified ip segment expression to all possible int ip segment array .EXAMPLE PS C:\> $result = Convert-IPV6Segment "20F1-20F2,20F4,20F6-20F8" PS C:\> $result | Should -be @('20F1', '20F2', '20F4', '20F6', '20F7', '20F8') #> param([string]$IPSegment) $IntResults = @() if ($null -ne $IPSegment -and $IPSegment -ne '') { if ($IPSegment.indexOf('.') -ge 0) { return ConvertFrom-IPV4RangeString $IPSegment } $IPSegment.Split(',') | ForEach-Object { # if segment contains '.', treat it as ipv4 $split = $_.Split('-') if ($split.count -gt 2) { throw $(Get-i18n ERROR_ILLEGAL_ADDR) } $from = Invoke-Expression "0x$($split[0])" $to = Invoke-Expression "0x$($split[-1])" $IntResults += $($from..$to) } } else { return @('') } $result = New-Object System.Collections.ArrayList for ($idx = 0; $idx -lt $IntResults.Count; $idx++) { [void] $result.add($IntResults[$idx].ToString('x')) } return ,$result.ToArray() } function Get-MatchedSizeArray { [CmdletBinding()] param($Source, $Target, $SourceName, $TargetName) if ($Target.Count -eq 1 -and $Source.Count -ne 1) { $Target = $Target * $Source.Count } if ($Source.Count -ne $Target.Count) { throw $([string]::Format($(Get-i18n ERROR_PARAMETER_COUNT_DIFFERERNT), $SourceName, $TargetName)) } return , $Target } function Get-OptionalMatchedSizeArray { [CmdletBinding()] param($Source, $Target) if ($null -eq $Target -or $Target.Count -eq 0) { $empty = @($null) * $Source.Count return , $empty } else { $matched = Get-MatchedSizeArray $Source $Target 'source' 'target' return , $matched } } function Get-OptionalMatchedSizeMatrix { [CmdletBinding()] param($Source, $Target, $ValidSet, $SourceName, $TargetName) if ($null -eq $Target -or $Target.Count -eq 0) { $empty = @($null) * $Source.Count return , $empty } else { # every element in the matrix should be an array if ($Target -isnot [array]) { throw [String]::Format($(Get-i18n ERROR_MUST_BE_MATRIX), $TargetName) } for ($idx = 0; $idx -lt $Target.Count; $idx++) { $element = $Target[$idx] if ($element -isnot [array]) { throw [String]::Format($(Get-i18n ERROR_ELEMENT_NOT_ARRAY), $TargetName) } if ($null -ne $ValidSet) { $diff = Compare-Object $ValidSet $element | ? {$_.sideindicator -eq "=>"} | % {$_.inputobject} if ($null -ne $diff -and $diff.Count -gt 0) { $ValidSetString = $ValidSet -join ", " $DiffString = $diff -join ", " throw [String]::Format($(Get-i18n ERROR_ELEMENT_ILLEGAL), $TargetName, $DiffString, $ValidSetString) } } } $matched = Get-MatchedSizeArray $Source $Target $SourceName $TargetName return , $matched } } function Assert-NotNull($Parameter, $ParameterName) { if ($null -eq $Parameter) { throw $([string]::Format($(Get-i18n ERROR_PARAMETER_EMPTY), $ParameterName)) } } function Assert-ArrayNotNull($Parameter, $ParameterName) { if ($null -eq $Parameter -or $Parameter.Count -eq 0 -or $Parameter -contains $null) { throw $([string]::Format($(Get-i18n ERROR_PARAMETER_ARRAY_EMPTY), $ParameterName)) } } function Remove-EmptyValues { [CmdletBinding()] param ( [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] $Target ) $hash = @{} if ($null -ne $Target) { # foreach ($pair in $Target.GetEnumerator()) { # $key = $pair.Name # $value = $pair.Value # if ($null -ne $value) { # if ($value -is [array] -and $value.count -eq 0) { # continue # } # if ($value -is [string] -and $value -ne '') { # continue # } # [Void]$hash.Add($key, $value) # } # } foreach ($key in $Target.Keys) { $value = $Target.Item($key) if ($null -ne $value) { if ($value -is [array] -and $value.count -eq 0) { continue } if ($value -is [hashtable] -and $value.count -eq 0) { continue } if ($value -is [string] -and $value -eq '') { continue } [Void]$hash.Add($key, $value) } } # for ($idx=0; $idx -lt $Target.Keys.Count; $idx++) { # $key = $Target.keys[$idx] # $value = $Target.Item($key) # if ($null -ne $value) { # if ($value -is [array] -and $value.count -eq 0) { # continue # } # if ($value -is [string] -and $value -eq '') { # continue # } # [Void]$hash.Add($key, $value) # } # } # $Target.Keys | ForEach-Object { # $value = $Target.Item($_) # if ($null -ne $value) { # if ($value -is [array] -and $value.count -eq 0) { # continue # } # if ($value -is [string] -and $value -eq '') { # continue # } # [Void]$hash.Add($_, $value) # } # } } return $hash } function Remove-NoneValues { [CmdletBinding()] param ( [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] $Source ) $hash = @{} if ($null -ne $Source) { foreach ($key in $Source.Keys) { $value = $Source.Item($key) if ($null -ne $value) { [Void]$hash.Add($key, $value) } } } return $hash } function Get-PlainPassword { [CmdletBinding()] param ($SecurePassword) if ($null -ne $SecurePassword -and $SecurePassword -is [SecureString]) { $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword) return [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) } return $SecurePassword } function Get-RandomIntGuid { return $(Get-Random -Maximum 1000000) } function Trace-Session ($Session, $message) { return "[$($Session.Address)] $message" } function Copy-ObjectProperties ($Source, $Properties) { $Clone = New-Object PSObject $Properties | ForEach-Object { $Clone | Add-Member -MemberType NoteProperty "$_" $Source."$_" } return $Clone } function Copy-ObjectExcludes ($Source, $excludes) { $Clone = New-Object PSObject $Properties = $Source.psobject.properties.Name $Properties | ForEach-Object { if ($_ -notin $excludes) { $Clone | Add-Member -MemberType NoteProperty "$_" $Source."$_" } } return $Clone } function Clear-OdataProperties { <# Clear Odata Properties of redfish response #> [CmdletBinding()] param ( [psobject] [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] $Source ) return Copy-ObjectExcludes $Source $BMC.OdataProperties } function Merge-OemProperties { <# Merge Redfish Odata Oem properties to main body #> [CmdletBinding()] param ( [psobject] [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] $Source ) # $Clone = $Source | Select-Object -Property * -ExcludeProperty Oem # $Oem = $Source.Oem.Huawei # $Properties = $Oem | Get-Member -MemberType NoteProperty | Select-Object -Expand Name # $Properties | ForEach-Object { # $Clone | Add-Member -MemberType NoteProperty $_ $Oem."$_" # } # return $Clone if ($Source.Oem.Huawei) { return Merge-NestProperties $Source @('Oem', 'huawei') } return $Source } function Merge-NestProperties { <# Merge Redfish Odata Oem properties to main body #> [CmdletBinding()] param ( [psobject] [parameter(Mandatory = $true)] $Source, [String[]] [parameter(Mandatory = $true)] $NestKeys ) $Clone = $Source | Select-Object -Property * -ExcludeProperty $NestKeys[0] $Nest = $Source for ($idx = 0; $idx -lt $NestKeys.Length; $idx++) { $key = $NestKeys[$idx] $Nest = $Nest."$key" } # $Logger.Info("Nest is $Nest") $Properties = $Nest.psobject.properties.Name $Properties | ForEach-Object { $Clone | Add-Member -MemberType NoteProperty $_ $Nest."$_" } return $Clone } function Resolve-EnumValues { [CmdletBinding()] param ( [parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] $Source ) $hash = @{} if ($null -ne $Source) { foreach ($key in $Source.Keys) { $value = $Source.Item($key) if ($value -is [Enum]) { [Void] $hash.Add($key, $value.toString()) } elseif ($value -is [Array]) { $Converted = New-Object System.Collections.ArrayList $value | ForEach-Object { if ($_ -is [Enum]) { [Void] $Converted.Add($_.toString()) } else { [Void] $Converted.Add($_) } } [Void] $hash.Add($key, $Converted) } else { [Void]$hash.Add($key, $value) } } } return $hash } function Get-EnumNames { param( [string]$enum ) $Names = New-Object System.Collections.ArrayList [enum]::getvalues([type]$enum) | ForEach-Object { [Void] $Names.add($_.toString()) } return $Names.ToArray() } function Protect-NetworkUriUserInfo { [CmdletBinding()] param ( [string] $NetworkPath ) try { $NetworkUri = New-Object System.Uri($NetworkPath) if($NetworkUri.UserInfo.Length -gt 0) { return $NetworkUri.AbsoluteUri -replace $NetworkUri.UserInfo, "***:***" } return $NetworkPath } catch { return $NetworkPath } } function Resolve-NetworkUriSchema { [CmdletBinding()] param ( [string] $NetworkPath ) try { $NetworkUri = New-Object System.Uri($NetworkPath) $Schema = $NetworkUri.Scheme if($NetworkPath.StartsWith($Schema, "CurrentCultureIgnoreCase")) { return "$($Schema.ToLower())$($NetworkPath.Substring($Schema.Length))" } return $NetworkPath } catch { return $NetworkPath } } function Update-SessionAddress { [CmdletBinding()] param ( [RedfishSession] [parameter(Mandatory = $true)] $Session, [PSObject] [parameter(Mandatory = $true)] $Target ) $Clone = New-Object PSObject -Property @{ Host = $Session.Address; } if ($null -ne $Target) { $Properties = $Target.psobject.properties.Name $Properties | ForEach-Object { $Clone | Add-Member -MemberType NoteProperty "$_" $Target."$_" } } # $Logger.info("return: $($Clone.psobject.properties.Name)") # $Logger.info("return: $($Clone.Host)") return $Clone } function Close-Pool ($pool) { if ($null -ne $pool) { $pool.close() } } function Assert-NetworkUriInSchema ($RedfishSession, $FilePath, $SupportSchema) { # iBMC local storage protocol handle $IsBMCFileProtocol = ($FilePath.StartsWith("file:///tmp", "CurrentCultureIgnoreCase") ` -or $FilePath.StartsWith("/tmp", "CurrentCultureIgnoreCase")) if ($IsBMCFileProtocol -and 'file' -in $SupportSchema) { return $FilePath } $SupportSchemaString = $SupportSchema -join ", " try { $ImageFileUri = New-Object System.Uri($FilePath) } catch { throw $(Get-i18n ERROR_FILE_URI_ILLEGAL) } $SecureFileUri = Protect-NetworkUriUserInfo $FilePath if ($ImageFileUri.Scheme -notin $SupportSchema) { $Logger.warn($(Trace-Session $RedfishSession "File $SecureFileUri is not in support schema: $SupportSchemaString")) throw $([string]::Format($(Get-i18n ERROR_FILE_URI_NOT_SUPPORT), $ImageFileUri, $SupportSchemaString)) } return Resolve-NetworkUriSchema $FilePath } function ConvertTo-PlainString { [CmdletBinding()] param ( [System.Object] [parameter(Mandatory = $true)] $SensitiveString, [System.String] [parameter(Mandatory = $true)] $ParameterName ) # if ($SensitiveString -isnot [SecureString] -and $SensitiveString -isnot [String]) { # throw $([string]::Format($(Get-i18n ERROR_INVAIL_SENSITIVE_STRING), $ParameterName)) # } $PlainPasswd = $SensitiveString.ToString() if ($SensitiveString -is [SecureString]) { $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($SensitiveString) $PlainPasswd = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR) return $PlainPasswd } return $PlainPasswd } function Assert-IsSensitiveString { [CmdletBinding()] param ( [System.Object[]] [parameter(Mandatory = $true)] $SensitiveStringList, [System.String] [parameter(Mandatory = $true)] $ParameterName ) for ($idx=0; $idx -lt $SensitiveStringList.Count; $idx++) { $SensitiveString = $SensitiveStringList[$idx] if ($SensitiveString -isnot [SecureString] -and $SensitiveString -isnot [String]) { throw $([string]::Format($(Get-i18n ERROR_INVAIL_SENSITIVE_STRING), $ParameterName)) } } } function Get-EthernetInterfaces-ID ($Session){ $EthernetInterfaces_ID = "" try { $Path = "/Managers/$($Session.Id)/EthernetInterfaces" $Response = Invoke-RedfishRequest $Session $Path | ConvertFrom-WebResponse $EthernetInterfaces_URL = $Response.Members[0]."@odata.id" $EthernetInterfaces_ID = $EthernetInterfaces_URL.Split("/")[-1] } catch { throw "[$($Session.Address)] $($_.Exception.Message)" } return $EthernetInterfaces_ID } function Assert-IPv4 ($IPv4Address) { if ($null -ne $IPv4Address){ $parttern = "^(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))$" $match = $IPv4Address -match $parttern return $match } return $true } function Assert-IPv6 ($IPv6Address) { if ($null -ne $IPv6Address){ $parttern = "^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$" $match = $IPv6Address -match $parttern return $match } return $true } |