From 63a1edc274753138958f7e98578816be17de1dd3 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 29 Jun 2016 12:26:18 +0200 Subject: [PATCH 1/7] Fix qvm-remove undefines domain in libvirt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Return error if domain is not halted - Undefine the domain in libvirt, to avoid: "Domain already exists with uuid…" --- qubes/tools/qvm_remove.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qubes/tools/qvm_remove.py b/qubes/tools/qvm_remove.py index b20bcbaf..e2a813a1 100644 --- a/qubes/tools/qvm_remove.py +++ b/qubes/tools/qvm_remove.py @@ -40,8 +40,12 @@ parser.add_argument('--just-db', def main(args=None): # pylint: disable=missing-docstring args = parser.parse_args(args) for vm in args.domains: + if not vm.is_halted(): + parser.print_error('VM {!s} not halted'.format(vm)) + return 1 del args.app.domains[vm.qid] args.app.save() + vm.libvirt_domain.undefine() if not args.just_db: vm.remove_from_disk() From de5487bf149393638bf0024e3e47194c89bc86ea Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 18:24:29 +0200 Subject: [PATCH 2/7] Dumb down qvm-remove - Move `vm.is_halted()` check in to VMCollection.__delitem__() - `vm.remove_from_disk()` will raise exception if is called on a running vm --- qubes/app.py | 7 +++++-- qubes/tools/qvm_remove.py | 4 ---- qubes/vm/qubesvm.py | 5 +++++ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qubes/app.py b/qubes/app.py index 805a64f4..c87c073c 100644 --- a/qubes/app.py +++ b/qubes/app.py @@ -437,14 +437,17 @@ class VMCollection(object): raise KeyError(key) - def __delitem__(self, key): vm = self[key] + if not vm.is_halted(): + msg = "Can't remove, vm {!s}, beacuse it's in state {!s}." + msg = msg.format(vm, vm.get_power_state()) + raise qubes.exc.QubesVMNotHaltedError(msg) self.app.fire_event_pre('domain-pre-delete', vm) + vm.libvirt_domain.undefine() del self._dict[vm.qid] self.app.fire_event('domain-delete', vm) - def __contains__(self, key): return any((key == vm or key == vm.qid or key == vm.name) for vm in self) diff --git a/qubes/tools/qvm_remove.py b/qubes/tools/qvm_remove.py index e2a813a1..b20bcbaf 100644 --- a/qubes/tools/qvm_remove.py +++ b/qubes/tools/qvm_remove.py @@ -40,12 +40,8 @@ parser.add_argument('--just-db', def main(args=None): # pylint: disable=missing-docstring args = parser.parse_args(args) for vm in args.domains: - if not vm.is_halted(): - parser.print_error('VM {!s} not halted'.format(vm)) - return 1 del args.app.domains[vm.qid] args.app.save() - vm.libvirt_domain.undefine() if not args.just_db: vm.remove_from_disk() diff --git a/qubes/vm/qubesvm.py b/qubes/vm/qubesvm.py index afe1a21e..0db875eb 100644 --- a/qubes/vm/qubesvm.py +++ b/qubes/vm/qubesvm.py @@ -1089,6 +1089,11 @@ class QubesVM(qubes.vm.mix.net.NetVMMixin, qubes.vm.BaseVM): def remove_from_disk(self): '''Remove domain remnants from disk.''' + if not self.is_halted(): + msg = "Can't remove, vm {!s}, beacuse it's in state {!s}." + msg = msg.format(self, self.get_power_state()) + raise qubes.exc.QubesVMNotHaltedError(msg) + self.fire_event('domain-remove-from-disk') self.storage.remove() shutil.rmtree(self.dir_path) From 6fad72255437c198c906ee374d884b3de19b039f Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 20:38:46 +0200 Subject: [PATCH 3/7] =?UTF-8?q?Make=20pylint=20really=20happy=20=E2=99=A5?= =?UTF-8?q?=E2=99=A5=E2=99=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qubes/__init__.py | 14 ++------------ qubes/app.py | 19 ++++++++++--------- qubes/ext/qubesmanager.py | 2 +- qubes/utils.py | 6 ++---- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/qubes/__init__.py b/qubes/__init__.py index fafbcb65..233f480f 100644 --- a/qubes/__init__.py +++ b/qubes/__init__.py @@ -32,22 +32,12 @@ Qubes OS from __future__ import absolute_import +import __builtin__ import collections -import errno -import grp -import logging import os import os.path -import sys -import tempfile -import time -import __builtin__ - -import jinja2 import lxml.etree -import pkg_resources - import qubes.config import qubes.events import qubes.exc @@ -439,7 +429,7 @@ class PropertyHolder(qubes.events.Emitter): propvalues = {} all_names = set(prop.__name__ for prop in self.property_list()) - for key in list(kwargs.keys()): + for key in list(kwargs): if not key in all_names: continue propvalues[key] = kwargs.pop(key) diff --git a/qubes/app.py b/qubes/app.py index c87c073c..83c5858f 100644 --- a/qubes/app.py +++ b/qubes/app.py @@ -36,13 +36,14 @@ import tempfile import time import uuid -import jinja2 import lxml.etree + +import jinja2 import libvirt try: - import xen.lowlevel.xs - import xen.lowlevel.xc + import xen.lowlevel.xs # pylint: disable=wrong-import-order + import xen.lowlevel.xc # pylint: disable=wrong-import-order except ImportError: pass @@ -58,12 +59,12 @@ else: raise RuntimeError("Qubes works only on POSIX or WinNT systems") -import qubes -import qubes.ext -import qubes.utils -import qubes.vm.adminvm -import qubes.vm.qubesvm -import qubes.vm.templatevm +import qubes # pylint: disable=wrong-import-position +import qubes.ext # pylint: disable=wrong-import-position +import qubes.utils # pylint: disable=wrong-import-position +import qubes.vm.adminvm # pylint: disable=wrong-import-position +import qubes.vm.qubesvm # pylint: disable=wrong-import-position +import qubes.vm.templatevm # pylint: disable=wrong-import-position class VirDomainWrapper(object): diff --git a/qubes/ext/qubesmanager.py b/qubes/ext/qubesmanager.py index 41b489cf..9b002d99 100644 --- a/qubes/ext/qubesmanager.py +++ b/qubes/ext/qubesmanager.py @@ -29,8 +29,8 @@ .. warning:: API defined here is not declared stable. ''' -import qubes.ext import dbus +import qubes.ext class QubesManager(qubes.ext.Extension): diff --git a/qubes/utils.py b/qubes/utils.py index 8f0e3121..6b012c17 100644 --- a/qubes/utils.py +++ b/qubes/utils.py @@ -29,11 +29,11 @@ import os import re import subprocess +import pkg_resources + import docutils import docutils.core import docutils.io -import pkg_resources - import qubes.exc @@ -158,5 +158,3 @@ def get_entry_point_one(group, name): ', '.join('{}.{}'.format(ep.module_name, '.'.join(ep.attrs)) for ep in epoints))) return epoints[0].load() - - From 774cbd499e25415566e089ef3fadf7e830403b2b Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 21:33:12 +0200 Subject: [PATCH 4/7] Add TestVM.is_halted() to fix failing tests --- qubes/tests/app.py | 6 ++++++ qubes/tests/init.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/qubes/tests/app.py b/qubes/tests/app.py index 0c81d733..ea1d7e95 100644 --- a/qubes/tests/app.py +++ b/qubes/tests/app.py @@ -42,6 +42,12 @@ class TestVM(qubes.vm.BaseVM): netid = qid uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') + def is_halted(self): + return False + + def get_power_state(): + return "Halted" + class TestApp(qubes.tests.TestEmitter): pass diff --git a/qubes/tests/init.py b/qubes/tests/init.py index 6969afc0..d8ffa324 100644 --- a/qubes/tests/init.py +++ b/qubes/tests/init.py @@ -293,6 +293,12 @@ class TestVM(qubes.vm.BaseVM): netid = qid uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') + def is_halted(self): + return False + + def get_power_state(): + return "Halted" + class TestApp(qubes.tests.TestEmitter): pass From 23e52775b6ca28ba8db9c98cb64b53f189f9716c Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 21:33:36 +0200 Subject: [PATCH 5/7] =?UTF-8?q?qubes.tests.init=20make=20pylint=20happy=20?= =?UTF-8?q?=E2=99=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qubes/tests/init.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qubes/tests/init.py b/qubes/tests/init.py index d8ffa324..351c8081 100644 --- a/qubes/tests/init.py +++ b/qubes/tests/init.py @@ -23,7 +23,6 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # -import os import unittest import uuid @@ -70,7 +69,7 @@ class TC_10_property(qubes.tests.QubesTestCase): try: class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder): testprop1 = qubes.property('testprop1') - except: # pylint: disable=bare-except + except: # pylint: disable=bare-except self.skipTest('MyTestHolder class definition failed') self.holder = MyTestHolder(None) @@ -120,6 +119,7 @@ class TC_10_property(qubes.tests.QubesTestCase): self.assertIs(prop, MyTestHolder.testprop1) self.assertEquals(value, 'testvalue') return 'settervalue' + class MyTestHolder(qubes.tests.TestEmitter, qubes.PropertyHolder): testprop1 = qubes.property('testprop1', setter=setter) holder = MyTestHolder(None) @@ -219,7 +219,6 @@ class TC_20_PropertyHolder(qubes.tests.QubesTestCase): self.holder = TestHolder(xml) - def test_000_property_list(self): self.assertListEqual([p.__name__ for p in self.holder.property_list()], ['testprop1', 'testprop2', 'testprop3', 'testprop4']) @@ -294,9 +293,9 @@ class TestVM(qubes.vm.BaseVM): uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') def is_halted(self): - return False + return True - def get_power_state(): + def get_power_state(self): return "Halted" class TestApp(qubes.tests.TestEmitter): From d380fb4aba2cdb4fbf4edeb779a7deab26afe077 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 21:56:50 +0200 Subject: [PATCH 6/7] =?UTF-8?q?qubes.tests.app=20make=20pylint=20happy=20?= =?UTF-8?q?=E2=99=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qubes/tests/app.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qubes/tests/app.py b/qubes/tests/app.py index ea1d7e95..e0190358 100644 --- a/qubes/tests/app.py +++ b/qubes/tests/app.py @@ -24,7 +24,6 @@ # import os -import unittest import uuid import lxml.etree @@ -43,9 +42,9 @@ class TestVM(qubes.vm.BaseVM): uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') def is_halted(self): - return False + return True - def get_power_state(): + def get_power_state(self): return "Halted" class TestApp(qubes.tests.TestEmitter): @@ -163,7 +162,7 @@ class TC_90_Qubes(qubes.tests.QubesTestCase): os.unlink('/tmp/qubestest.xml') except: pass - app = qubes.Qubes.create_empty_store('/tmp/qubestest.xml') + qubes.Qubes.create_empty_store('/tmp/qubestest.xml') @qubes.tests.skipUnlessGit def test_900_example_xml_in_doc(self): From 71161bfb934da8594f66d3303945ad63ffef5be9 Mon Sep 17 00:00:00 2001 From: Bahtiar `kalkin-` Gadimov Date: Wed, 13 Jul 2016 23:28:02 +0200 Subject: [PATCH 7/7] Add mock libvirt objects --- qubes/tests/app.py | 6 ++++++ qubes/tests/init.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/qubes/tests/app.py b/qubes/tests/app.py index e0190358..adbe8447 100644 --- a/qubes/tests/app.py +++ b/qubes/tests/app.py @@ -41,6 +41,12 @@ class TestVM(qubes.vm.BaseVM): netid = qid uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') + class MockLibvirt(object): + def undefine(self): + pass + + libvirt_domain = MockLibvirt() + def is_halted(self): return True diff --git a/qubes/tests/init.py b/qubes/tests/init.py index 351c8081..99105e47 100644 --- a/qubes/tests/init.py +++ b/qubes/tests/init.py @@ -292,6 +292,12 @@ class TestVM(qubes.vm.BaseVM): netid = qid uuid = uuid.uuid5(uuid.NAMESPACE_DNS, 'testvm') + class MockLibvirt(object): + def undefine(self): + pass + + libvirt_domain = MockLibvirt() + def is_halted(self): return True