diff --git a/ci/coveragerc b/ci/coveragerc
index 37b61cc..98a5244 100644
--- a/ci/coveragerc
+++ b/ci/coveragerc
@@ -1,3 +1,5 @@
[run]
source = qubesadmin
omit = qubesadmin/tests/*
+# breaks backup tests for unknown reason
+# concurrency=multiprocessing
diff --git a/qubesadmin/tests/backup/backupcompatibility.py b/qubesadmin/tests/backup/backupcompatibility.py
index 5569f0a..d5b98c6 100644
--- a/qubesadmin/tests/backup/backupcompatibility.py
+++ b/qubesadmin/tests/backup/backupcompatibility.py
@@ -20,11 +20,15 @@
#
import functools
import tempfile
+import unittest
+from distutils import spawn
from multiprocessing import Queue
import os
import subprocess
+import logging
+
try:
import unittest.mock as mock
except ImportError:
@@ -32,54 +36,15 @@ except ImportError:
import re
import multiprocessing
-
+import pkg_resources
import sys
import qubesadmin.backup.core2
+import qubesadmin.backup.core3
import qubesadmin.storage
import qubesadmin.tests
import qubesadmin.tests.backup
-QUBESXML_R2B2 = '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-'''
-
-QUBESXML_R2 = '''
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-'''
MANGLED_SUBDIRS_R2 = {
"test-work": "vm5",
@@ -90,6 +55,16 @@ MANGLED_SUBDIRS_R2 = {
"test-testhvm": "vm14",
"test-net": "vm16",
}
+MANGLED_SUBDIRS_R4 = {
+ "test-work": "vm3",
+ "test-fedora-25-clone": "vm7",
+ "test-custom-template-appvm": "vm31",
+ "test-standalonevm": "vm4",
+ "test-proxy": "vm30",
+ "test-hvm": "vm9",
+ "test-net": "vm6",
+ "test-d8test": "vm20",
+}
APPTEMPLATE_R2B2 = '''
[Desktop Entry]
@@ -153,6 +128,14 @@ compressed={compressed}
compression-filter=gzip
'''
+BACKUP_HEADER_R4 = '''version=4
+hmac-algorithm=scrypt
+encrypted=True
+compressed={compressed}
+compression-filter=gzip
+backup-id=20161020T123455-1234
+'''
+
parsed_qubes_xml_r2 = {
'domains': {
'dom0': {
@@ -424,13 +407,314 @@ parsed_qubes_xml_r2 = {
},
}
+parsed_qubes_xml_v4 = {
+ 'domains': {
+ 'dom0': {
+ 'klass': 'AdminVM',
+ 'label': 'black',
+ 'properties': {},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {},
+ 'template': None,
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'fedora-25': {
+ 'klass': 'TemplateVM',
+ 'label': 'black',
+ 'properties': {},
+ 'devices': {},
+ 'tags': {'created-by-test-work'},
+ 'features': {
+ 'gui': '1',
+ 'qrexec': 'True',
+ 'updates-available': False
+ },
+ 'template': None,
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'fedora-25-lvm': {
+ 'klass': 'TemplateVM',
+ 'label': 'black',
+ 'properties': {
+ 'maxmem': '4000',
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {},
+ 'template': None,
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'debian-8': {
+ 'klass': 'TemplateVM',
+ 'label': 'black',
+ 'properties': {},
+ 'devices': {},
+ 'tags': {'created-by-dom0'},
+ 'features': {
+ 'gui': '1',
+ 'qrexec': 'True',
+ 'updates-available': False},
+ 'template': None,
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'sys-net': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'hvm': 'False',
+ 'kernelopts': 'nopat i8042.nokbd i8042.noaux',
+ 'maxmem': '300',
+ 'memory': '300',
+ 'netvm': None,
+ 'default_user': 'user',
+ 'provides_network': 'True'},
+ 'devices': {
+ 'pci': {
+ ('dom0', '02_00.0'): {},
+ }
+ },
+ 'tags': set(),
+ 'features': {
+ 'service.clocksync': '1',
+ 'service.meminfo-writer': False
+ },
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'sys-firewall': {
+ 'klass': 'AppVM',
+ 'label': 'green',
+ 'properties': {
+ 'autostart': 'True',
+ 'memory': '500',
+ 'provides_network': 'True'
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {},
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'test-d8test': {
+ 'klass': 'AppVM',
+ 'label': 'gray',
+ 'properties': {'debug': 'True', 'kernel': None},
+ 'devices': {},
+ 'tags': {'created-by-dom0'},
+ 'features': {},
+ 'template': 'debian-8',
+ 'backup_path': 'appvms/test-d8test',
+ 'included_in_backup': True,
+ },
+ 'fedora-25-dvm': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'dispvm_allowed': 'True',
+ 'vcpus': '1',
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {
+ 'internal': '1', 'service.meminfo-writer': '1'},
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'fedora-25-clone-dvm': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'vcpus': '1',
+ 'dispvm_allowed': 'True',
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {
+ 'internal': '1', 'service.meminfo-writer': '1'},
+ 'template': 'test-fedora-25-clone',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'vault': {
+ 'klass': 'AppVM',
+ 'label': 'black',
+ 'properties': {'hvm': 'False', 'maxmem': '1536', 'netvm': None},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {},
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'personal': {
+ 'klass': 'AppVM',
+ 'label': 'yellow',
+ 'properties': {'netvm': 'sys-firewall'},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {
+ 'feat1': '1',
+ 'feat2': False,
+ 'feat32': '1',
+ 'featdis': False,
+ 'xxx': '1'
+ },
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'untrusted': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'netvm': None,
+ 'backup_timestamp': '1474318497',
+ 'default_dispvm': 'fedora-25-clone-dvm',
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {'service.meminfo-writer': '1'},
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'sys-usb': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'hvm': 'False',
+ 'autostart': 'True',
+ 'maxmem': '400',
+ 'provides_network': 'True',
+ },
+ 'devices': {},
+ 'tags': set(),
+ 'features': {
+ 'service.meminfo-writer': False,
+ 'service.network-manager': False,
+ },
+ 'template': 'fedora-25',
+ 'backup_path': None,
+ 'included_in_backup': False,
+ },
+ 'test-proxy': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {'netvm': 'sys-net', 'provides_network': 'True'},
+ 'devices': {},
+ 'tags': {'created-by-dom0'},
+ 'features': {},
+ 'template': 'debian-8',
+ 'backup_path': 'appvms/test-proxy',
+ 'included_in_backup': True,
+ },
+ 'test-hvm': {
+ 'klass': 'StandaloneVM',
+ 'label': 'purple',
+ 'properties': {'hvm': 'True', 'maxmem': '4000'},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {'service.meminfo-writer': False},
+ 'template': None,
+ 'backup_path': 'appvms/test-hvm',
+ 'included_in_backup': True,
+ 'root_size': 2097664,
+ },
+ 'test-work': {
+ 'klass': 'AppVM',
+ 'label': 'green',
+ 'properties': {
+ 'ip': '192.168.0.1',
+ 'maxmem': '4000',
+ 'memory': '400'},
+ 'devices': {},
+ 'tags': {'tag1', 'tag2'},
+ 'features': {'service.meminfo-writer': '1'},
+ 'template': 'fedora-25',
+ 'backup_path': 'appvms/test-work',
+ 'included_in_backup': True,
+ },
+ 'test-fedora-25-clone': {
+ 'klass': 'TemplateVM',
+ 'label': 'black',
+ 'properties': {'maxmem': '4000'},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {'service.meminfo-writer': '1'},
+ 'template': None,
+ 'backup_path': 'vm-templates/test-fedora-25-clone',
+ 'included_in_backup': True,
+ },
+ 'test-custom-template-appvm': {
+ 'klass': 'AppVM',
+ 'label': 'yellow',
+ 'properties': {'debug': 'True', 'kernel': None},
+ 'devices': {},
+ 'tags': {'created-by-dom0'},
+ 'features': {},
+ 'template': 'test-fedora-25-clone',
+ 'backup_path': 'appvms/test-custom-template-appvm',
+ 'included_in_backup': True,
+ },
+ 'test-standalonevm': {
+ 'klass': 'StandaloneVM',
+ 'label': 'blue',
+ 'properties': {'maxmem': '4000'},
+ 'devices': {},
+ 'tags': set(),
+ 'features': {},
+ 'template': None,
+ 'backup_path': 'appvms/test-standalonevm',
+ 'included_in_backup': True,
+ 'root_size': 2097664,
+ },
+ 'test-net': {
+ 'klass': 'AppVM',
+ 'label': 'red',
+ 'properties': {
+ 'maxmem': '300',
+ 'memory': '300',
+ 'netvm': None,
+ 'provides_network': 'True'
+ },
+ 'devices': {
+ 'pci': {
+ ('dom0', '03_00.0'): {},
+ }
+ },
+ 'tags': set(),
+ 'features': {
+ 'service.ntpd': False,
+ 'service.meminfo-writer': False
+ },
+ 'template': 'fedora-25',
+ 'backup_path': 'appvms/test-net',
+ 'included_in_backup': True,
+ },
+ },
+ 'globals': {
+ 'default_template': 'fedora-25',
+ 'default_kernel': '4.9.31-17',
+ 'default_netvm': 'sys-firewall',
+ 'default_dispvm': 'fedora-25-dvm',
+ #'default_fw_netvm': 'sys-net',
+ 'clockvm': 'sys-net',
+ 'updatevm': 'sys-firewall'
+ },
+}
+
class TC_00_QubesXML(qubesadmin.tests.QubesTestCase):
- def assertCorrectlyConverted(self, xml_data, expected_data):
- with tempfile.NamedTemporaryFile() as qubes_xml:
- qubes_xml.file.write(xml_data.encode())
- backup_app = qubesadmin.backup.core2.Core2Qubes(qubes_xml.name)
+ def assertCorrectlyConverted(self, backup_app, expected_data):
self.assertCountEqual(backup_app.domains.keys(),
expected_data['domains'].keys())
for vm in expected_data['domains']:
@@ -458,7 +742,19 @@ class TC_00_QubesXML(qubesadmin.tests.QubesTestCase):
self.assertEqual(backup_app.globals, expected_data['globals'])
def test_000_qubes_xml_r2(self):
- self.assertCorrectlyConverted(QUBESXML_R2, parsed_qubes_xml_r2)
+ xml_data = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
+ with tempfile.NamedTemporaryFile() as qubes_xml:
+ qubes_xml.file.write(xml_data)
+ backup_app = qubesadmin.backup.core2.Core2Qubes(qubes_xml.name)
+ self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_r2)
+
+ def test_010_qubes_xml_r4(self):
+ self.maxDiff = None
+ xml_data = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
+ with tempfile.NamedTemporaryFile() as qubes_xml:
+ qubes_xml.file.write(xml_data)
+ backup_app = qubesadmin.backup.core3.Core3Qubes(qubes_xml.name)
+ self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_v4)
# backup code use multiprocessing, synchronize with main process
class AppProxy(object):
@@ -655,6 +951,91 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
"vm-templates/test-template-clone")),
appmenus_list)
+ def create_v4_files(self):
+ appmenus_list = [
+ "firefox", "gnome-terminal", "evince", "evolution",
+ "mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
+ "gedit", "gpk-update-viewer", "gpk-application"
+ ]
+
+ os.mkdir(self.fullpath("appvms"))
+ os.mkdir(self.fullpath("vm-templates"))
+
+ # normal AppVMs
+ for vm in ('test-work', 'test-d8test', 'test-proxy',
+ 'test-custom-template-appvm', 'test-net'):
+ os.mkdir(self.fullpath('appvms/{}'.format(vm)))
+ self.create_whitelisted_appmenus(self.fullpath(
+ 'appvms/{}/whitelisted-appmenus.list'.format(vm)))
+ self.create_private_img(self.fullpath('appvms/{}/private.img'.format(
+ vm)))
+
+ # StandaloneVMs
+ for vm in ('test-standalonevm', 'test-hvm'):
+ os.mkdir(self.fullpath('appvms/{}'.format(vm)))
+ self.create_whitelisted_appmenus(self.fullpath(
+ 'appvms/{}/whitelisted-appmenus.list'.format(vm)))
+ self.create_private_img(self.fullpath(
+ 'appvms/{}/private.img'.format(vm)))
+ self.create_sparse(
+ self.fullpath('appvms/{}/root.img'.format(vm)), 10*2**30)
+ self.fill_image(self.fullpath('appvms/{}/root.img'.format(vm)),
+ 1024*1024, True,
+ signature='{}/root'.format(vm).encode())
+
+ # only for Linux one
+ os.mkdir(self.fullpath('appvms/test-standalonevm/apps.templates'))
+ self.create_appmenus(
+ self.fullpath('appvms/test-standalonevm/apps.templates'),
+ APPTEMPLATE_R2B2,
+ appmenus_list)
+
+ # Custom template
+ os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone"))
+ self.create_private_img(
+ self.fullpath("vm-templates/test-fedora-25-clone/private.img"))
+ self.create_sparse(self.fullpath(
+ "vm-templates/test-fedora-25-clone/root.img"), 10*2**20)
+ self.fill_image(self.fullpath(
+ "vm-templates/test-fedora-25-clone/root.img"), 1*2**20, True,
+ signature=b'test-fedora-25-clone/root')
+ self.create_volatile_img(self.fullpath(
+ "vm-templates/test-fedora-25-clone/volatile.img"))
+ self.create_whitelisted_appmenus(self.fullpath(
+ "vm-templates/test-fedora-25-clone/whitelisted-appmenus.list"))
+ self.create_whitelisted_appmenus(self.fullpath(
+ "vm-templates/test-fedora-25-clone/vm-whitelisted-appmenus.list"))
+ os.mkdir(
+ self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"))
+ self.create_appmenus(
+ self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"),
+ APPTEMPLATE_R2B2,
+ appmenus_list)
+ os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone/apps"))
+ self.create_appmenus(
+ self.fullpath("vm-templates/test-fedora-25-clone/apps"),
+ APPTEMPLATE_R2B2.replace("%VMNAME%", "test-fedora-25-clone")
+ .replace("%VMDIR%", self.fullpath(
+ "vm-templates/test-fedora-25-clone")),
+ appmenus_list)
+
+ def scrypt_encrypt(self, f_name, output_name=None, password='qubes',
+ basedir=None):
+ if basedir is None:
+ basedir = self.backupdir
+ if output_name is None:
+ output_name = f_name + '.enc'
+ if f_name == 'backup-header':
+ scrypt_pass = 'backup-header!' + password
+ else:
+ scrypt_pass = '20161020T123455-1234!{}!{}'.format(f_name, password)
+ p = subprocess.Popen(['scrypt', 'enc', '-P', '-t', '0.1',
+ os.path.join(basedir, f_name), os.path.join(basedir, output_name)],
+ stdin=subprocess.PIPE)
+ p.communicate(scrypt_pass.encode())
+ assert p.wait() == 0
+ return output_name
+
def calculate_hmac(self, f_name, algorithm="sha512", password="qubes"):
with open(self.fullpath(f_name), "r") as f_data:
with open(self.fullpath(f_name+".hmac"), "w") as f_hmac:
@@ -715,6 +1096,42 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
self.append_backup_stream(part_with_dir+".hmac", stream,
basedir=self.fullpath("stage1"))
+ def handle_v4_file(self, f_name, subdir, stream, compressed=True):
+ # create inner archive
+ tar_cmdline = ["tar", "-Pc", '--sparse',
+ '-C', self.fullpath(os.path.dirname(f_name)),
+ '--xform', 's:^%s:%s\\0:' % (
+ os.path.basename(f_name),
+ subdir),
+ os.path.basename(f_name)
+ ]
+ if compressed:
+ tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
+ tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
+ data = tar.stdout
+
+ stage1_dir = self.fullpath(os.path.join("stage1", subdir))
+ if not os.path.exists(stage1_dir):
+ os.makedirs(stage1_dir)
+ subprocess.check_call(["split", "--numeric-suffixes",
+ "--suffix-length=3",
+ "--bytes="+str(100*1024*1024), "-",
+ os.path.join(stage1_dir,
+ os.path.basename(f_name+"."))],
+ stdin=data)
+ data.close()
+
+ for part in sorted(os.listdir(stage1_dir)):
+ if not re.match(
+ r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
+ part):
+ continue
+ part_with_dir = os.path.join(subdir, part)
+ f_name = self.scrypt_encrypt(part_with_dir,
+ basedir=self.fullpath('stage1'))
+ self.append_backup_stream(f_name, stream,
+ basedir=self.fullpath("stage1"))
+
def create_v3_backup(self, encrypted=True, compressed=True):
"""
Create "backup format 3" backup - used in R2 and R3.0
@@ -732,15 +1149,15 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
self.calculate_hmac("backup-header")
self.append_backup_stream("backup-header", output)
self.append_backup_stream("backup-header.hmac", output)
- with open(self.fullpath("qubes.xml"), "w") as f:
+ with open(self.fullpath("qubes.xml"), "wb") as f:
+ qubesxml = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
if encrypted:
- qubesxml = QUBESXML_R2
for vmname, subdir in MANGLED_SUBDIRS_R2.items():
- qubesxml = re.sub(r"[a-z-]*/{}".format(vmname),
- subdir, qubesxml)
+ qubesxml = re.sub(r"[a-z-]*/{}".format(vmname).encode(),
+ subdir.encode(), qubesxml)
f.write(qubesxml)
else:
- f.write(QUBESXML_R2)
+ f.write(qubesxml)
self.handle_v3_file("qubes.xml", "", output, encrypted=encrypted,
compressed=compressed)
@@ -770,6 +1187,44 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
output.close()
+ def create_v4_backup(self, compressed=True):
+ """
+ Create "backup format 4" backup - used in R4.0
+
+ :param compressed: Should the backup be compressed
+ :return:
+ """
+ output = open(self.fullpath("backup.bin"), "w")
+ with open(self.fullpath("backup-header"), "w") as f:
+ f.write(BACKUP_HEADER_R4.format(
+ compressed=str(compressed)
+ ))
+ self.scrypt_encrypt("backup-header", output_name='backup-header.hmac')
+ self.append_backup_stream("backup-header", output)
+ self.append_backup_stream("backup-header.hmac", output)
+ with open(self.fullpath("qubes.xml"), "wb") as f:
+ qubesxml = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
+ for vmname, subdir in MANGLED_SUBDIRS_R4.items():
+ qubesxml = re.sub(
+ r'backup-path">[a-z-]*/{}'.format(vmname).encode(),
+ ('backup-path">' + subdir).encode(),
+ qubesxml)
+ f.write(qubesxml)
+
+ self.handle_v4_file("qubes.xml", "", output, compressed=compressed)
+
+ self.create_v4_files()
+ for vm_type in ["appvms", "vm-templates"]:
+ for vm_name in os.listdir(self.fullpath(vm_type)):
+ vm_dir = os.path.join(vm_type, vm_name)
+ for f_name in os.listdir(self.fullpath(vm_dir)):
+ subdir = MANGLED_SUBDIRS_R4[vm_name]
+ self.handle_v4_file(
+ os.path.join(vm_dir, f_name),
+ subdir+'/', output, compressed=compressed)
+
+ output.close()
+
def setup_expected_calls(self, parsed_qubes_xml, templates_map=None):
if templates_map is None:
templates_map = {}
@@ -887,6 +1342,10 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
(name, 'admin.vm.feature.Set', feature,
str(value).encode())] = b'0\0'
+ for tag in vm['tags']:
+ self.app.expected_calls[
+ (name, 'admin.vm.tag.Set', tag, None)] = b'0\0'
+
orig_admin_vm_list = self.app.expected_calls[
('dom0', 'admin.vm.List', None, None)]
self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
@@ -957,6 +1416,81 @@ class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
self.assertAllCalled()
+ @unittest.skipUnless(spawn.find_executable('scrypt'),
+ "scrypt not installed")
+ def test_230_r4(self):
+ self.create_v4_backup(False)
+ self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
+ b'0\0dom0 class=AdminVM state=Running\n'
+ b'fedora-25 class=TemplateVM state=Halted\n'
+ b'testvm class=AppVM state=Running\n'
+ b'sys-net class=AppVM state=Running\n'
+ )
+ self.app.expected_calls[
+ ('dom0', 'admin.property.Get', 'default_template', None)] = \
+ b'0\0default=no type=vm fedora-25'
+ self.app.expected_calls[
+ ('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
+ b'0\0default=no type=bool True'
+ self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
+ 'debian-8': 'fedora-25'
+ })
+
+ qubesd_calls_queue = multiprocessing.Queue()
+
+ with mock.patch('qubesadmin.storage.Volume',
+ functools.partial(MockVolume, qubesd_calls_queue)):
+ self.restore_backup(self.fullpath("backup.bin"), options={
+ 'use-default-template': True,
+ 'use-default-netvm': True,
+ })
+
+ # retrieve calls from other multiprocess.Process instances
+ while not qubesd_calls_queue.empty():
+ call_args = qubesd_calls_queue.get()
+ self.app.qubesd_call(*call_args)
+ qubesd_calls_queue.close()
+
+ self.assertAllCalled()
+
+ @unittest.skipUnless(spawn.find_executable('scrypt'),
+ "scrypt not installed")
+ def test_230_r4_compressed(self):
+ self.create_v4_backup(True)
+
+ self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
+ b'0\0dom0 class=AdminVM state=Running\n'
+ b'fedora-25 class=TemplateVM state=Halted\n'
+ b'testvm class=AppVM state=Running\n'
+ b'sys-net class=AppVM state=Running\n'
+ )
+ self.app.expected_calls[
+ ('dom0', 'admin.property.Get', 'default_template', None)] = \
+ b'0\0default=no type=vm fedora-25'
+ self.app.expected_calls[
+ ('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
+ b'0\0default=no type=bool True'
+ self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
+ 'debian-8': 'fedora-25'
+ })
+
+ qubesd_calls_queue = multiprocessing.Queue()
+
+ with mock.patch('qubesadmin.storage.Volume',
+ functools.partial(MockVolume, qubesd_calls_queue)):
+ self.restore_backup(self.fullpath("backup.bin"), options={
+ 'use-default-template': True,
+ 'use-default-netvm': True,
+ })
+
+ # retrieve calls from other multiprocess.Process instances
+ while not qubesd_calls_queue.empty():
+ call_args = qubesd_calls_queue.get()
+ self.app.qubesd_call(*call_args)
+ qubesd_calls_queue.close()
+
+ self.assertAllCalled()
+
class TC_11_BackupCompatibilityIntoLVM(TC_10_BackupCompatibility):
storage_pool = 'some-pool'
diff --git a/qubesadmin/tests/backup/v3-qubes.xml b/qubesadmin/tests/backup/v3-qubes.xml
new file mode 100644
index 0000000..1d60230
--- /dev/null
+++ b/qubesadmin/tests/backup/v3-qubes.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/qubesadmin/tests/backup/v4-qubes.xml b/qubesadmin/tests/backup/v4-qubes.xml
new file mode 100644
index 0000000..f02c4ac
--- /dev/null
+++ b/qubesadmin/tests/backup/v4-qubes.xml
@@ -0,0 +1,526 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ sys-net
+ fedora-25-dvm
+ 4.9.31-17
+ sys-firewall
+ fedora-25
+ sys-firewall
+
+
+
+
+ True
+
+ label-5
+ test-d8test
+ 20
+ 9204481c-7e37-42a6-8a66-b4cc63d65f11
+ debian-8
+
+
+ True
+ appvms/test-d8test
+ 20971520
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-1
+ test-proxy
+ 30
+ 367c64e6-ab8c-42df-91b6-c9d4c7d015f2
+ debian-8
+ True
+ sys-net
+
+
+ True
+ appvms/test-proxy
+ 209715200
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+ debian-8
+ 16
+ 0e2fa953-016f-4486-9e36-c9b386fc2bac
+
+
+ True
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+
+
+
+
+
+
+
+
+
+ True
+
+ label-3
+ test-custom-template-appvm
+ 31
+ 1bdb7b32-8a4b-4bb7-8da0-6b06494f642c
+ test-fedora-25-clone
+
+
+ True
+ appvms/test-custom-template-appvm
+ 20971520
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+ 4000
+ test-fedora-25-clone
+ 7
+ e3b8d458-8c4c-4ccb-b00d-b7ae454cb08f
+
+
+ 1
+ True
+ vm-templates/test-fedora-25-clone
+ 2097152000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ label-1
+ fedora-25-clone-dvm
+ 10
+ 30daa6b5-693b-460a-9da7-99078a2d9d14
+ test-fedora-25-clone
+ 1
+
+
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ label-1
+ fedora-25-dvm
+ 10
+ 30daa6b5-693b-460a-9da7-99078a2d9d14
+ fedora-25
+ 1
+
+
+ 1
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+ 4000
+ fedora-25-lvm
+ 14
+ 20785bf4-42fa-4035-ab95-c1bf054c153a
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+ fedora-25
+ 8
+ 51870d8e-2e71-41d2-9582-30661f611004
+
+
+ True
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ label-7
+ 4000
+ test-hvm
+ 9
+ 9909066b-0f03-4725-ad9e-fa3561d5566e
+
+
+
+ True
+ appvms/test-hvm
+ 2097152000
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1474318497
+ label-1
+ untrusted
+ 11
+ 359b8e38-9e50-46a3-a42c-8d3bb15d3890
+ fedora-25-clone-dvm
+
+ fedora-25
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-3
+ personal
+ 22
+ efa8ddc4-6661-4231-9bfd-ef34907da358
+ sys-firewall
+ fedora-25
+
+
+ 1
+ 1
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ label-4
+ 500
+ sys-firewall
+ True
+ 21
+ 40f0775a-c259-44bc-be57-6148c67c42c7
+ fedora-25
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ user
+ False
+ label-1
+ 300
+ 300
+ sys-net
+
+ True
+ nopat i8042.nokbd i8042.noaux
+ 2
+ eb8b1680-d4fa-449b-8baa-b5146d9b62b7
+ fedora-25
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-1
+ 300
+ test-net
+
+ 300
+ True
+ 6
+ d5e9a792-9247-4f6f-a936-b7c65a1adfac
+ fedora-25
+
+
+
+
+ True
+ appvms/test-net
+ 209715200
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ label-1
+ False
+ 400
+ sys-usb
+ 5
+ 0da5616e-f2db-4c17-9c0d-cdef2e728344
+ fedora-25
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-8
+ vault
+
+ 13
+ d5284828-988d-46e2-8388-a09c495475e3
+ fedora-25
+ False
+ 1536
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 192.168.0.1
+ label-4
+ 4000
+ 400
+ test-work
+ 3
+ 07c17d1e-0982-417d-b19e-a81d51bed423
+ fedora-25
+
+
+ 1
+ True
+ appvms/test-work
+ 2097152000
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ label-6
+ 4000
+ test-standalonevm
+ 4
+ e8034b8a-29b3-4f02-b8cb-05cd74a4bb68
+
+
+ True
+ appvms/test-standalonevm
+ 2097152000
+
+
+
+
+
+
+
+
+
+
+
diff --git a/setup.py b/setup.py
index 6283ecd..25b04dd 100644
--- a/setup.py
+++ b/setup.py
@@ -33,6 +33,9 @@ if __name__ == '__main__':
license='LGPL2.1+',
url='https://www.qubes-os.org/',
packages=setuptools.find_packages(exclude=exclude),
+ package_data={
+ 'qubesadmin.tests.backup': ['*.xml'],
+ },
entry_points={
'console_scripts': list(get_console_scripts()),
'qubesadmin.vm': [