diff options
Diffstat (limited to 'scripts/testing/lib')
| -rw-r--r-- | scripts/testing/lib/network-diagnostics.sh | 24 | ||||
| -rw-r--r-- | scripts/testing/lib/validation.sh | 11 | ||||
| -rwxr-xr-x | scripts/testing/lib/vm-utils.sh | 471 |
3 files changed, 285 insertions, 221 deletions
diff --git a/scripts/testing/lib/network-diagnostics.sh b/scripts/testing/lib/network-diagnostics.sh index 3f9735b..f7cae11 100644 --- a/scripts/testing/lib/network-diagnostics.sh +++ b/scripts/testing/lib/network-diagnostics.sh @@ -3,27 +3,30 @@ # Author: Craig Jennings <craigmartinjennings@gmail.com> # License: GNU GPLv3 -# Note: logging.sh should already be sourced by the calling script +# Note: logging.sh and vm-utils.sh should already be sourced by the calling script +# Uses globals: ROOT_PASSWORD, SSH_PORT, SSH_OPTS, VM_IP (from vm-utils.sh or calling script) # Run quick network diagnostics -# Args: $1 = VM IP address or hostname run_network_diagnostics() { - local vm_host="$1" + local password="${ROOT_PASSWORD:-archsetup}" + local port="${SSH_PORT:-22}" + local host="${VM_IP:-localhost}" + local ssh_base="sshpass -p $password ssh $SSH_OPTS -p $port root@$host" section "Pre-flight Network Diagnostics" - # Test 1: Basic connectivity + # Test 1: Basic connectivity (use curl instead of ping - SLIRP may not handle ICMP) step "Testing internet connectivity" - if sshpass -p 'archsetup' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$vm_host "ping -c 3 8.8.8.8 >/dev/null 2>&1"; then + if $ssh_base "curl -s --connect-timeout 5 -o /dev/null http://archlinux.org" 2>/dev/null; then success "Internet connectivity OK" else error "No internet connectivity" return 1 fi - # Test 2: DNS resolution + # Test 2: DNS resolution (use getent which is always available, unlike nslookup/dig) step "Testing DNS resolution" - if sshpass -p 'archsetup' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$vm_host "nslookup archlinux.org >/dev/null 2>&1"; then + if $ssh_base "getent hosts archlinux.org >/dev/null 2>&1" 2>/dev/null; then success "DNS resolution OK" else error "DNS resolution failed" @@ -32,7 +35,7 @@ run_network_diagnostics() { # Test 3: Arch mirror accessibility step "Testing Arch mirror access" - if sshpass -p 'archsetup' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$vm_host "curl -s -I https://mirrors.kernel.org/archlinux/ | head -1 | grep -qE '(200|301)'"; then + if $ssh_base "curl -s -I https://mirrors.kernel.org/archlinux/ | head -1 | grep -qE '(200|301)'" 2>/dev/null; then success "Arch mirrors accessible" else error "Cannot reach Arch mirrors" @@ -41,7 +44,7 @@ run_network_diagnostics() { # Test 4: AUR accessibility step "Testing AUR access" - if sshpass -p 'archsetup' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$vm_host "curl -s -I https://aur.archlinux.org/ | head -1 | grep -qE '(200|405)'"; then + if $ssh_base "curl -s -I https://aur.archlinux.org/ | head -1 | grep -qE '(200|405)'" 2>/dev/null; then success "AUR accessible" else error "Cannot reach AUR" @@ -50,8 +53,7 @@ run_network_diagnostics() { # Show network info info "Network configuration:" - sshpass -p 'archsetup' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null root@$vm_host \ - "ip addr show | grep 'inet ' | grep -v '127.0.0.1'" 2>/dev/null | while read line; do + $ssh_base "ip addr show | grep 'inet ' | grep -v '127.0.0.1'" 2>/dev/null | while read line; do info " $line" done diff --git a/scripts/testing/lib/validation.sh b/scripts/testing/lib/validation.sh index 8c4787e..3191c64 100644 --- a/scripts/testing/lib/validation.sh +++ b/scripts/testing/lib/validation.sh @@ -20,7 +20,7 @@ declare -a UNKNOWN_ISSUES # SSH helper (uses globals: VM_IP, ROOT_PASSWORD) ssh_cmd() { sshpass -p "$ROOT_PASSWORD" ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - -o ConnectTimeout=10 "root@$VM_IP" "$@" 2>/dev/null + -o ConnectTimeout=10 -p "${SSH_PORT:-22}" "root@$VM_IP" "$@" 2>/dev/null } # Validation result helpers @@ -458,17 +458,12 @@ validate_hyprland_tools() { local missing="" # Check core Hyprland packages - for pkg in hyprland hypridle hyprlock waybar wofi swww grim slurp gammastep; do + for pkg in hyprland hypridle hyprlock waybar fuzzel swww grim slurp gammastep foot; do if ! ssh_cmd "pacman -Q $pkg &>/dev/null"; then missing="$missing $pkg" fi done - # st should still be installed (via XWayland) - if ! ssh_cmd "test -f /usr/local/bin/st"; then - missing="$missing st" - fi - if [ -z "$missing" ]; then validation_pass "All Hyprland tools installed" else @@ -483,7 +478,7 @@ validate_hyprland_config() { for config in ".config/hypr/hyprland.conf" ".config/hypr/hypridle.conf" \ ".config/hypr/hyprlock.conf" ".config/waybar/config" \ - ".config/wofi/config" ".config/gammastep/config.ini"; do + ".config/fuzzel/fuzzel.ini" ".config/gammastep/config.ini"; do if ! ssh_cmd "test -f /home/cjennings/$config"; then missing="$missing $config" fi diff --git a/scripts/testing/lib/vm-utils.sh b/scripts/testing/lib/vm-utils.sh index 81aec33..47bd391 100755 --- a/scripts/testing/lib/vm-utils.sh +++ b/scripts/testing/lib/vm-utils.sh @@ -1,39 +1,52 @@ #!/bin/bash -# VM management utilities for archsetup testing +# VM management utilities for archsetup testing (direct QEMU) # Author: Craig Jennings <craigmartinjennings@gmail.com> # License: GNU GPLv3 +# +# Manages QEMU VMs directly without libvirt. Uses user-mode networking +# with port forwarding for SSH access and qemu-img for snapshots. # Note: logging.sh should already be sourced by the calling script # VM configuration defaults VM_CPUS="${VM_CPUS:-4}" -VM_RAM="${VM_RAM:-8192}" # MB -VM_DISK="${VM_DISK:-50}" # GB -VM_NETWORK="${VM_NETWORK:-default}" -LIBVIRT_URI="qemu:///system" # Use system session, not user session - -# Check if libvirt is running -check_libvirt() { - if ! systemctl is-active --quiet libvirtd; then - error "libvirtd service is not running" - info "Start it with: sudo systemctl start libvirtd" - return 1 - fi - return 0 +VM_RAM="${VM_RAM:-4096}" # MB +VM_DISK_SIZE="${VM_DISK_SIZE:-50}" # GB + +# SSH configuration +SSH_PORT="${SSH_PORT:-2222}" +SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10" +ROOT_PASSWORD="${ROOT_PASSWORD:-archsetup}" + +# OVMF firmware paths +OVMF_CODE="/usr/share/edk2/x64/OVMF_CODE.4m.fd" +OVMF_VARS_TEMPLATE="/usr/share/edk2/x64/OVMF_VARS.4m.fd" + +# VM runtime paths (set by init_vm_paths) +VM_IMAGES_DIR="" +DISK_PATH="" +OVMF_VARS="" +PID_FILE="" +MONITOR_SOCK="" +SERIAL_LOG="" + +# Initialize all VM paths from images directory +# Must be called before any other vm-utils function +init_vm_paths() { + local images_dir="${1:-$VM_IMAGES_DIR}" + [ -z "$images_dir" ] && fatal "VM_IMAGES_DIR not set" + + VM_IMAGES_DIR="$images_dir" + DISK_PATH="$VM_IMAGES_DIR/archsetup-base.qcow2" + OVMF_VARS="$VM_IMAGES_DIR/OVMF_VARS.fd" + PID_FILE="$VM_IMAGES_DIR/qemu.pid" + MONITOR_SOCK="$VM_IMAGES_DIR/qemu-monitor.sock" + SERIAL_LOG="$VM_IMAGES_DIR/qemu-serial.log" + mkdir -p "$VM_IMAGES_DIR" } -# Check if user is in libvirt group -check_libvirt_group() { - if ! groups | grep -q libvirt; then - warn "Current user is not in libvirt group" - info "Add yourself with: sudo usermod -a -G libvirt $USER" - info "Then log out and back in for changes to take effect" - return 1 - fi - return 0 -} +# ─── Prerequisite Checks ───────────────────────────────────────────── -# Check if KVM is available check_kvm() { if [ ! -e /dev/kvm ]; then error "KVM is not available" @@ -44,249 +57,311 @@ check_kvm() { return 0 } -# Wait for VM to boot (check for SSH or serial console) -wait_for_vm() { - local vm_name="$1" - local timeout="${2:-300}" # 5 minutes default - local elapsed=0 +check_qemu() { + if ! command -v qemu-system-x86_64 &>/dev/null; then + error "qemu-system-x86_64 not found" + info "Install with: sudo pacman -S qemu-full" + return 1 + fi + return 0 +} - progress "Waiting for VM $vm_name to boot..." +check_ovmf() { + if [ ! -f "$OVMF_CODE" ]; then + error "OVMF firmware not found: $OVMF_CODE" + info "Install with: sudo pacman -S edk2-ovmf" + return 1 + fi + return 0 +} - while [ $elapsed -lt $timeout ]; do - if virsh --connect "$LIBVIRT_URI" domstate "$vm_name" 2>/dev/null | grep -q "running"; then - sleep 5 - complete "VM $vm_name is running" - return 0 - fi - sleep 2 - elapsed=$((elapsed + 2)) - done +check_sshpass() { + if ! command -v sshpass &>/dev/null; then + error "sshpass not found" + info "Install with: sudo pacman -S sshpass" + return 1 + fi + return 0 +} - error "Timeout waiting for VM $vm_name to boot" - return 1 +check_socat() { + if ! command -v socat &>/dev/null; then + error "socat not found" + info "Install with: sudo pacman -S socat" + return 1 + fi + return 0 } -# Check if VM exists -vm_exists() { - local vm_name="$1" - virsh --connect "$LIBVIRT_URI" dominfo "$vm_name" &>/dev/null - return $? +check_prerequisites() { + local failed=0 + check_kvm || failed=1 + check_qemu || failed=1 + check_ovmf || failed=1 + check_sshpass || failed=1 + check_socat || failed=1 + return $failed } -# Check if VM is running +# ─── VM Lifecycle ───────────────────────────────────────────────────── + +# Check if a QEMU VM is running via PID file vm_is_running() { - local vm_name="$1" - [ "$(virsh --connect "$LIBVIRT_URI" domstate "$vm_name" 2>/dev/null)" = "running" ] - return $? -} + [ -f "$PID_FILE" ] || return 1 -# Start VM -start_vm() { - local vm_name="$1" + local pid + pid=$(cat "$PID_FILE" 2>/dev/null) || return 1 - if vm_is_running "$vm_name"; then - warn "VM $vm_name is already running" + if kill -0 "$pid" 2>/dev/null && grep -q "qemu" "/proc/$pid/cmdline" 2>/dev/null; then return 0 fi - step "Starting VM: $vm_name" - if virsh --connect "$LIBVIRT_URI" start "$vm_name" >> "$LOGFILE" 2>&1; then - success "VM $vm_name started" - return 0 - else - error "Failed to start VM $vm_name" + # Stale PID file + rm -f "$PID_FILE" + return 1 +} + +# Start a QEMU VM +# Args: $1 = disk path +# $2 = boot mode: "iso" or "disk" (default: disk) +# $3 = ISO path (required if mode=iso) +# $4 = display: "none" (headless) or "gtk" (graphical, default: none) +start_qemu() { + local disk="$1" + local mode="${2:-disk}" + local iso_path="${3:-}" + local display="${4:-none}" + + # Stop any existing instance + stop_qemu 2>/dev/null || true + + # Check port availability + if ss -tln 2>/dev/null | grep -q ":${SSH_PORT} "; then + error "Port $SSH_PORT is already in use" + info "Another QEMU instance or service may be running" return 1 fi -} -# Stop VM gracefully -stop_vm() { - local vm_name="$1" - local timeout="${2:-60}" + # Ensure OVMF_VARS exists + if [ ! -f "$OVMF_VARS" ]; then + cp "$OVMF_VARS_TEMPLATE" "$OVMF_VARS" + fi + + # Truncate serial log + : > "$SERIAL_LOG" + + # Build QEMU command + local qemu_cmd=( + qemu-system-x86_64 + -name "archsetup-test" + -machine "q35,accel=kvm" + -cpu host + -smp "$VM_CPUS" + -m "$VM_RAM" + -drive "if=pflash,format=raw,readonly=on,file=$OVMF_CODE" + -drive "if=pflash,format=raw,file=$OVMF_VARS" + -drive "file=$disk,format=qcow2,if=virtio" + -netdev "user,id=net0,hostfwd=tcp::${SSH_PORT}-:22" + -device "virtio-net-pci,netdev=net0" + -monitor "unix:$MONITOR_SOCK,server,nowait" + -pidfile "$PID_FILE" + -serial "file:$SERIAL_LOG" + -usb + -device usb-tablet + ) + + # Boot mode + if [ "$mode" = "iso" ]; then + [ -z "$iso_path" ] && fatal "ISO path required for iso boot mode" + qemu_cmd+=(-cdrom "$iso_path" -boot d) + else + qemu_cmd+=(-boot c) + fi - if ! vm_is_running "$vm_name"; then - info "VM $vm_name is not running" - return 0 + # Display mode + if [ "$display" = "gtk" ]; then + qemu_cmd+=(-device virtio-vga-gl -display "gtk,gl=on") + else + qemu_cmd+=(-display none) fi - step "Shutting down VM: $vm_name" - if virsh --connect "$LIBVIRT_URI" shutdown "$vm_name" >> "$LOGFILE" 2>&1; then - # Wait for graceful shutdown - local elapsed=0 - while [ $elapsed -lt $timeout ]; do - if ! vm_is_running "$vm_name"; then - success "VM $vm_name stopped gracefully" - return 0 - fi - sleep 2 - elapsed=$((elapsed + 2)) - done - - warn "VM $vm_name did not stop gracefully, forcing..." - virsh --connect "$LIBVIRT_URI" destroy "$vm_name" >> "$LOGFILE" 2>&1 + step "Starting QEMU (mode=$mode, display=$display)" + + # Launch in background + "${qemu_cmd[@]}" &>> "$LOGFILE" & + + # Wait for PID file to appear + local wait=0 + while [ ! -f "$PID_FILE" ] && [ $wait -lt 10 ]; do + sleep 1 + wait=$((wait + 1)) + done + + if ! vm_is_running; then + error "QEMU failed to start" + return 1 fi - success "VM $vm_name stopped" + success "QEMU started (PID: $(cat "$PID_FILE"))" return 0 } -# Destroy VM (force stop) -destroy_vm() { - local vm_name="$1" +# Stop VM gracefully via ACPI powerdown, fallback to kill +stop_qemu() { + local timeout="${1:-60}" - if ! vm_exists "$vm_name"; then - info "VM $vm_name does not exist" + if ! vm_is_running; then return 0 fi - step "Destroying VM: $vm_name" - if vm_is_running "$vm_name"; then - virsh --connect "$LIBVIRT_URI" destroy "$vm_name" >> "$LOGFILE" 2>&1 + step "Sending shutdown signal to VM" + + # Send ACPI powerdown via monitor socket + if [ -S "$MONITOR_SOCK" ]; then + echo "system_powerdown" | socat - "UNIX-CONNECT:$MONITOR_SOCK" >> "$LOGFILE" 2>&1 || true fi - virsh --connect "$LIBVIRT_URI" undefine "$vm_name" --nvram >> "$LOGFILE" 2>&1 - success "VM $vm_name destroyed" + # Wait for graceful shutdown + local elapsed=0 + while [ $elapsed -lt $timeout ]; do + if ! vm_is_running; then + success "VM stopped gracefully" + _cleanup_qemu_files + return 0 + fi + sleep 2 + elapsed=$((elapsed + 2)) + done + + # Force kill + warn "VM did not stop gracefully after ${timeout}s, force killing" + kill_qemu return 0 } -# Create snapshot +# Force kill VM immediately +kill_qemu() { + if [ -f "$PID_FILE" ]; then + local pid + pid=$(cat "$PID_FILE" 2>/dev/null) + if [ -n "$pid" ]; then + kill -9 "$pid" 2>/dev/null || true + fi + fi + _cleanup_qemu_files +} + +# Clean up runtime files +_cleanup_qemu_files() { + rm -f "$PID_FILE" "$MONITOR_SOCK" +} + +# ─── Snapshot Operations (qemu-img) ────────────────────────────────── +# All snapshot operations require the VM to be stopped. + create_snapshot() { - local vm_name="$1" - local snapshot_name="$2" + local disk="${1:-$DISK_PATH}" + local snapshot_name="${2:-clean-install}" + + if vm_is_running; then + error "Cannot create snapshot while VM is running" + return 1 + fi step "Creating snapshot: $snapshot_name" - if virsh --connect "$LIBVIRT_URI" snapshot-create-as "$vm_name" "$snapshot_name" >> "$LOGFILE" 2>&1; then - success "Snapshot $snapshot_name created" + if qemu-img snapshot -c "$snapshot_name" "$disk" >> "$LOGFILE" 2>&1; then + success "Snapshot '$snapshot_name' created" return 0 else - error "Failed to create snapshot $snapshot_name" + error "Failed to create snapshot '$snapshot_name'" return 1 fi } -# Restore snapshot restore_snapshot() { - local vm_name="$1" - local snapshot_name="$2" + local disk="${1:-$DISK_PATH}" + local snapshot_name="${2:-clean-install}" + + if vm_is_running; then + error "Cannot restore snapshot while VM is running" + return 1 + fi step "Restoring snapshot: $snapshot_name" - if virsh --connect "$LIBVIRT_URI" snapshot-revert "$vm_name" "$snapshot_name" >> "$LOGFILE" 2>&1; then - success "Snapshot $snapshot_name restored" + if qemu-img snapshot -a "$snapshot_name" "$disk" >> "$LOGFILE" 2>&1; then + success "Snapshot '$snapshot_name' restored" return 0 else - error "Failed to restore snapshot $snapshot_name" + error "Failed to restore snapshot '$snapshot_name'" return 1 fi } -# Delete snapshot delete_snapshot() { - local vm_name="$1" - local snapshot_name="$2" + local disk="${1:-$DISK_PATH}" + local snapshot_name="${2:-clean-install}" step "Deleting snapshot: $snapshot_name" - if virsh --connect "$LIBVIRT_URI" snapshot-delete "$vm_name" "$snapshot_name" >> "$LOGFILE" 2>&1; then - success "Snapshot $snapshot_name deleted" + if qemu-img snapshot -d "$snapshot_name" "$disk" >> "$LOGFILE" 2>&1; then + success "Snapshot '$snapshot_name' deleted" return 0 else - error "Failed to delete snapshot $snapshot_name" + error "Failed to delete snapshot '$snapshot_name'" return 1 fi } -# Clone disk image (copy-on-write) -clone_disk() { - local base_image="$1" - local new_image="$2" - - if [ ! -f "$base_image" ]; then - error "Base image not found: $base_image" - return 1 - fi - - step "Cloning disk image (full copy)" - if qemu-img convert -f qcow2 -O qcow2 "$base_image" "$new_image" >> "$LOGFILE" 2>&1; then - success "Disk cloned: $new_image" - else - error "Failed to clone disk" - return 1 - fi - - # Truncate machine-id so systemd generates a new one on boot (avoids DHCP conflicts) - step "Clearing machine-id for unique network identity" - if guestfish -a "$new_image" -i truncate /etc/machine-id >> "$LOGFILE" 2>&1; then - success "Machine-ID cleared (will regenerate on boot)" - return 0 - else - warn "Failed to clear machine-ID (guestfish failed)" - info "Network may conflict with base VM if both run simultaneously" - return 0 # Don't fail the whole operation - fi +list_snapshots() { + local disk="${1:-$DISK_PATH}" + qemu-img snapshot -l "$disk" 2>/dev/null } -# Get VM IP address (requires guest agent or DHCP lease) -get_vm_ip() { - local vm_name="$1" +snapshot_exists() { + local disk="${1:-$DISK_PATH}" + local snapshot_name="${2:-clean-install}" + qemu-img snapshot -l "$disk" 2>/dev/null | grep -q "$snapshot_name" +} - # Try guest agent first - local ip - ip=$(virsh --connect "$LIBVIRT_URI" domifaddr "$vm_name" 2>/dev/null | grep -oP '(\d+\.){3}\d+' | head -1) +# ─── SSH Operations ─────────────────────────────────────────────────── - if [ -n "$ip" ]; then - echo "$ip" - return 0 - fi - - # Fall back to DHCP leases - local mac - mac=$(virsh --connect "$LIBVIRT_URI" domiflist "$vm_name" | grep -oP '([0-9a-f]{2}:){5}[0-9a-f]{2}' | head -1) +# Wait for SSH to become available on localhost:$SSH_PORT +wait_for_ssh() { + local password="${1:-$ROOT_PASSWORD}" + local timeout="${2:-120}" + local elapsed=0 - if [ -n "$mac" ]; then - ip=$(grep "$mac" /var/lib/libvirt/dnsmasq/default.leases 2>/dev/null | awk '{print $3}') - if [ -n "$ip" ]; then - echo "$ip" + progress "Waiting for SSH on localhost:$SSH_PORT..." + while [ $elapsed -lt $timeout ]; do + if sshpass -p "$password" ssh $SSH_OPTS -p "$SSH_PORT" root@localhost true 2>/dev/null; then + success "SSH is available" return 0 fi - fi + sleep 5 + elapsed=$((elapsed + 5)) + done + error "SSH timeout after ${timeout}s" return 1 } # Execute command in VM via SSH vm_exec() { - local vm_name="$1" + local password="${1:-$ROOT_PASSWORD}" shift - local cmd="$*" - - local ip - ip=$(get_vm_ip "$vm_name") - - if [ -z "$ip" ]; then - error "Could not get IP address for VM $vm_name" - return 1 - fi - - ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$ip" "$cmd" 2>> "$LOGFILE" + sshpass -p "$password" ssh $SSH_OPTS \ + -o ServerAliveInterval=30 -o ServerAliveCountMax=10 \ + -p "$SSH_PORT" root@localhost "$@" 2>> "$LOGFILE" } # Copy file to VM copy_to_vm() { - local vm_name="$1" - local local_file="$2" - local remote_path="$3" - - local ip - ip=$(get_vm_ip "$vm_name") - - if [ -z "$ip" ]; then - error "Could not get IP address for VM $vm_name" - return 1 - fi + local local_file="$1" + local remote_path="$2" + local password="${3:-$ROOT_PASSWORD}" - step "Copying $local_file to VM" - if scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "$local_file" "root@$ip:$remote_path" >> "$LOGFILE" 2>&1; then + step "Copying $(basename "$local_file") to VM:$remote_path" + if sshpass -p "$password" scp $SSH_OPTS -P "$SSH_PORT" \ + "$local_file" "root@localhost:$remote_path" >> "$LOGFILE" 2>&1; then success "File copied to VM" return 0 else @@ -297,21 +372,13 @@ copy_to_vm() { # Copy file from VM copy_from_vm() { - local vm_name="$1" - local remote_file="$2" - local local_path="$3" - - local ip - ip=$(get_vm_ip "$vm_name") - - if [ -z "$ip" ]; then - error "Could not get IP address for VM $vm_name" - return 1 - fi + local remote_file="$1" + local local_path="$2" + local password="${3:-$ROOT_PASSWORD}" step "Copying $remote_file from VM" - if scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \ - "root@$ip:$remote_file" "$local_path" >> "$LOGFILE" 2>&1; then + if sshpass -p "$password" scp $SSH_OPTS -P "$SSH_PORT" \ + "root@localhost:$remote_file" "$local_path" >> "$LOGFILE" 2>&1; then success "File copied from VM" return 0 else |
