From c8ef500588452d39b4b41e9f38066c22c6b832ad Mon Sep 17 00:00:00 2001 From: Joanna Rutkowska Date: Thu, 8 Jul 2010 12:41:29 +0200 Subject: [PATCH] Pause/Unpause all running VMs on system suspend/resume This is to fix the VM lockup problem on HT processors that occured after S3 resume (see ticket #52). The qvm-run command now takes additional two switches: --pause --unpause --- dom0/pm-utils/02qubes-pause-vms | 23 ++++++++++++++ dom0/qvm-core/qubes.py | 6 ++++ dom0/qvm-tools/qvm-run | 56 +++++++++++++++++++++++---------- rpm_spec/core-dom0.spec | 2 ++ 4 files changed, 71 insertions(+), 16 deletions(-) create mode 100755 dom0/pm-utils/02qubes-pause-vms diff --git a/dom0/pm-utils/02qubes-pause-vms b/dom0/pm-utils/02qubes-pause-vms new file mode 100755 index 00000000..5da1be84 --- /dev/null +++ b/dom0/pm-utils/02qubes-pause-vms @@ -0,0 +1,23 @@ +#!/bin/sh + +. "${PM_FUNCTIONS}" + +pause_vms() +{ +echo +qvm-run --all --pause +} + + +unpause_vms() +{ +echo +qvm-run --all --unpause +} + + +case "$1" in + thaw|resume) unpause_vms ;; + suspend|hibernate) pause_vms ;; + *) exit 0 ;; +esac diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 841f758d..110062f2 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -370,6 +370,12 @@ class QubesVm(object): else: return False + def is_paused(self): + if self.get_power_state() == "Paused": + return True + else: + return False + def get_disk_usage(self, file_or_dir): if not os.path.exists(file_or_dir): return 0 diff --git a/dom0/qvm-tools/qvm-run b/dom0/qvm-tools/qvm-run index ecd615a0..6125ad5d 100755 --- a/dom0/qvm-tools/qvm-run +++ b/dom0/qvm-tools/qvm-run @@ -53,6 +53,18 @@ def vm_run_cmd(vm, cmd, options): subprocess.call (["/usr/sbin/xm", "shutdown", vm.name]) return + if options.pause: + if options.verbose: + print "Pausing VM: '{0}'...".format(vm.name) + subprocess.call (["/usr/sbin/xm", "pause", vm.name]) + return + + if options.unpause: + if options.verbose: + print "UnPausing VM: '{0}'...".format(vm.name) + subprocess.call (["/usr/sbin/xm", "unpause", vm.name]) + return + if options.verbose: print "Running command on VM: '{0}'...".format(vm.name) @@ -124,7 +136,7 @@ def main(): help="Use tray notifications instead of stdout" ) parser.add_option ("--all", action="store_true", dest="run_on_all_running", default=False, - help="Run command on all currently running VMs") + help="Run command on all currently running VMs (or all paused, in case of --unpause)") parser.add_option ("--exclude", action="append", dest="exclude_list", help="When --all is used: exclude this VM name (might be repeated)") @@ -135,24 +147,36 @@ def main(): parser.add_option ("--shutdown", action="store_true", dest="shutdown", default=False, help="Do 'xm shutdown' for the VM(s) (can be combined this with --all and --wait)") + parser.add_option ("--pause", action="store_true", dest="pause", default=False, + help="Do 'xm pause' for the VM(s) (can be combined this with --all and --wait)") + + parser.add_option ("--unpause", action="store_true", dest="unpause", default=False, + help="Do 'xm unpause' for the VM(s) (can be combined this with --all and --wait)") + + (options, args) = parser.parse_args () - if options.run_on_all_running: - if len(args) < 1 and not options.shutdown: - parser.error ("You must provide a command to execute on all the VMs.") - if len(args) > 1 or (options.shutdown and len(args) > 0): - parser.error ("To many arguments...") - cmdstr = args[0] if not options.shutdown else None + if (options.shutdown or options.pause or options.unpause): + takes_cmd_argument = False else: - if len (args) < 1 and options.shutdown: - parser.error ("You must specify the VM name to shutdown.") - if len (args) < 2 and not options.shutdown: + takes_cmd_argument = True + + if options.run_on_all_running: + if len(args) < 1 and takes_cmd_argument: + parser.error ("You must provide a command to execute on all the VMs.") + if len(args) > 1 or ((not takes_cmd_argument) and len(args) > 0): + parser.error ("To many arguments...") + cmdstr = args[0] if takes_cmd_argument else None + else: + if len (args) < 1 and not takes_cmd_argument: + parser.error ("You must specify the VM name to shutdown/pause/unpause.") + if len (args) < 2 and takes_cmd_argument: parser.error ("You must specify the VM name and the command to execute in the VM.") - if len (args) > 2 or (options.shutdown and len(args) > 1): + if len (args) > 2 or ((not takes_cmd_argument) and len(args) > 1): parser.error ("To many arguments...") vmname = args[0] - cmdstr = args[1] if not options.shutdown else None + cmdstr = args[1] if takes_cmd_argument else None if options.tray: global notify_object @@ -171,7 +195,7 @@ def main(): continue if vm.qid == 0: continue - if vm.is_running(): + if (options.unpause and vm.is_paused()) or (not options.unpause and vm.is_running()): vms_list.append (vm) else: vm = qvm_collection.get_vm_by_name(vmname) @@ -180,10 +204,10 @@ def main(): exit(1) vms_list.append(vm) - if options.shutdown: - cmd = None - else: + if takes_cmd_argument: cmd = "{user}:{cmd}".format(user=options.user, cmd=cmdstr) + else: + cmd = None for vm in vms_list: vm_run_cmd(vm, cmd, options) diff --git a/rpm_spec/core-dom0.spec b/rpm_spec/core-dom0.spec index a7b71808..753f93cf 100644 --- a/rpm_spec/core-dom0.spec +++ b/rpm_spec/core-dom0.spec @@ -97,6 +97,7 @@ cp init.d/iptables $RPM_BUILD_ROOT/etc/sysconfig mkdir -p $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d cp pm-utils/01qubes-sync-vms-clock $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ +cp pm-utils/02qubes-pause-vms $RPM_BUILD_ROOT/usr/lib64/pm-utils/sleep.d/ %post @@ -200,3 +201,4 @@ fi /etc/NetworkManager/dispatcher.d/qubes_nmhook /etc/sysconfig/iptables /usr/lib64/pm-utils/sleep.d/01qubes-sync-vms-clock +/usr/lib64/pm-utils/sleep.d/02qubes-pause-vms