Script to Prevent 2 VMs Running Simultaneously?

Hello :slight_smile:

I would like to know how I can configure a native way to forbid that 2 VMs run together ?

Thank’s :smiley:

1 Like

Last time I tried this, I was sucessfully able to prevent a qube to start with a libxl hook BUT I wasn’t able to allow it to start :upside_down_face: … so it’s just an idea:

1 Like

If that helps for doing researches, this is called “anti affinity”, when you do not want 2 VM to run on the same host. In Qubes OS case, you just have a host group of 1 host.

2 Likes

Something like this in dom0 in executable /etc/libvirt/hooks/libxl might do:

#!/bin/bash
set -e

qube_name="$1"
libvirt_operation="$2"
case $qube_name in
  qube1)
    blocker="qube2"
    ;;
  qube2)
    blocker="qube1"
    ;;
  *)
    exit
    ;;
esac

if [[ "$libvirt_operation" = "prepare" ]] ; then
(
exec 0</dev/null
exec 1>/dev/null
exec 2>/dev/null

if qvm-check --running -q $blocker; then
  qvm-kill $qube_name
fi
) & disown
fi
I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.
3 Likes

Well, I was trying to play again with the libxl hooks before @unman’s solution… So anyway, here’s my dirty script:

#!/usr/bin/env python3
import sys
from pathlib import Path

guest_name, operation = sys.argv[1:3]

OPERATIONS = ['prepare', 'release']
STATUS_ROOT = Path('/tmp')
STATUS_NAME = ".anti-affinity-status-for-{}-vm"

guest_names = [f"antiaffinity{x}" for x in range(3)]

# Hook checks
try:
    guest_names.pop(guest_names.index(guest_name))
except ValueError:
    sys.exit()

if operation not in OPERATIONS:
    sys.exit()

# Status checks 
status_path = STATUS_ROOT / STATUS_NAME.format(guest_name)

if operation == OPERATIONS[0]:
    for other_name in guest_names:
        other_path = STATUS_ROOT / STATUS_NAME.format(other_name)
        if other_path.exists():
            sys.exit(1)
    
    status_path.touch() 

elif operation == OPERATIONS[1]:
    status_path.unlink(missing_ok=True)

It works with 2 or more guests and uses a file in /tmp to check the status of any other guest listed in guest_names… But using Qubes’ own tools maybe better (but longer?).

Instead of putting any script in a file called /etc/libvirt/hooks/libxl, I recommend to put it into a file with any name, inside a directory called /etc/libvirt/hooks/libxl.d. You have to create the directory, the file and check that the file is executable. Then, you can test it on command line:

 /etc/libvirt/hooks/libxl.d/my-script.ext "$GUEST_NAME" prepare - -; echo "Exit status: $?"

Of course, replace $GUEST_NAME by any guest name you want and you can use an other operation name instead of prepare.

If the exit status is different from 0, the VM won’t start. If everything is alright, restart the libvirt daemon, as suggested here:

I was trying to achieve what @unman is doing but without the disown part, thus the system was hanging indefinitely. Maybe because calling Qubes tools is like doing the forbidden calls as said in the libvirt’s docs?

Calling libvirt functions from within a hook script

A hook script must not call back into libvirt, as the libvirt daemon is already waiting for the script to exit.

A deadlock is likely to occur.

3 Likes

Hi :slightly_smiling_face:

This solution works fine after 1 modification :

Replace if [[ "$libvirt_operation" = "prepare" ]] ; then
By if [[ "${libvirt_operation}" = "prepare" ]] ; then

This modification has directly worked to enter in the condition and kill next the vm.

Still thank’s for the solution :pray:

1 Like

I don’t understand why this change fix anything. But I’m glad you got something working.

[user@dev shm]$ cat test.sh 
#!/bin/bash

echo $SHELL

libvirt_operation="prepare"

if [[ "$libvirt_operation" = "prepare" ]] ; then
    echo "it worked"
fi

if [[ "${libvirt_operation}" = "prepare" ]] ; then
    echo "it worked"
fi

libvirt_operation="something else"
if [[ "$libvirt_operation" = "prepare" ]] ; then
    echo "it worked"
else
    echo "it's false"
fi

if [[ "${libvirt_operation}" = "prepare" ]] ; then
    echo "it worked"
else
    echo "it's false"
fi

[user@dev shm]$ bash test.sh 
/bin/bash
it worked
it worked
it's false
it's false
2 Likes