Util/Util.psm1
using module '../Common/Result' Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Error | Join-Path -ChildPath Error) Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Common | Join-Path -ChildPath Wrappers | Join-Path -ChildPath Wrappers) function Test-UserLoggedIn { param() [RMReturn] $RMReturn = [RMReturn]::new() if (-not (Get-Variable -Name "RMContext-UserLogin" -ErrorAction SilentlyContinue)) { $UserMessage = "Please login using the cmdlet 'Invoke-RMLogin' and try again." Write-RMError -Message $UserMessage [RMError] $RMError = [RMError]::new("login_required", $UserMessage) $RMReturn.AddRMError($RMError) $RMReturn.SetReturnCode([RMReturn]::ERROR) return $RMReturn } $RMReturn.SetReturnCode([RMReturn]::SUCCESS) return $RMReturn } # TODO: This wrapper is not doing much, can be removed. function Invoke-RMRestMethod { param( [hashtable] $Params ) return Invoke-RestMethod @Params } function Get-RMStringAsHashtable { param( [string] $InputString ) $Result = @{} if (!("" -ne $InputString -and $InputString.Contains("="))) { return $Result } foreach ($item in $InputString.Split(",").Trim()) { $item = $item.Split("=") $Result.add($item[0].Trim(), $item[1].Trim()) } return $Result } function Get-RMStringArrayAsHashtable { param( [string[]] $InputItems, [string] $ParameterName ) $Result = @{} if ($null -eq $InputItems -or 0 -eq $InputItems.Count) { return $Result } foreach ($item in $InputItems) { if (!$item.Contains("=")) { throw "$ParameterName is invalid, each item in '$ParameterName' must be of the format 'key=value'" } $item = $item.Split("=") $Result.add($item[0].Trim(), $item[1].Trim()) } return $Result } function Get-RMVolumeType { param( [string] $VolumeType ) if ("GP2" -ieq $VolumeType) { return "ssd2" } if ("GP3" -ieq $VolumeType) { return "ssd3" } if ("magnetic" -ieq $VolumeType) { return "magnetic" } if ("IO1" -ieq $VolumeType) { return "iops_ssd" } if ("IO2" -ieq $VolumeType) { return "iops2_ssd" } throw "Unsupported volume type '$VolumeType'" } function Get-RMExcludedMountPoint { param( [System.Object] $Source, [System.Object[]] $MountPoints ) while ($true) { $ReadValue = Read-RMString -UserMessage "Enter the mount points to be excluded, separated by commas" ` -DefaultValue "None" -IsRequired $false -ParameterName "Mount points to be excluded" if ("" -eq $ReadValue) { return $ReadValue } $MountsToExclude = $ReadValue.Split(",").Trim() if ("windows" -ieq $Source.os_type) { if ($MountsToExclude -contains "c") { Write-RMError -Message "Cannot exclude 'C' drive, please try again." continue } } elseif ($MountsToExclude -contains "/" -or $MountsToExclude -contains "/boot" -or $MountsToExclude -contains "/usr") { Write-RMError -Message "Cannot exclude mount points '/', '/boot' or '/usr', please try again." continue } $Result = Test-RMNonExistentMountPoints -SourceMountPoints $MountPoints.Values -UserInputMountPoints $MountsToExclude if (0 -ne $Result.Count) { $ResultAsString = $Result -join ", " Write-RMError -Message "Mount points '$ResultAsString' does not exist on source and hence cannot be excluded, please try again." continue } return $ReadValue } } function Get-RMSelectedMount { param( [System.Object[]] $MountPoints, [System.Object[]] $DifferenceList, [bool] $IncludeEqual ) $SelectedMountPoints = @() foreach($MountPoint in $MountPoints) { $Result = Compare-Object -ReferenceObject $MountPoint.values -DifferenceObject $DifferenceList -IncludeEqual if ($IncludeEqual) { if ($Result.SideIndicator -contains "==") { $SelectedMountPoints += $MountPoint } } elseif (!($Result.SideIndicator -contains "==")) { $SelectedMountPoints += $MountPoint } } return $SelectedMountPoints } function Compare-RMMountPoint { param( [System.Object] $Source, [System.Object[]] $SourceMountPoints, [System.Object[]] $UserInputMountPoints, [string] $ParameterName ) $Errors = @() if("windows" -ieq $Source.os_type) { if ($UserInputMountPoints -inotcontains "c") { $Errors += "Mount point 'C' was not included in parameter '$ParameterName', mount point 'C' is required." } return $Errors } if ($SourceMountPoints -contains "/" -and $UserInputMountPoints -notcontains "/") { $Errors += "Mount point '/' was not included in parameter '$ParameterName', mount point '/' is required." } if ($SourceMountPoints -contains "/boot" -and $UserInputMountPoints -notcontains "/boot") { $Errors += "Mount point '/boot' was not included in parameter '$ParameterName', mount point '/boot' is required." } if ($SourceMountPoints -contains "/usr" -and $UserInputMountPoints -notcontains "/usr") { $Errors += "Mount point '/usr' was not included in parameter '$ParameterName', mount point '/usr' is required." } return $Errors } function Test-RMNonExistentMountPoints { param( [System.Object[]] $SourceMountPoints, [System.Object[]] $UserInputMountPoints ) $NonExistentMountPoints = @() $Results = Compare-Object -ReferenceObject $SourceMountPoints -DifferenceObject $UserInputMountPoints -IncludeEqual if ($Results.SideIndicator -contains "=>") { foreach ($Result in $Results) { if ($Result.SideIndicator -eq "=>") { $NonExistentMountPoints += $Result.InputObject } } } return $NonExistentMountPoints } function Watch-RMPreflightStatus { param( [string] $PreflightId, [string] $TimeOutMessage ) $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" $Uri = Get-Variable -Name "RMContext-ReactorURI" $Headers = @{ Accept = "application/rm+json" "X-Auth-Token" = $RMLoginResult.Value.token } $Params = @{ Method = "Get" Uri = $Uri.Value + "/preflights/" + $PreflightId Headers = $Headers } $Timeout30MinsInSeconds = 30 * 60 $StartTime = [DateTimeOffset]::Now.ToUnixTimeSeconds() $Response = Invoke-RMRestMethod -Params $Params while ($Response.state -ne "success" -and $Response.state -ne "error") { $TimeNow = [DateTimeOffset]::Now.ToUnixTimeSeconds() if (($TimeNow - $StartTime) -gt $Timeout30MinsInSeconds) { throw $TimeOutMessage } Start-Sleep -Seconds 5 $Response = Invoke-RMRestMethod -Params $Params } return $Response } function Watch-RMTargetInventoryStatus { param( [string] $TargetInventoryId ) $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" $Uri = Get-Variable -Name "RMContext-ReactorURI" $Headers = @{ Accept = "application/rm+json" "X-Auth-Token" = $RMLoginResult.Value.token } $Params = @{ Method = "Get" Uri = $Uri.Value + "/targetinventories/" + $TargetInventoryId Headers = $Headers } $Timeout30MinsInSeconds = 30 * 60 $StartTime = [DateTimeOffset]::Now.ToUnixTimeSeconds() Write-Output "Waiting for target inventory to complete..." | Out-Host $Response = Invoke-RMRestMethod -Params $Params while ($Response.state -ne "success" -and $Response.state -ne "error") { $TimeNow = [DateTimeOffset]::Now.ToUnixTimeSeconds() if (($TimeNow - $StartTime) -gt $Timeout30MinsInSeconds) { throw "Timed out while waiting for target inventory to complete" } Start-Sleep -Seconds 5 $Response = Invoke-RMRestMethod -Params $Params } return $Response } function Get-RMWindowsOSMMapping { param() # ME OSM name to user label return @{ "Windows 10 Pro" = "Windows 10 Pro" "Windows 10 Enterprise" = "Windows 10 Enterprise" "Windows 11 Pro" = "Windows 11 Pro" "Windows 11 Enterprise" = "Windows 11 Enterprise" "Windows Server 2008 R2 SERVERSTANDARD" = "Windows Server 2008 R2 Standard" "Windows Server 2008 R2 SERVERDATACENTER" = "Windows Server 2008 R2 Datacenter" "Windows Server 2012 SERVERSTANDARD" = "Windows Server 2012 Standard" "Windows Server 2012 SERVERDATACENTER" = "Windows Server 2012 Datacenter" "Windows Server 2012 R2 SERVERSTANDARD" = "Windows Server 2012 R2 Standard" "Windows Server 2012 R2 SERVERDATACENTER" = "Windows Server 2012 R2 Datacenter" "Windows Server 2016 SERVERSTANDARD" = "Windows Server 2016 Standard" "Windows Server 2016 SERVERSTANDARDCORE" = "Windows Server 2016 Standard Core" "Windows Server 2016 SERVERDATACENTER" = "Windows Server 2016 Datacenter" "Windows Server 2016 SERVERDATACENTERCORE" = "Windows Server 2016 Datacenter Core" "Windows Server 2019 SERVERSTANDARD" = "Windows Server 2019 Standard" "Windows Server 2019 SERVERSTANDARDCORE" = "Windows Server 2019 Standard Core" "Windows Server 2019 SERVERDATACENTER" = "Windows Server 2019 Datacenter" "Windows Server 2019 SERVERDATACENTERCORE" = "Windows Server 2019 Datacenter Core" "Windows Server 2022 SERVERSTANDARD" = "Windows Server 2022 Standard" "Windows Server 2022 SERVERSTANDARDCORE" = "Windows Server 2022 Standard Core" "Windows Server 2022 SERVERDATACENTER" = "Windows Server 2022 Datacenter" "Windows Server 2022 SERVERDATACENTERCORE" = "Windows Server 2022 Datacenter Core" } } function Get-RMLinuxOSMMapping { param() # ME OSM name to user label return @{ "RHEL7" = "Red Hat Enterprise Linux Server 7.9" "RHEL8" = "Red Hat Enterprise Linux Server 8" "RHEL9" = "Red Hat Enterprise Linux Server 9" "RHEL610" = "Red Hat Enterprise Linux Server 6.1" "CENTOS7" = "CentOS 7.9" "CENTOS610" = "CentOS 6.1" "SLES15SP1" = "SUSE Linux Enterprise Server 15 SP1" "UBUNTU1604LTS" = "Ubuntu 16.04 LTS" } } function Get-RMOSMMappingBySource { param( [System.Object] $Source ) $ResultOSMMapping = @{} $OSMMapping = @{} if ($Source.os_type -eq "windows") { $OSMMapping = Get-RMWindowsOSMMapping } else { $OSMMapping = Get-RMLinuxOSMMapping } $SourceMigrationState = $Source.attributes.os.source_migration_state | ConvertFrom-Json foreach ($UpgradeOption in $SourceMigrationState.upgrade_options) { # Mapping of label to ME OSM name - this will be helpful as the user will be # providing labels and we need to send ME OSM name to ME. $ResultOSMMapping.Add($OSMMapping[$UpgradeOption], $UpgradeOption) } return $ResultOSMMapping } function Add-RMResizeMount { param( [string[]] $SelectedMounts, [hashtable] $ResizeMounts, [object] $Source ) $MountsResizeErrors = @() $ResizeMountList = @() $ResizeMounts.keys | ForEach-Object { $ResizeMountList += $_ } $Result = Compare-Object -ReferenceObject $SelectedMounts -DifferenceObject $ResizeMountList -IncludeEqual if ($Result.SideIndicator -contains "=>") { $MountsResizeErrors += "Resize mount points contains a mount point that has not been selected for migration, please check and try again" return $null, $MountsResizeErrors } $IsValidData = $true $MountsResize = @{} $SourceMountPointObjects = Get-RMMountPointObject -Source $Source foreach ($Mount in $ResizeMounts.keys) { foreach ($MountPoint in $SourceMountPointObjects) { $MountPath = $MountPoint.path if ($MountPath -ne $Mount) { continue } $TotalSpace = [math]::round($MountPoint.size_kb/(1024 * 1024), 2) $UsedSpace = [math]::round($MountPoint.used_kb/(1024 * 1024), 2) $ResizeValue = $ResizeMounts[$Mount] if (-not($ResizeValue -match "^[\d]+$")) { $MountsResizeErrors += "Mount point '$MountPath' contains non-integer resize value '$ResizeValue', please enter an integer value only" $IsValidData = $false continue } $ResizeValue = $ResizeValue -as [int] if (-not($ResizeValue -gt $UsedSpace -and $ResizeValue -lt $TotalSpace)) { $MountsResizeErrors += "Resize value for mount point '$MountPath' is not valid, resize value should not be greater than the current total size or less than the used size" $IsValidData = $false continue } $MountResizeInKiB = $ResizeValue * 1024 * 1024 $MountsResize.Add($MountPath, $MountResizeInKiB) break } } if (!$IsValidData) { return $null, $MountsResizeErrors } return $MountsResize, $MountsResizeErrors } function Get-RMMountPointObject { param( [System.Object] $Source ) $MountPoints = @() if ("windows" -ieq $Source.os_type) { foreach ($Mount in $Source.attributes.storage.mounts.psobject.properties.value) { $MountPoints += $Mount } } else { foreach ($Mount in $Source.attributes.storage.mounts.psobject.properties.value) { if ("disk" -ieq $Mount.nature -or "subvolume" -ieq $Mount.nature -and "squashfs" -ine $Mount.fs_type) { $MountPoints += $Mount } } } return $MountPoints } function Get-RMMigrationInstruction { param ( [hashtable] $MigrationInstructions ) $MigrationInstructionList = @() if ($null -ne $MigrationInstructions) { $MigrationInstructionList = $MigrationInstructions.Keys | foreach-object { "$_/$($MigrationInstructions[$_])"} } return $MigrationInstructionList } function Get-RMPartition { param ( [System.Object[]] $SelectedMounts ) $Partitions = @() foreach($MountPoint in $SelectedMounts) { $Partitions += $MountPoint.mount_point } return $Partitions } function Get-RMResizeMountsPoint { param ( [string[]] $ResizeMountPoints, [array] $SelectedMountPoints, [System.Object] $Source ) $Errors = @() try { $ResizeMounts = Get-RMStringArrayAsHashtable -InputItems $ResizeMountPoints -ParameterName "ResizeMountPoints" } catch { $Errors += $PSItem.Exception.Message } $MountsResize = $null $MountsResizeErrors = $null if ($null -ne $ResizeMounts) { $MountsResize, $MountsResizeErrors = Add-RMResizeMount -SelectedMounts $SelectedMountPoints.values -ResizeMounts $ResizeMounts -Source $Source } if ($Errors -gt 0) { $MountsResizeErrors += $Errors } return $MountsResize, $MountsResizeErrors } function Get-RMInteractiveMountsResize { param ( [array] $SelectedMountPoints, [System.Object] $Source ) $MountPointList = @() $SelectedMountPoints | ForEach-Object { $MountPointList += $_.values } $MountsResize = @{} $SourceMountPointObjects = Get-RMMountPointObject -Source $Source foreach ($Mount in $MountPointList) { foreach ($MountPoint in $SourceMountPointObjects) { if ($MountPoint.path -ne $Mount) { continue } $TotalSpace = [math]::round($MountPoint.size_kb/(1024 * 1024), 2) if ([int]$TotalSpace -le 1) { # Cannot resize the disk that is <= 1GiB break } $UsedSpace = [math]::round($MountPoint.used_kb/(1024 * 1024), 2) $RoundedTotalSpace = [math]::round($TotalSpace) while ($true) { $ReadValue = Read-Host "Enter new size in GiB for mount point $Mount, current used space is $UsedSpace GiB of total space $TotalSpace GiB [$RoundedTotalSpace GiB]" if ("" -eq $ReadValue) { $ReadValue = $RoundedTotalSpace } if (-not($ReadValue -match "^[\d]+$")) { Write-RMError -Message "Please enter an integer value only" continue } $ReadValue = $ReadValue -as [int] if (-not($ReadValue -gt $UsedSpace -and $ReadValue -le $RoundedTotalSpace)) { Write-RMError -Message "Resize value for mount point '$Mount' is not valid, resize value should not be greater than the current total size or less than the used size" continue } $MountResizeInKiB = $ReadValue * 1024 * 1024 $MountsResize.Add($Mount, $MountResizeInKiB) break } break } } return $MountsResize } function Get-RMMoveGroupList { param ( [string] $OrganizationId, [int] $PageNumber ) $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly $Headers = @{ Accept = "application/rm+json" "X-Auth-Token" = $RMLoginResult.token } $Params = @{ Method = "Get" Uri = $Uri + "/organizations/" + $OrganizationId + "/movegroups?size=25&page=" + $PageNumber +"&sort=name,desc" Headers = $Headers } $Response = Invoke-RMRestMethod -Params $Params return $Response } function Get-MoveGroupByName { param ( [string] $MoveGroupName, [string] $OrganizationId ) $MoveGroupList = @() $Response = Get-RMMoveGroupList -OrganizationId $OrganizationId -PageNumber 0 $MoveGroupList += $Response $MoveGroup = Get-MoveGroup -MoveGroupName $MoveGroupName -MoveGroupList $MoveGroupList.content if ($null -eq $MoveGroup) { for ($index = 1; $index -lt $Response.page.totalPages; $index++) { $MoveGroupList = Get-RMMoveGroupList -OrganizationId $OrganizationId -PageNumber $index $MoveGroup = Get-MoveGroup -MoveGroupName $MoveGroupName -MoveGroupList $MoveGroupList.content if ($null -ne $MoveGroup) { Set-Variable -Name "RMContext-MoveGroup" -Value $MoveGroup -Scope Global return $MoveGroup } } } else { Set-Variable -Name "RMContext-MoveGroup" -Value $MoveGroup -Scope Global return $MoveGroup } return $null } function Get-MoveGroup { param( [string] $MoveGroupName, [array] $MoveGroupList ) foreach($MoveGroup in $MoveGroupList) { if ($MoveGroupName -eq $MoveGroup.name) { return $MoveGroup } } return $null } function Get-RMEntitlement { param() $CurrentProjectId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly $RMLoginResult = Get-Variable -Name "RMContext-UserLogin" -ValueOnly $Uri = Get-Variable -Name "RMContext-ReactorURI" -ValueOnly $Headers = @{ Accept = "application/rm+json" "X-Auth-Token" = $RMLoginResult.token } $Params = @{ Method = "Get" Uri = $Uri + "/organizations/" + $CurrentProjectId + "/entitlements" Headers = $Headers } $Entitlements = Invoke-RMRestMethod -Params $Params foreach ($Entitlement in $Entitlements.content) { $Remaining = $Entitlement.total - $Entitlement.used if ($Remaining -ge 1) { return $Entitlement } } throw "Not enough entitlements available to start the migration" } function Get-RMTransferMethodInternal { param ( [System.Object] $Source, [string[]] $SelectedMountPoints ) $Result = @{} $Result.Add("Transfer_Method", "file-based") $Result.Add("Is_Block_Based_Supported_By_Source", $false) $Result.Add("Block_Based_Unsupported_Mounts", @()) if ($Source.os_type -eq "windows") { $Result["Transfer_Method"] = "block-based" return $Result } if($Source.attributes.discovered_features.features_list -notcontains "linux_block_based") { return $Result } $Result["Is_Block_Based_Supported_By_Source"] = $true $fs = @("ext2", "ext3", "ext4", "xfs") $IsBlockBasedSupportedByAllSelectedMounts = $true foreach($Mount in $Source.attributes.storage.mounts.psobject.properties.value){ if ($SelectedMountPoints -inotcontains $Mount.path) { continue } if ($Mount.nature -eq "disk") { if ($fs -notcontains $Mount.fs_type) { $Result["Block_Based_Unsupported_Mounts"] += $Mount.path $IsBlockBasedSupportedByAllSelectedMounts = $false; } } } if ($IsBlockBasedSupportedByAllSelectedMounts) { $Result["Transfer_Method"] = "block-based" } return $Result } function Get-RMTransferMethod { param( [System.Object] $Source, [System.Object[]] $SelectedMountPoints, [bool] $IsInteractive, [string] $TransferMethod ) $MountPoints = @() foreach ($SelectedMount in $SelectedMountPoints) { $MountPoints += $SelectedMount.values } $Result = Get-RMTransferMethodInternal -Source $Source -SelectedMountPoints $MountPoints if ($IsInteractive) { if ("block-based" -ieq $Result["Transfer_Method"]) { $ReadValue = Read-RMString -UserMessage "Enter transfer method" -Options "file-based", "block-based" ` -DefaultValue "block-based" -ParameterName "Transfer method" -IsRequired $false return $ReadValue, "" } else { if ($Result["Is_Block_Based_Supported_By_Source"] -and $Result["Block_Based_Unsupported_Mounts"].Count -gt 0) { $BlockBasedUnsupportedMountsAsString = $Result["Block_Based_Unsupported_Mounts"] -join ", " Write-Output "Block-based transfer method is not supported by the mount points '$BlockBasedUnsupportedMountsAsString', file-based transfer method will be used." | Out-Host } else { Write-Output "Block-based transfer method is not supported for the given source, file-based transfer method will be used." | Out-Host } } return $Result["Transfer_Method"], "" } if ([string]::IsNullOrEmpty($TransferMethod)) { return $Result["Transfer_Method"], "" } if ($TransferMethod -eq "file-based") { return $TransferMethod, "" } $ErrorString = "" if ($TransferMethod -eq "block-based" -and $Result["Transfer_Method"] -eq "file-based") { if ($Result["Is_Block_Based_Supported_By_Source"] -and $Result["Block_Based_Unsupported_Mounts"].Count -gt 0) { $BlockBasedUnsupportedMountsAsString = $Result["Block_Based_Unsupported_Mounts"] -join ", " $ErrorString = "Block-based transfer method is not supported by the mount points '$BlockBasedUnsupportedMountsAsString'" } else { $ErrorString = "Block-based transfer method is not supported for the given source." } } return $Result["Transfer_Method"], $ErrorString } function Confirm-RMIPAddress { param ( [string] $IPAddress ) $IPPattern = '^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$' if ($IPAddress -notmatch $IPPattern) { return $false } return $true } |