RiverMeadow.Development.Source/SourceUtil/SourceUtil.psm1

using module '../../Common/Result'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath .. | Join-Path -ChildPath Preflight | Join-Path -ChildPath Preflight)
function Get-RMSourcesForCurrentProject {
    param()

    $LoginStatus = Test-UserLoggedIn
    if ($LoginStatus.ReturnCode -eq [RMReturn]::ERROR) {
        return $LoginStatus
    }

    $CurrentProjectId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    $Result = @()
    $Response = Get-RMSourcesInternal -OrganizationId $CurrentProjectId -PageNumber 0
    $Result += $Response
    for ($index = 1; $index -lt $Response.page.totalPages; $index++) {
        $Result += Get-RMSourcesInternal -OrganizationId $CurrentProjectId -PageNumber $index
    }

    return $Result
}

function Get-RMSourcesInternal {
    param(
        [string] $OrganizationId,
        [int] $PageNumber
    )

    $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 + "/organizations/" + $OrganizationId + "/sources?size=25&page=" + $PageNumber + "&sort=created_at%2Cdesc"
        Headers = $Headers
    }
    return Invoke-RMRestMethod -Params $Params
}

function Get-RMSourceById {
    param(
        [string] $SourceId
    )

    $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 + "/sources/" + $SourceId
        Headers = $Headers
    }
    return Invoke-RMRestMethod -Params $Params
}

function Get-MountPoint {
    param(
        [System.Object] $Source
    )
    $MountPoints = @()
    if ("windows" -ieq $Source.os_type) {
        foreach ($Mount in $Source.attributes.storage.mounts.psobject.properties.value) {
            $MountPoints += @{mount_point = $Mount.path}
        }
    } 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_point = $Mount.path}
            }
        }
    }

    return $MountPoints
}

function Get-RMSourceByIP {
    param(
        [string] $IPAddress
    )

    $CurrentProjectId = Get-Variable -Name "RMContext-CurrentProjectId" -ValueOnly

    $PageIndex = 0
    do {
        $Sources = Get-RMSourcesInternal -OrganizationId $CurrentProjectId -PageNumber $PageIndex
        foreach ($Source in $Sources.content) {
            if ($IPAddress -eq $Source.host) {
                return $Source
            }
        }
        $PageIndex++
    } while ($PageIndex -lt $Sources.page.totalPages)
    
    $ProjectName = Get-Variable -Name "RMContext-CurrentProjectName" -ValueOnly
    $OrgName = Get-Variable -Name "RMContext-CurrentOrganizationName" -ValueOnly
    throw [System.Management.Automation.ItemNotFoundException]::new(`
    "Source with IP address '$IPAddress' does not exist in project '$ProjectName' of organization '$OrgName', cannot start the migration. Please add the source in the project and try again.")
}

function Start-RMSourcePreflight {
    param (
        [System.Object] $Source
    )
    $RequestAttributes = @{
        "type" = "source"
        "resource_id"= $Source.id
        "overrides"= @{}
    }

    $SourceAttributesRequest = @($RequestAttributes)
    $SourceAttributesRequestJson = ConvertTo-Json $SourceAttributesRequest

    $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 = "Post"
        Uri = $Uri + "/preflights"
        Body = $SourceAttributesRequestJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Write-Output "Starting source preflight..." | Out-Host
    return Invoke-RMRestMethod -Params $Params
}

function Get-RMSourceWithAttribute {
    param(
        [System.Object] $Source,
        [System.Object] $CloudAccount,
        [bool] $IgnoreValidationErrors,
        [RMMigrationReturn] $RMMigrationReturn
    )
    if ($Source.attribute_state -eq "running") {
        throw "Source attribute collection state is 'running', please wait for the attribute collection to complete."
    }

    Update-RMSourceWithAppliance -SourceId $Source.id -ApplianceId $CloudAccount.appliance.id
    $Response = Start-RMSourcePreflight -Source $Source
    Write-Output "Waiting for source preflight to complete..." | Out-Host
    $PreflightResult = Watch-RMPreflightStatus -PreflightId $Response.preflights[0].id `
        -TimeOutMessage "Timed out waiting for collection to complete"
    $Source = Get-RMSourceById -SourceId $Source.id
    if (![string]::IsNullOrEmpty($Source.attribute_error)) {
        $PreflightID = $Response.preflights[0].id
        $AttributeCollectionError = $Source.attribute_error
        throw "Source attribute collection with ID: $PreflightID has failed with error $AttributeCollectionError"
    }

    $ShouldExit = Out-RMPreflight -PreflightResult $PreflightResult -IgnoreValidationErrors $IgnoreValidationErrors -RMMigrationReturn $RMMigrationReturn
    return $Source, $ShouldExit
}

function Update-RMSourceWithAppliance {
    param (
        [System.Object] $SourceId,
        [string] $ApplianceId
    )
    
    $RequestAttributes = @{
        "data_only_migration" = $false
        "appliance_id" = $ApplianceId
    }

    $RequestAttributesJson = ConvertTo-Json $RequestAttributes

    $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 = "Put"
        Uri = $Uri + "/sources/" + $SourceId
        Body = $RequestAttributesJson
        ContentType = "application/json"
        Headers = $Headers
    }

    Invoke-RMRestMethod -Params $Params | Out-Null
}

function Read-RMSource {
    param()
    while ($true) {
        $SourceIP =  Read-RMIPAddress -UserMessage "Enter the IP address of the source machine to be migrated"`
             -ParameterName "Source IP address" -IsRequired $true  

        #TODO: Add IP address validation
        try {
            $Source = Get-RMSourceByIP -IPAddress $SourceIP
        } catch [System.Management.Automation.ItemNotFoundException] {
            Write-RMError -Message "Source with IP address '$SourceIP' does not exist in project '$ProjectName' of organization '$OrgName'."
            continue
        }

        return $Source
    }
}

function Test-RMSourceHasDynamicDisks {
    param(
        [System.Object] $Source
    )

    if ($Source.os_type -ine "windows") {
        return $false
    }

    foreach ($Disk in $Source.attributes.storage.disks.psobject.Properties.Value) {
        if ($null -ne $Disk.flags -and $Disk.flags -contains "dynamic_disk") {
            return $true
        }
    }
    return $false
}


# No Export-ModuleMember is being used which will automatically export all the functions
# of this module and we want all the functions to be exported.