Browse Source

qubes/tools: rewrite qvm-shutdown

fixes QubesOS/qubes-issues#1228
Wojtek Porczyk 8 years ago
parent
commit
e720e1634b
5 changed files with 123 additions and 142 deletions
  1. 39 17
      doc/manpages/qvm-shutdown.rst
  2. 1 1
      qubes/tools/__init__.py
  3. 82 0
      qubes/tools/qvm_shutdown.py
  4. 0 124
      qvm-tools/qvm-shutdown
  5. 1 0
      rpm_spec/core-dom0.spec

+ 39 - 17
doc/manpages/qvm-shutdown.rst

@@ -1,42 +1,64 @@
 .. program:: qvm-shutdown
 
-====================================================
-:program:`qvm-shutdown` -- Gracefully shut down a VM
-====================================================
+:program:`qvm-shutdown` -- Gracefully shut down a qube
+======================================================
+
+.. warning::
+
+   This page was autogenerated from command-line parser. It shouldn't be 1:1
+   conversion, because it would add little value. Please revise it and add
+   more descriptive help, which normally won't fit in standard ``--help``
+   option.
+
+   After rewrite, please remove this admonition.
 
 Synopsis
-========
-:command:`qvm-shutdown` [*options*] <*vm-name*>
+--------
+
+:command:`qvm-shutdown` [-h] [--verbose] [--quiet] [--all] [--exclude *EXCLUDE*] [--force] [--wait] [--timeout *TIMEOUT*] [*VMNAME*]
 
 Options
-=======
+-------
 
 .. option:: --help, -h
 
-    Show this help message and exit
+   show the help message and exit
+
+.. option:: --verbose, -v
+
+   increase verbosity
 
 .. option:: --quiet, -q
 
-    Be quiet
+   decrease verbosity
 
-.. option:: --force
+.. option:: --all
 
-    Force operation, even if may damage other VMs (eg. shutdown of NetVM)
+   perform the action on all qubes
 
-.. option:: --wait
+.. option:: --exclude=EXCLUDE
 
-    Wait for the VM(s) to shutdown
+   exclude the qube from :option:`--all`
 
-.. option:: --all
+.. option:: --force
+
+   force operation, even if may damage other VMs (eg. shutdown of network
+   provider)
 
-    Shutdown all running VMs
+.. option:: --wait
+
+   wait for the VMs to shut down
 
-.. option:: --exclude=EXCLUDE_LIST
+.. option:: --timeout
 
-    When :option:`--all` is used: exclude this VM name (might be repeated)
+   timeout after which domains are killed when using :option:`--wait`
 
 Authors
-=======
+-------
+
 | Joanna Rutkowska <joanna at invisiblethingslab dot com>
 | Rafal Wojtczuk <rafal at invisiblethingslab dot com>
 | Marek Marczykowski <marmarek at invisiblethingslab dot com>
+| Wojtek Porczyk <woju at invisiblethingslab dot com>
+
+.. vim: ts=3 sw=3 et tw=80

+ 1 - 1
qubes/tools/__init__.py

@@ -189,7 +189,7 @@ class QubesArgumentParser(argparse.ArgumentParser):
                 vmchoice.add_argument('--all',
                     action='store_const', const=VM_ALL, dest='vm',
                     help='perform the action on all qubes')
-                vmchoice.add_argument('--exclude',
+                self.add_argument('--exclude',
                     action='append', default=[],
                     help='exclude the qube from --all')
                 nargs = '?'

+ 82 - 0
qubes/tools/qvm_shutdown.py

@@ -0,0 +1,82 @@
+#!/usr/bin/python2
+# vim: fileencoding=utf8
+#
+# The Qubes OS Project, http://www.qubes-os.org
+#
+# Copyright (C) 2010-2016  Joanna Rutkowska <joanna@invisiblethingslab.com>
+# Copyright (C) 2011-2016  Marek Marczykowski-Górecki
+#                                              <marmarek@invisiblethingslab.com>
+# Copyright (C) 2016       Wojtek Porczyk <woju@invisiblethingslab.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+from __future__ import print_function
+
+import qubes.config
+import qubes.tools
+
+import sys
+import time
+
+
+parser = qubes.tools.QubesArgumentParser(
+    description='gracefully shut down a qube',
+    want_vm=True,
+    want_vm_all=True)
+
+parser.add_argument('--force',
+    action='store_true', default=False,
+    help='force operation, even if may damage other VMs (eg. shutdown of'
+        ' network provider)')
+
+parser.add_argument('--wait',
+    action='store_true', default=False,
+    help='wait for the VMs to shut down')
+
+parser.add_argument('--timeout',
+    action='store', type=float,
+    default=qubes.config.defaults['shutdown_counter_max'],
+    help='timeout after which domains are killed when using --wait'
+        ' (default: %d)')
+
+
+def main(args=None):
+    args = parser.parse_args(args)
+
+    for vm in args.vm:
+        vm.shutdown(force=args.force)
+
+    if not args.wait:
+        return
+
+    timeout = args.timeout
+    current_vms = list(sorted(args.vm))
+    while timeout >= 0:
+        current_vms = [vm for vm in current_vms
+            if vm.get_power_state() != 'Halted']
+        args.app.log.info('Waiting for shutdown ({}): {}'.format(
+            timeout, ', '.join(map(str, current_vms))))
+        time.sleep(1)
+        timeout -= 1
+
+    args.app.log.notice(
+        'Killing remaining qubes: {}'.format(', '.join(map(str, current_vms))))
+    for vm in current_vms:
+        vm.force_shutdown()
+
+
+if __name__ == '__main__':
+    sys.exit(main())

+ 0 - 124
qvm-tools/qvm-shutdown

@@ -1,124 +0,0 @@
-#!/usr/bin/python2
-# -*- encoding: utf8 -*-
-#
-# The Qubes OS Project, http://www.qubes-os.org
-#
-# Copyright (C) 2011  Marek Marczykowski <marmarek@invisiblethingslab.com>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-#
-#
-
-from qubes.qubes import QubesVmCollection,QubesException
-from qubes.qubes import defaults
-from optparse import OptionParser;
-import sys
-import time
-
-def main():
-    usage = "usage: %prog [options] <vm-name>"
-    parser = OptionParser (usage)
-    
-    parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True)
-    parser.add_option ("--force", action="store_true", dest="force", default=False,
-                      help="Force operation, even if may damage other VMs (eg shutdown of NetVM)")
-    parser.add_option ("--wait", action="store_true", dest="wait_for_shutdown", default=False,
-                      help="Wait for the VM(s) to shutdown")
-    parser.add_option("--wait-time", action="store", dest="wait_time",
-                      default=defaults["shutdown_counter_max"],
-                      help="Timout after which VM will be killed when --wait "
-                           "is used")
-    parser.add_option ("--all", action="store_true", dest="shutdown_all", default=False,
-                      help="Shutdown all running VMs")
-    parser.add_option ("--exclude", action="append", dest="exclude_list",
-                       help="When --all is used: exclude this VM name (may be "
-                            "repeated)")
-
-    (options, args) = parser.parse_args ()
-    if not options.shutdown_all and (len (args) != 1):
-        parser.error ("You must specify VM name!")
-
-    qvm_collection = QubesVmCollection()
-    qvm_collection.lock_db_for_reading()
-    qvm_collection.load()
-    qvm_collection.unlock_db()
-
-    vms_list = []
-    if options.shutdown_all:
-        all_vms = [vm for vm in qvm_collection.values()]
-        for vm in all_vms:
-            if options.exclude_list is not None and vm.name in options.exclude_list:
-                continue
-            if vm.qid == 0:
-                continue
-            if vm.is_running():
-                vms_list.append (vm)
-    else:
-        vmname = args[0]
-        vm = qvm_collection.get_vm_by_name(vmname)
-        if vm is None:
-            print >> sys.stderr, "A VM with the name '{0}' does not exist in the system!".format(vmname)
-            exit(1)
-        vms_list.append(vm)
-
-    # Check VMs network dependencies
-    if not options.force:
-        for vm in vms_list:
-            if vm.is_netvm():
-                for connected_vm in vm.connected_vms.values():
-                    if connected_vm.is_running() and not connected_vm in vms_list:
-                        print >> sys.stderr, "ERROR: There are other VMs connected to VM '%s'" % vm.name
-                        exit(1)
-
-    for vm in vms_list:
-        try:
-            if options.verbose:
-                print >> sys.stderr, "Shutting down VM: '{0}'...".format(vm.name)
-            # Dependencies already checked above
-            vm.shutdown(force=True)
-        except (IOError, OSError, QubesException) as err:
-            print >> sys.stderr, "ERROR: {0}".format(err)
-            exit (1)
-
-    if options.wait_for_shutdown:
-        if options.verbose:
-            print >> sys.stderr, "Waiting for the VM(s) to shutdown..."
-        shutdown_counter = 0
-
-        halting_vms = []
-        while len (vms_list):
-            if options.verbose:
-                print >> sys.stderr, "Waiting for VMs: ", [vm.name for vm in vms_list]
-            for vm in vms_list:
-                if not vm.is_running():
-                    vms_list.remove (vm)
-                    continue
-                if vm.get_power_state() == "Halting":
-                    if vm in halting_vms:
-                        vm.force_shutdown()
-                        continue
-                    else:
-                        halting_vms.append(vm)
-                if shutdown_counter > int(options.wait_time):
-                    # kill the VM
-                    if options.verbose:
-                        print >> sys.stderr, "Killing the (apparently hanging) VM '{0}'...".format(vm.name)
-                    vm.force_shutdown()
-                    #vms_list.remove(vm)
-
-            shutdown_counter += 1
-            time.sleep (1)
-
-main()

+ 1 - 0
rpm_spec/core-dom0.spec

@@ -247,6 +247,7 @@ fi
 %{python_sitelib}/qubes/tools/qvm_pause.py*
 %{python_sitelib}/qubes/tools/qvm_prefs.py*
 %{python_sitelib}/qubes/tools/qvm_run.py*
+%{python_sitelib}/qubes/tools/qvm_shutdown.py*
 %{python_sitelib}/qubes/tools/qvm_start.py*
 %{python_sitelib}/qubes/tools/qvm_unpause.py*