4

Bringing this question straight from here.

I am running archlinux and I have a VM running often on it along with the system. Most of the time actually.

My goal is to produce the following behaviour:

  • A shutdown / poweroff / reboot / halt signal is sent to the system
  • No action other then trying to shut down the virtual machines gracefully
  • If the VMs are shut down gracefully after X seconds, proceeds with shutting down the host system too.
  • If not, execute a different command

Just give me a good idea on what to work on, because I don't even know from where to begin. I guess there is a call to the kernel that can be looked at.

Let me know.


My current code

At the moment I am using these scripts to gracefully shutdown my kvm virtual machines, and it works! But only as long as my user launches a shutdown or a reboot using his shell. Any other case wouldn't work.

These alias:

alias sudocheck="/bin/bash /home/damiano/.script/sudocheck"
alias sudo="sudocheck "

Are triggering this function:

#!/bin/bash

# This script checks for what is being passed to sudo.
# If the command passed is poweroff or reboot, it
# launches a custom script instead, that also looks
# fur currently running virtual machines and shuts them.

sudocheck() {
    if [ $1 == "poweroff" ] || [ $1 == "reboot" ]; then
        eval "sudo /home/damiano/.script/graceful $@"
    else
        eval "sudo $@"
    fi
}
sudocheck $@

That launches this script if needed:

#!/bin/bash
i=0
e=0
## if virsh finds VMs running
virsh -c qemu:///system list | awk '{ print $3}' | \
if grep running > /dev/null ; then
    virsh -c qemu:///system list --all | grep running | awk '{print "-c qemu:///system shutdown "$2}' | \
## shuts them dow gracefully
    xargs -L1 virsh
## wait 30 seconds for them to go down
    until (( i >= 30 || e == 1 )) ; do
## check every second for their status
        virsh -c qemu:///system list --all | awk '{ print $3}' | \
        if grep -E '(running|shutdown)' > /dev/null ; then
## keep waiting if still running
            if (( i <= 30 )) ; then
                sleep 1 && let i++ && echo $i
            else
                e=1 && notify-send 'Shutdown has been canceled' 'Please check the status of your virtual machines: seems like even though a stop signal has been sent, some are still running.' --urgency=critical
            fi
        else
## if no machine is running anymore, original power command can be executed
            e=1 && eval $@
        fi
    done
fi

Systemd Unit

I also made the following draft, to manage the execution of my VM:

[email protected]

[Unit]
Description=This service manages the execution of the %i virtual machine
Documentation=https://libvirt.org/manpages/virsh.html

[Service]
ExecStartPre=virsh -c qemu:///system
ExecStart=virsh start %i
ExecStop=virsh -c qemu:///system
ExecStop=virsh shutdown %i 
TimeoutStopSec=30
KillMode=none

[Install]
WantedBy=multi-user.target

But how can I tell the system to don't shut down the desktop environment, to stay as it is UNTIL the VM has been successfully shut down? Because if the system can't shut down the vm, I want to do it while still in my DE. I don't want the computer to begin stopping all the services and remain hung until it just forces the shut down.

3
  • Does running virsh shutdown domainname from the host do what you need? Mar 14, 2020 at 2:29
  • No, it just shuts down my virtual machine. I want to script a graceful shutdown for the entire system. So that all the vm's get shut down along with the host. Mar 14, 2020 at 4:10
  • May be using the full path of virsh is more secure. Oct 22, 2020 at 9:47

2 Answers 2

5

Strange way for controlling VM. Good way -- use systemd for automatic start and stop VM. For example:

/etc/systemd/system/[email protected]

[Unit]
Description=QEMU virtual machine

[Service]
Environment="type=system-x86_64" "haltcmd=kill -INT $MAINPID"
EnvironmentFile=/etc/conf.d/qemu.d/%i
ExecStart=/usr/bin/qemu-${type} -name %i -nographic $args
ExecStop=/bin/sh -c ${haltcmd}
TimeoutStopSec=30
KillMode=none

[Install]
WantedBy=multi-user.target

... And, of course config(s) for run VM's:

/etc/conf.d/qemu.d/one

type="system-x86_64"

args="-enable-kvm -m 512 -hda /dev/vg0/vm1 -net nic,macaddr=DE:AD:BE:EF:E0:00 \
 -net tap,ifname=tap0 -serial telnet:localhost:7000,server,nowait,nodelay \
 -monitor telnet:localhost:7100,server,nowait,nodelay -vnc :0"

haltcmd="echo 'system_powerdown' | nc localhost 7100" # or netcat/ncat

# You can use other ways to shut down your VM correctly
#haltcmd="ssh powermanager@vm1 sudo poweroff"

...and another VM config:

/etc/conf.d/qemu.d/two

args="-enable-kvm -m 512 -hda /srv/kvm/vm2.img -net nic,macaddr=DE:AD:BE:EF:E0:01 \
 -net tap,ifname=tap1 -serial telnet:localhost:7001,server,nowait,nodelay \
 -monitor telnet:localhost:7101,server,nowait,nodelay -vnc :1"

haltcmd="echo 'system_powerdown' | nc localhost 7101"

Et cetera. Thanks ArchWiki contributors for this article.

3
  • This is useful only to get the machine shut down, if they can be shut down in - say - 30 seconds. But the systemd unit you posted will try to shut down the machine while the rest of the system is being shut down, and, after 30 seconds it will shut it down forcefully which is not what I want. I want that when a shutdown signal gets launched everything stays as it is, the VMs are shut down and: if successful then the system proceeds to shut down the rest of the system as usual, else -> another command. Mar 15, 2020 at 3:25
  • The virtual machine ? do you use something cfengine, puppet or chef to manage it ? Could you have something in policy which if something is true for the host propagates a shutdown action to the virtual machines ? In the case of cfengine : the hub can initiate a policy check with special variables set -> special variable set ? create a marker file and at end of policy shutdown the VM. Mar 15, 2020 at 13:44
  • Hello Stefan, thanks for your comment. No I just use virt-manager and virsh Mar 16, 2020 at 12:28
2

The draft by the original post and @Oxyd's answer are useful when you need to control each Virtual Machines individually.

However if it's desired to shutdown/start all the VMs one could use the libvirt guests as mentioned on Libvirt docs.

  • Edit the file /etc/conf.d/libvirt-guests, or /etc/sysconfig/libvirt-guests for CentOS, then set ON_SHUTDOWN=shutdown and SHUTDOWN_TIMEOUT=0
  • I think one also needs to activate the libvirt-guests service, for example systemctl enable libvirt-guests and systemctl start libvirt-guests

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .