common_utils.ps1

$ZVM_VM_NAME = "ZVML"
$VRA_VM_PATTERN = "Z-VRA*"
$ZERTO_FOLDER_ON_HOST = "/var/zerto"

function Start-ZVM {

    try {
        Write-Host "Starting $($MyInvocation.MyCommand)..."

        $ZVM = Get-VM -Name $ZVM_VM_NAME
        if ($null -eq $ZVM) {
            throw "$ZVM_VM_NAME VM does not exist."
        }
        else {
            if ($ZVM.PowerState -eq 'PoweredOff') {
                Write-Host "$ZVM_VM_NAME is powered off, going to power it on"
                Start-VM $ZVM -ErrorAction Stop | Out-Null
            }
            else {
                Write-Host "$ZVM_VM_NAME is up and running"
            }
        }
    }
    catch {
        throw "Failed to start $ZVM_VM_NAME. Problem: $_"
    }
}

function Stop-ZVM {

    try {
        Write-Host "Starting $($MyInvocation.MyCommand)..."

        $ZVM = Get-VM -Name $ZVM_VM_NAME
        if ($null -eq $ZVM) {
            throw "$ZVM_VM_NAME VM does not exist."
        }
        else {
            if ($ZVM.PowerState -eq 'PoweredOn') {
                Write-Host "$ZVM_VM_NAME is powered on, going to power it off"
                Stop-VM -VM $ZVM -Confirm:$False -ErrorAction Stop | Out-Null
            }
            else {
                Write-Host "$ZVM_VM_NAME is off"
            }
        }
    }
    catch {
        throw "Failed to stop $ZVM_VM_NAME. Problem: $_"
    }
}

function New-RandomPassword {

    # Generate a password with at least 4 uppercase, 4 lowercase, 3 digits and 3 special characters from !@#%^*()
    # The first character must be a letter
    # The password length will vary between 14 and 18 symbols

    Write-Host "Starting $($MyInvocation.MyCommand)..."

    $extlen = { Get-SecureRandom(0, 1) }

    $firstRand = 'a'..'z' | Get-SecureRandom
    $lowerRand = 'a'..'z' | Get-SecureRandom -Count (3 + (& $extlen)) # Eventually, the password will have at least 4 lowercase letters when firstRand is added
    $upperRand = 'A'..'Z' | Get-SecureRandom -Count (4 + (& $extlen))
    $numericRand = '0'..'9' | Get-SecureRandom -Count (3 + (& $extlen))
    $specialRand = [char[]]'!@#%^*()' | Get-SecureRandom -Count (3 + (& $extlen))

    $allRand = $upperRand + $lowerRand + $numericRand + $specialRand

    $shuffledRand = $allRand | Get-SecureRandom -Count $allRand.Length

    $password = $firstRand + ($shuffledRand -join '')

    return $password
}

function New-ZertoFolderOnHost {
    param(
        [Parameter(Mandatory = $true,
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName
    )

    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."

        $Command = "mkdir -p $ZERTO_FOLDER_ON_HOST"
        $Res = Invoke-SSHCommands -HostName $HostName -Commands $Command
        $ExitStatus = $Res["0_exitStatus"];

        if ( $ExitStatus -ne '0' ) {
            throw "failed to create $ZERTO_FOLDER_ON_HOST on host $HostName. Exit status for ""$Command"" is $ExitStatus"
        }
        else {
            Write-Host "Zerto folder ($ZERTO_FOLDER_ON_HOST) was created on $HostName."
        }
    }
}

function Invoke-Retry {
    param (
        [Parameter(Mandatory = $true)]
        [ScriptBlock]$Action,

        [Parameter(Mandatory = $true)]
        [string]$ActionName,

        [Parameter(Mandatory = $true)]
        [int]$RetryCount,

        [Parameter(Mandatory = $true)]
        [int]$RetryIntervalSeconds
    )

    for ($i = 1; $i -le $RetryCount; $i++) {
        try {
            Write-Output "Attempt $i of $RetryCount executing $ActionName."
            & $Action
            Write-Output "$ActionName succeeded."
            return
        }
        catch {
            Write-Output "Attempt $i of $RetryCount on $ActionName wasn't successful."

            if ($i -ne $RetryCount) {
                Write-Output "Waiting for $RetryIntervalSeconds seconds before retrying $ActionName..."
                Start-Sleep -Seconds $RetryIntervalSeconds
            }
            else {
                Write-Output "Error: All attempts to execute $ActionName failed. Exiting."
                throw
            }
        }
    }
}

function Get-DatastoreUUID ($DatastoreName) {
    Write-Host "Starting $($MyInvocation.MyCommand)..."
    $datastore = Get-Datastore -Name $DatastoreName
    $uuid = ($datastore.ExtensionData.Info.Url -replace '.*volumes/', '').TrimEnd('/') # Url format is 'ds:///vmfs/volumes/vsan:529ecf79732d3104-202d349462b20b76/'
    Write-Host "Datastore '$DatastoreName' UUID: $uuid"
    return $uuid
}

function Invoke-SSHCommands {
    param(
        [Parameter(Mandatory = $true,
            HelpMessage = "Host Name to connect with SSH")]
        [string]$HostName,
        [Parameter(Mandatory = $true,
            HelpMessage = "Commands to execute")]
        [String[]]$Commands,
        [Parameter(Mandatory = $false,
            HelpMessage = "Action on exitStatus 1")]
        [string]$ExitStatusAction = "Stop"
    )

    process {
        Write-Host "Starting $($MyInvocation.MyCommand)..."

        $NamedOutputs = @{}
        Set-Variable -Name NamedOutputs -Value $NamedOutputs -Scope Global

        $i = 0
        foreach ($Command in $Commands) {
            $SSH = Invoke-SSHCommand -SSHSession $SSH_Sessions[$HostName].Value -Command $Command

            if (!$SSH) {
                throw "Failed to Invoke-SSHCommand '$Command' on host $HostName"
            }

            $ExitStatus = $SSH.ExitStatus
            $SshError = $SSH.Error
            $Output = ($SSH.Output -join ";")

            if ($ExitStatus -ne 0 -Or $SshError) {
                if (($ExitStatus -eq 1) -And (!$SshError) -And ($ExitStatusAction -eq "Skip")) {
                    Write-Host "ExitStatus of '$Command' is 1, while ExitStatusAction = Skip. Skipping..."
                }
                else {
                    throw "Failed to run '$Command' on host $HostName, ExitStatus: $ExitStatus, Output: $Output, Error: $SshError, ExitStatusAction: $ExitStatusAction"
                }
            }

            Write-Host "Command was completed successfully '$Command' on host $HostName, ExitStatus: $ExitStatus, Output: $Output, Error: $SshError"

            $NamedOutputs["$($i)_cmd"] = $Command
            $NamedOutputs["$($i)_exitStatus"] = $ExitStatus
            $NamedOutputs["$($i)_output"] = $Output
            $NamedOutputs["$($i)_error"] = $SshError

            $i++;
        }

        return $NamedOutputs
    }
}