Parallels Automation


As part of my recent work for automation VyOS, I've been working with Parallels in order to run versions fo the router locally for testing. My initial attempts were entirely manual:

  1. Look up the lastest rolling release on GitHub
  2. Download the ISO image from the VyOS website
  3. Create a new VM in Parallels
  4. Configure the VM to ahve appropriate network interfaces (4, all bridged)
  5. Boot the VM
  6. Attach the ISO image to the VM
  7. Reboot (to get the live VyOS to run)
  8. Use install image to install the image to the VM
  9. Reboot
  10. Disconnect the ISO image
  11. Configure an IP address on the primary interface
  12. ssh to the system and add the test configuration
  13. run tests

All told, not a bad process, and highly repeatable. But, once something is repeatable, I want to automate it. So, I started looking at the Parallels CLI so that I could start with some bash scripts.

I know there is a mechanism for stuffing keystrokes, but I'm not confident in that approach, so I'll be doing steps 8-13 manually for now.

The code presented here takes an version argument to determine which build to use. If the version is 1.5, it will look up the latest rolling release on GitHub and use that. If the version is not specified, it will look in the specified directory for the latest image. If the version is specified, it will look for the latest image with that version prefix.

#!/bin/bash
set -x

# Function to retrieve the latest VyOS version from GitHub
get_latest_vyos_version() {
  curl -L -sS \
    -H "Accept: application/vnd.github+json" \
    -H "X-GitHub-Api-Version: 2022-11-28" \
    https://api.github.com/repos/vyos/vyos-nightly-build/releases | jq -r ".[0].name"
}

# Function to find the latest VyOS image in a directory
find_latest_vyos_image() {
  local version_prefix="$1"
  local directory="$2"
  find "$directory" -type f -name "vyos-${version_prefix}*-*amd64.iso" -print0 | xargs -0 ls -t | head -n 1
}

# Set defaults
VYOS_VERSION=""
DOWNLOAD_DIR="${HOME}/VyOS"
ROLLING_VERSION="1.5"

# Parse arguments
while [[ $# -gt 0 ]]; do
  case "$1" in
    -v|--version)
      VYOS_VERSION="$2"
      shift 2
      ;;
    -d|--directory)
      DOWNLOAD_DIR="$2"
      shift 2
      ;;
    *)
      echo "Unknown option: $1" >&2
      exit 1
      ;;
  esac
done

# Determine VYOS_VERSION and VYOS_IMAGE_FILE
if [[ "$VYOS_VERSION" == "${ROLLING_VERSION}" ]]; then
  VYOS_VERSION=$(get_latest_vyos_version)
  export VYOS_IMAGE_FILE="${DOWNLOAD_DIR}/vyos-${VYOS_VERSION}-generic-amd64.iso"
elif [[ -z "$VYOS_VERSION" ]]; then
  VYOS_IMAGE_FILE=$(find_latest_vyos_image "*" "$DOWNLOAD_DIR")
  VYOS_VERSION=$(basename "$VYOS_IMAGE_FILE" .iso | awk -F'-' '{print $2}')
else
  VYOS_IMAGE_FILE=$(find_latest_vyos_image "$VYOS_VERSION" "$DOWNLOAD_DIR")
fi

#export VYOS_VERSION=1.5-rolling-202411070006
export VYOS_VM="Vyos-${VYOS_VERSION}"
export VYOS_IMAGE_URL=https://github.com/vyos/vyos-nightly-build/releases/download/${VYOS_VERSION}/vyos-${VYOS_VERSION}-generic-amd64.iso
#export VYOS_IMAGE_FILE=~/Downloads/vyos-${VYOS_VERSION}-*amd64.iso
export VYOS_SIG_URL=${VYOS_IMAGE_URL}.minisig
export VYOS_SIG_FILE=${VYOS_IMAGE_FILE}.minisig
export VYOS_PUBKEY="RWSIhkR/dkM2DSaBRniv/bbbAf8hmDqdbOEmgXkf1RxRoxzodgKcDyGq"


RUNNING=$(prlctl list $VYOS_VM)
if [ $? -eq 0 ]; then
  echo "$VYOS_VM already running, abort it?"
  prlctl stop $VYOS_VM --kill
  prlctl delete $VYOS_VM
fi

if [ -f $VYOS_SIG_FILE ]; then
  echo "checking existing file ${VYOS_IMAGE_FILE}"
  minisign -Vm ${VYOS_IMAGE_FILE} -P ${VYOS_PUBKEY}
  if [ $? -ne 0 ]; then
    echo "Signature failed for $VYOS_VERSION"
    exit 1
  fi
fi

if [ ! -f $VYOS_SIG_FILE ]; then
  if [[ $VYOS_VERSION == $ROLLING_VERSION* ]] ; then
    # retrieving file
    curl -L -o $VYOS_IMAGE_FILE $VYOS_IMAGE_URL
    curl -L -o $VYOS_SIG_FILE $VYOS_SIG_URL
    minisign -Vm ${VYOS_IMAGE_FILE} -P ${VYOS_PUBKEY}
    if [ $? -ne 0 ]; then
      echo "Signature failed for $VYOS_VERSION"
      exit 1
    fi
  else
    echo "Warning: Skipping signature check due to no signature file"
  fi
fi

prlctl create $VYOS_VM -d debian
prlctl set $VYOS_VM --cpus 2 --memsize 512
prlctl set $VYOS_VM --device-set net0 --type bridged --iface en0 --mac auto
prlctl set $VYOS_VM --device-add net --type bridged --iface en0 --mac auto
prlctl set $VYOS_VM --device-add net --type bridged --iface en0 --mac auto
prlctl set $VYOS_VM --device-add net --type bridged --iface en0 --mac auto
prlctl set $VYOS_VM --device-set cdrom0 --image $VYOS_IMAGE_FILE
prlctl start $VYOS_VM
wait 5
prlctl set $VYOS_VM --device-connect cdrom0
prlctl reset $VYOS_VM