zvm_dns_scripts_utils.ps1


function Test-Netplan {
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    try {
        # test -L checks if file is a symlink, which indicates netplan is used
        $commandAddDnsEntry = "FILE=/etc/resolv.conf; test -e `$FILE || exit 1; test -L `$FILE && echo 'SYMLINK' || echo 'REGULAR'"

        $res = Invoke-ZVMLScript -ScriptText $commandAddDnsEntry
        if ($res.ExitCode -ne 0) {
            throw "Cannot test resolv.conf, $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }

        switch ($res.ScriptOutput.Trim()) {
            'SYMLINK' { return $true }
            'REGULAR' { return $false }
            default { throw "Unexpected file type: $($res.ScriptOutput.Trim())" }
        }
    }
    catch {
        throw "Failed to test Netplan. Problem: $_"
    }
}

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

    try {
        $command = 'sudo netplan apply'
        $res = Invoke-ZVMLScript -ScriptText $command
        if ($res.ExitCode -ne 0) {
            throw $res.ScriptOutput # ScriptOutput should contain bash error
        }
        Write-Host "Netplan configurations applied successfully."
    }
    catch {
        throw "Failed apply Netplan configurations. Problem: $_"
    }
}

function Get-NameserversNetplan {
    $netplan_handling_file = "netplan_utils.py"

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

    try {
        Invoke-CmdletWithPyScript -ScriptName $netplan_handling_file -Action {

            $scriptPath = "/opt/zerto/zlinux/avs/" + $netplan_handling_file

            $scriptParameters = @{
                'action' = 'get'
            }
            $commandToExecute = Build-SafeShellPyCommand -PyScriptPath $scriptPath -PyScriptParams $scriptParameters

            $result = Invoke-ZVMLScriptWithTimeout -ScriptText $commandToExecute -ActionName 'Get nameservers netplan' -TimeoutMinutes 3

            if ($result.ScriptOutput.Contains("Success")) {
                Write-Host "Nameservers were obtained successfully."
                $cleanResultMsg = $result.ScriptOutput -replace "Success", ""
                $ips = $cleanResultMsg | ConvertFrom-Json
                return $ips | ForEach-Object {
                    [System.Net.IPAddress]$_
                }
            }
            else {
                if ($result.ScriptOutput.Contains("Error:")) {
                    $cleanErrMsg = $result.ScriptOutput -replace "Error: ", ""
                    throw $cleanErrMsg
                }
                throw "Unexpected error occurred while getting nameservers."
            }
        }
    }
    catch {
        throw "Failed to get Netplan ZVML DNS nameserver entries. Problem: $_"
    }
}

function Add-NameserverNetplan ([System.Net.IPAddress]$DnsIp) {
    $netplan_handling_file = "netplan_utils.py"

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

    try {
        Invoke-CmdletWithPyScript -ScriptName $netplan_handling_file -Action {

            $scriptPath = "/opt/zerto/zlinux/avs/" + $netplan_handling_file

            $scriptParameters = @{
                'action' = 'add'
                'value'  = $DnsIp
            }
            $commandToExecute = Build-SafeShellPyCommand -PyScriptPath $scriptPath -PyScriptParams $scriptParameters

            $result = Invoke-ZVMLScriptWithTimeout -ScriptText $commandToExecute -ActionName 'Add nameserver netplan' -TimeoutMinutes 3

            if ($result.ScriptOutput.Contains("Success")) {
                ApplyNetplanConfigurations

                Write-Host "Nameserver added successfully."
            }
            else {
                if ($result.ScriptOutput.Contains("Error:")) {
                    $cleanErrMsg = $result.ScriptOutput -replace "Error: ", ""
                    throw $cleanErrMsg
                }
                throw "Unexpected error occurred while adding nameserver."
            }
        }
    }
    catch {
        throw "Failed to add Netplan ZVML DNS nameserver entry. Problem: $_"
    }
}

function Remove-NameserverNetplan ([System.Net.IPAddress]$DnsIp) {
    $netplan_handling_file = "netplan_utils.py"

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

    try {
        Invoke-CmdletWithPyScript -ScriptName $netplan_handling_file -Action {

            $scriptPath = "/opt/zerto/zlinux/avs/" + $netplan_handling_file

            $scriptParameters = @{
                'action' = 'remove'
                'value'  = $DnsIp
            }
            $commandToExecute = Build-SafeShellPyCommand -PyScriptPath $scriptPath -PyScriptParams $scriptParameters

            $result = Invoke-ZVMLScriptWithTimeout -ScriptText $commandToExecute -ActionName 'Remove nameserver netplan' -TimeoutMinutes 3

            if ($result.ScriptOutput.Contains("Success")) {
                ApplyNetplanConfigurations

                Write-Host "Nameserver removed successfully."
            }
            else {
                if ($result.ScriptOutput.Contains("Error:")) {
                    $cleanErrMsg = $result.ScriptOutput -replace "Error: ", ""
                    throw $cleanErrMsg
                }
                throw "Unexpected error occurred while removing nameserver."
            }
        }
    }
    catch {
        throw "Failed to remove Netplan ZVML DNS nameserver entry. Problem: $_"
    }
}

#region Legacy resolv.conf functions

function Get-Nameservers {
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    try {
        # Unlock resolv.conf file which could have been locked during the installation of previous versions, no need to lock again, fixed in ZER-150945
        $commandUnlockFile = 'sudo chattr -i /etc/resolv.conf'
        $res = Invoke-ZVMLScript -ScriptText $commandUnlockFile
        if ($res.ExitCode -ne 0) {
            throw "Cannot unlock resolv.conf. $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }

        # awk searches for lines starting with 'nameserver' and prints the second field (the IP address)
        $commandGetDnsEntries = "awk '/^[[:space:]]*nameserver[[:space:]]/ {print `$2}' /etc/resolv.conf || exit 1"

        $res = Invoke-ZVMLScript -ScriptText $commandGetDnsEntries
        if ($res.ExitCode -ne 0) {
            throw "Cannot read resolv.conf, $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }
        $output = $res.ScriptOutput
        $ips = $output -split "`n"

        return $ips | ForEach-Object {
            [System.Net.IPAddress]$_
        }
    }
    catch {
        throw "Failed to get ZVML DNS nameserver entries. Problem: $_"
    }
}

function Add-Nameserver ([System.Net.IPAddress]$DnsIp) {
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    try {
        # tee adds the line to the end of file
        $commandAddDnsEntry = "echo 'nameserver $DnsIp # AVS $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')' | sudo tee -a /etc/resolv.conf || exit 1"

        $res = Invoke-ZVMLScript -ScriptText $commandAddDnsEntry
        if ($res.ExitCode -ne 0) {
            throw "Cannot edit resolv.conf, $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }
    }
    catch {
        throw "Failed to add ZVML DNS nameserver entry. Problem: $_"
    }
}

function Remove-Nameserver ([System.Net.IPAddress]$DnsIp) {
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    try {
        # sed /{pattern}/{command} with /d removes the matched line starting with 'nameserver'
        $commandRemoveDnsEntry = "sudo sed -i '/^[[:space:]]*nameserver[[:space:]]\+$DnsIp/d' /etc/resolv.conf || exit 1"

        $res = Invoke-ZVMLScript -ScriptText $commandRemoveDnsEntry
        if ($res.ExitCode -ne 0) {
            throw "Cannot edit resolv.conf, $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }
    }
    catch {
        throw "Failed to remove ZVML DNS nameserver entry. Problem: $_"
    }
}

function Check-Nameserver ([System.Net.IPAddress]$DnsIp) {
    Write-Host "Starting $($MyInvocation.MyCommand)..."

    try {
        $destinationHost = "management.azure.com"

        # dig queries DNS using the specified DNS server for the specified host
        $commandCheckDnsEntry = "dig @$DnsIp $destinationHost A +short +time=10 +retry=3 || exit 1"

        $res = Invoke-ZVMLScript -ScriptText $commandCheckDnsEntry
        if ($res.ExitCode -ne 0) {
            throw "Cannot query DNS, $($res.ScriptOutput)" # ScriptOutput should contain bash error
        }
    }
    catch {
        throw "Failed to check ZVML DNS nameserver entry. Problem: $_"
    }
}

#endregion