MigrationProfile/VSphereMigrationProfile.psm1

using module '../Migration/vSphere/RMVSphereNetworkConfiguration'
Import-Module -Name @(Join-Path $PSScriptRoot .. | Join-Path -ChildPath Util | Join-Path -ChildPath Util)
function New-RMVSphereMigrationProfile {
    param(
        [Parameter(Mandatory)]
        [System.Object] $CloudAccount,

        [Parameter(Mandatory)]
        [System.Object] $Entitlement,

        [Parameter(Mandatory)]
        [System.Object] $Source,

        [Parameter(Mandatory)]
        [System.Object] $ScheduledAt,

        [Parameter(Mandatory)]
        [string] $TargetVMName,

        [Parameter(Mandatory)]
        [System.Object[]] $SelectedMounts,

        [Parameter(Mandatory)]
        [hashtable] $ResizeMountPoints,

        [Parameter(Mandatory)]
        [bool] $ShutdownSource,

        [Parameter(Mandatory)]
        [bool] $ShutdownTarget,

        [bool] $RemoveRMSAgentFromSource,

        [bool] $RemoveRMSAgentFromTarget,

        [Parameter(Mandatory)]
        [string] $DatacenterName,

        [bool] $EnableDestinationNetworkIsolation,

        [RMVSphereMigrationNetworkConfiguration] $MigrationNetworkConfig,

        [RMVSphereIsolatedNetworkConfiguration] $IsolatedNetworkConfig,

        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,

        [bool]$DisableAutomaticDNSRegistrationOnTheTarget,

        [Parameter(Mandatory)]
        [string] $ClusterName,

        [Parameter(Mandatory)]
        [string[]] $DatastoreLocations,

        [string] $FolderName,

        [string] $DatastoreClusterName,

        [Parameter(Mandatory)]
        [string[]] $DisksProvisioningType,

        [string]  $TransferType,

        [string] $TargetVMVCPUCount,

        [Parameter(Mandatory)]
        [string] $CoresPerSocket,

        [int] $TargetVMMemorySize,

        [string] $ResourcePoolName,

        [Parameter(Mandatory)]
        [int] $HardwareVersion,

        [Parameter(Mandatory)]
        [string] $ToolsPackage,

        [hashtable] $MigrationInstructions,

        [Parameter(Mandatory)]
        [bool] $InPlaceUpgrade,

        [System.Object] $UpgradeOSVersion,

        [bool] $IgnoreValidationErrors
    )

    $CurrentTime = Get-Date -Format "yyyy/MM/dd HH:mm:ss"

    if (0 -ne $TargetVMMemorySize) {
        $MemorySize = $TargetVMMemorySize
    } else {
        $MemorySize = [math]::Round($Source.attributes.memory.total_kb/(1024 * 1024),2)
    }

    if ([string]::IsNullOrEmpty($ScheduledAt)) {
        # To run "now", FE requires that the $ScheduledAt to be null
        $ScheduledAt = $null
    }

    $Routes, $DNS = Get-RMRoutesAndDNS -IsolatedNetworkConfig $IsolatedNetworkConfig -DestinationNICConfig $DestinationNICConfig `
        -EnableDestinationNetworkIsolation $EnableDestinationNetworkIsolation
    $MigrationProfile = @{
        "name"= "powershell-" + $Source.host + "-" + $CurrentTime
        "tags"= @()
        "entitlement"=$Entitlement.id
        "cloud_account"=$CloudAccount.id
        "is_data_only"= $false
        "is_vdi"= $false
        "appliance_id"=$CloudAccount.appliance.id
        "schedule"= $ScheduledAt
        "transfer_mode"="run-once"
        "sources"= @(
            @{
                "source"= $Source.id
                "target_config"= @{
                    "vm_details"= @{
                        "datacenter_name" = "$DatacenterName"
                        "datastore_cluster" = ""
                        "datastore_cluster_name" =  "$DatastoreClusterName"
                        "datastore_locations" = $DatastoreLocations
                        "storage_profile_ids" = @()
                        "storage_profile_names" = @()
                        "disks_provisioning_types" = $DisksProvisioningType
                        "network_name"= $null
                        "folder_name" = "$FolderName"
                        "target_name" = "$ClusterName"
                        "target_type" = "cluster"
                        "resource_pool" = "$ResourcePoolName"
                        "vapp_name" = "$TargetVMName"
                        "flavor"= @{
                            "cpu" = $TargetVMVCPUCount
                            "ram_gb" = $MemorySize
                            "cores_per_socket" =  $CoresPerSocket
                        }
                        "tools_package" = "$ToolsPackage"
                        "tools_removed_exclusions" = @(
                            "ThinPrint",
                            "WYSE",
                            "Buslogic",
                            "Sync"
                        )
                        "hardware_version" = "$HardwareVersion"
                    }
                    "properties" = @{
                        "network" = @{
                            "interfaces" = (Get-RMNetworkInterfaces -IsolatedNetworkConfig $IsolatedNetworkConfig `
                                -DestinationNICConfig $DestinationNICConfig -EnableDestinationNetworkIsolation $EnableDestinationNetworkIsolation)
                            "routes" = $Routes
                            "dns" = $DNS
                            "automatic_dns_registration" = !$DisableAutomaticDNSRegistrationOnTheTarget
                        }
                        "selected_mounts" = $SelectedMounts
                        "name" = $TargetVMName
                        "mounts_new_size" = $ResizeMountPoints
                        "migration_network" = (Get-RMMigrationNetwork -MigrationNetworkConfig $MigrationNetworkConfig -EnableDestinationNetworkIsolation $EnableDestinationNetworkIsolation)
                    }
                    "options"= @{
                        "power_on"= $true
                    }
                }
                "verify_ssl_certificates"= $false
                "transfer_type" = "$TransferType"
                "publish_migration_hub"= $false
                "os_type"= $Source.os_type
                "name"= $Source.name
                "collection_type"= $null
                "shutdown_source"= $ShutdownSource
                "shutdown_target"= $ShutdownTarget
                "remove_source_agent" = $RemoveRMSAgentFromSource
                "remove_target_agent"= $RemoveRMSAgentFromTarget
                "in_place_upgrade"= $InPlaceUpgrade
                "upgrade_os_version"= $UpgradeOSVersion
                "migration_instructions"= $MigrationInstructions
                "data_transfer_port"=  $null
                "ignore_validation_errors"= $IgnoreValidationErrors
                "preflight_warning"= $false
            }
        )
    }

    Invoke-RMMigrationProfilePost -MigrationProfile $MigrationProfile
}

function New-RMVSphereVMBasedMigrationProfile {
    param(
        [System.Object] $CloudAccount,
        [System.Object] $Entitlement,
        [System.Object] $Source,
        [System.Object] $ScheduledAt,
        [string] $TargetVMName,
        [System.Object[]] $SelectedDisk,
        [string[]] $SelectedDiskIndex,
        [string] $TargetVMVCPUCount,
        [string] $CoresPerSocket,
        [int] $TargetVMMemorySize,
        [string] $DatacenterName,
        [bool] $PreserveIPAddress,
        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,
        [bool] $PreserveMACAddress,
        [hashtable] $VMTag,
        [string] $ClusterName,
        [string] $ResourcePoolName,
        [string] $FolderName,
        [string] $DatastoreClusterName,        
        [string[]] $DatastoreLocation,
        [hashtable] $StorageProfileId,
        [hashtable] $StorageProfileName,
        [System.Object[]] $DiskProvisioningType,
        [bool] $UpgradeTool,
        [string] $HardwareVersion,
        [string] $ToolsPackage,
        [bool] $ShutdownSource,
        [bool] $ShutdownTarget,
        [bool] $FinalizeMigration,
        [hashtable] $MigrationInstruction,
        [bool] $IgnoreValidationErrors
    )

    $StorageProfileIds, $StorageProfileNames = Get-RMStorageProfileIdAndName -Source $Source `
        -SelectedDiskIndex $SelectedDiskIndex -StorageProfileId $StorageProfileId -StorageProfileName $StorageProfileName
    $IncludeDiskLabels, $ExcludedDiskIdentifiers = Get-RMVMBasedSourceDiskInfo -Source $Source `
        -SelectedDiskIndex $SelectedDiskIndex -SelectedDisk $SelectedDisk
    $NetworkInterfaces = Get-RMNetworkInterfaces -DestinationNICConfig $DestinationNICConfig
    if ($PreserveMACAddress) {
        $NIC = $NetworkInterfaces["eth0"]
        $NIC.Add("mac_addr_type", "manual")
        $NIC.Add("mac_addr", $Source.attributes.network.interfaces.psobject.properties.Value.mac_addr)
        $NetworkInterfaces["eth0"] = $NIC
    }

    $Routes, $DNS = Get-RMRoutesAndDNS -DestinationNICConfig $DestinationNICConfig
    if (0 -eq $TargetVMMemorySize) {
        $TargetVMMemorySize = [math]::Round($Source.attributes.memory.total_kb/(1024 * 1024))
    }

    $CurrentTime = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
    $MigrationProfile = @{
        "name" = "powershell-" + $Source.host + "-" + $CurrentTime
        "tags" = @()
        "entitlement" = $Entitlement.id
        "cloud_account" = $CloudAccount.id
        "is_data_only" = $false
        "is_vdi" = $false
        "appliance_id" = $CloudAccount.appliance.id
        "schedule" = $ScheduledAt
        "transfer_mode" = "run-once"
        "sources" = @(
            @{
                "source" = $Source.id
                "target_config" = @{
                    "vm_details" = @{
                        "datacenter_name" = $DatacenterName
                        "datastore_cluster_name" = $DatastoreClusterName
                        "datastore_locations" = $DatastoreLocation
                        "storage_profile_ids" = $StorageProfileIds
                        "storage_profile_names" = $StorageProfileNames
                        "disks_provisioning_types" = $DiskProvisioningType
                        "network_name" = $null
                        "folder_name" = $FolderName
                        "target_name" = $ClusterName
                        "target_type" = "cluster"
                        "resource_pool" = $ResourcePoolName
                        "vapp_name" = $TargetVMName
                        "flavor" = @{
                            "cpu"= $TargetVMVCPUCount
                            "ram_gb" = $TargetVMMemorySize
                            "cores_per_socket" = $CoresPerSocket
                        }
                        "tools_package" = $ToolsPackage
                        "tools_removed_exclusions" = @("ThinPrint", "WYSE", "Buslogic", "Sync")
                        "hardware_version" = $HardwareVersion
                        "tags"= (Get-RMVMTagsForMigrationProfile -SelectedVMTag $VMTag)
                        "upgrade_vm_tools" = $UpgradeTool
                        "preserve_network_config" = $PreserveIPAddress
                        "excluded_disks_identifiers" = $ExcludedDiskIdentifiers
                        "included_disk_labels" = $IncludeDiskLabels
                    }
                    "properties" = @{
                        "network" = @{
                            "interfaces" = $NetworkInterfaces
                            "routes" = $Routes
                            "dns" = $DNS
                        }
                        "selected_mounts" = $SelectedDisk
                        "name" = $TargetVMName
                        "mounts_new_size" = @{}
                    }
                    "options" = @{
                        "power_on" = $true
                    }
                }
                "verify_ssl_certificates" = $false
                "drive_mapping" = @()
                "os_type" = $Source.os_type
                "name" = $TargetVMName
                "collection_type" = "vm"
                "in_place_upgrade" = $false
                "upgrade_os_version" = $null
                "migration_instructions" = $MigrationInstruction
                "ignore_validation_errors" = $IgnoreValidationErrors
                "preflight_warning" = $false
            }
        )
    }

    Invoke-RMMigrationProfilePost -MigrationProfile $MigrationProfile    
}

function Get-RMNetworkInterfaces {
    param(
        [RMVSphereIsolatedNetworkConfiguration] $IsolatedNetworkConfig,
        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,
        [bool] $EnableDestinationNetworkIsolation
    )
    $NetworkInterfaces = @{}
    if ($EnableDestinationNetworkIsolation) {
        $NetworkName, $DvSwitchName = $IsolatedNetworkConfig.GetNetworkAndDvSwitchName(`
            $IsolatedNetworkConfig.IsolatedNetworkName)
        $NIC = @{"ip_type" = $IsolatedNetworkConfig.IPType.ToLower(); "type" = "Ethernet";
            "network_name" = $NetworkName; "dv_switch_name" = $DvSwitchName;
            "ip_addr" = $IsolatedNetworkConfig.IPAddress; "netmask" = $IsolatedNetworkConfig.Netmask}
        $NetworkInterfaces.Add("eth0", $NIC)        
        return $NetworkInterfaces
    }

    for ($Index = 0; $Index -lt $DestinationNICConfig.Count; $Index++) {
        $NetworkName, $DvSwitchName = $DestinationNICConfig[$Index].GetNetworkAndDvSwitchName(`
            $DestinationNICConfig[$Index].DestinationNetworkName)
        $NIC = @{"ip_type" = $DestinationNICConfig[$Index].IPType.ToLower(); "type" = "Ethernet";
            "network_name" = $NetworkName; "dv_switch_name" = $DvSwitchName;
            "ip_addr" = $DestinationNICConfig[$Index].IPAddress; "netmask" = $DestinationNICConfig[$Index].Netmask}
        $NetworkInterfaces.Add("eth" + $Index, $NIC)
    }

    return $NetworkInterfaces
}

function Get-RMRoutesAndDNS {
    param(
        [RMVSphereIsolatedNetworkConfiguration] $IsolatedNetworkConfig,
        [RMVSphereDestinationNetworkConfiguration[]] $DestinationNICConfig,
        [bool] $EnableDestinationNetworkIsolation
    )
    $Routes = @{}
    $DNS = @{}
    if ($EnableDestinationNetworkIsolation) {
        if ($IsolatedNetworkConfig.IPType -ieq "dhcp") {
            return $null, $null
        }
        $Routes.Add("default_gateway", $IsolatedNetworkConfig.DefaultGateway)
        $DNS.Add("primary", $IsolatedNetworkConfig.PrimaryDNS)
        $DNS.Add("secondary", $IsolatedNetworkConfig.SecondaryDNS)
        return $Routes, $DNS
    }

    $NICConfig = Get-RMDestinationNICConfigWithGatewayAndDNS -DestinationNICConfig $DestinationNICConfig
    $Routes.Add("default_gateway", $NICConfig.DefaultGateway)
    $DNS.Add("primary", $NICConfig.PrimaryDNS)
    $DNS.Add("secondary", $NICConfig.SecondaryDNS)
    return $Routes, $DNS
}

function Get-RMMigrationNetwork {
    param(
        [RMVSphereMigrationNetworkConfiguration] $MigrationNetworkConfig,
        [bool] $EnableDestinationNetworkIsolation
    )
    if (!$EnableDestinationNetworkIsolation) {
        return $null
    }
    $MigrationNetwork = @{}
    $NetworkName, $DvSwitchName = $MigrationNetworkConfig.GetNetworkAndDvSwitchName($MigrationNetworkConfig.MigrationNetworkName)
    $Routes = @{"default_gateway" = $MigrationNetworkConfig.DefaultGateway;}
    $DNS = @{"primary" = $MigrationNetworkConfig.PrimaryDNS; "secondary" = $MigrationNetworkConfig.SecondaryDNS;}
    $Interface = @{"ip_type" = $MigrationNetworkConfig.IPType.ToLower(); "type" = "Ethernet";
        "network_name" = $NetworkName; "dv_switch_name" = $DvSwitchName;
        "ip_addr" = $MigrationNetworkConfig.IPAddress; "netmask" = $MigrationNetworkConfig.Netmask}

    $MigrationNetwork.Add("routes", $Routes)
    $MigrationNetwork.Add("dns", $DNS)
    $MigrationNetwork.Add("interface", $Interface)
    return $MigrationNetwork
}

function Get-RMStorageProfileIdAndName {
    param(
        [System.Object] $Source,
        [string[]] $SelectedDiskIndex,
        [hashtable] $StorageProfileId,
        [hashtable] $StorageProfileName
    )
    $ResultIds = @()
    $ResultNames = @()
    for ($Index = 0; $Index -lt $Source.attributes.storage.vm_disks.Count; $Index++) {
        $Disk = $Source.attributes.storage.vm_disks[$Index]
        if ($null -ne $StorageProfileId -and $StorageProfileId.ContainsKey($Disk.label)) {
            $ResultIds += $StorageProfileId[$Disk.label]
        } else {
            $ResultIds += ""
        }

        if ($null -ne $StorageProfileName -and $StorageProfileName.ContainsKey($Disk.label)) {
            $ResultNames += $StorageProfileName[$Disk.label]
        } elseif ($SelectedDiskIndex -contains $Index) {
            $ResultNames += "Datastore Default"
        } else {
            $ResultNames += $null
        }
    }

    return $ResultIds, $ResultNames
}

function Get-RMVMTagsForMigrationProfile {
    param(
        [hashtable] $SelectedVMTag
    )
    $ResultTags = @()
    foreach ($Key in $SelectedVMTag.Keys) {
        $Tags = $SelectedVMTag[$Key]
        foreach ($Tag in $Tags) {
            $TagEntry = @{}
            $TagEntry.Add("tag_id", $Tag.Identifier)
            $TagEntry.Add("tag_name", $Tag.Name)
            $TagEntry.Add("category_name", $Key)
            $ResultTags += $TagEntry
        }
    }

    return $ResultTags
}

function Get-RMVMBasedSourceDiskInfo {
    param(
        [System.Object] $Source,
        [string[]] $SelectedDiskIndex,
        [hashtable[]] $SelectedDisk
    )

    $IncludedDiskLabels = @()
    $ExcludedDiskIdentifiers = @()
    for ($Index = 0; $Index -lt $Source.attributes.storage.vm_disks.Count; $Index++) {
        if ($SelectedDiskIndex -contains $Index) {
            $IncludedDiskLabels += $Source.attributes.storage.vm_disks[$Index].label
        } else {
            $ExcludedDiskIdentifiers += $Source.attributes.storage.vm_disks[$Index].disk_object_id
        }
    }

    return $IncludedDiskLabels, $ExcludedDiskIdentifiers
}


Export-ModuleMember -Function New-RMVSphereMigrationProfile, New-RMVSphereVMBasedMigrationProfile