88c7c66bb9
...when verifying old backup restored. It wasn't present in the backup, but its presence is expected in some cases. Properly setting 'servicevm' feature is tested elsewhere.
540 lines
29 KiB
Python
540 lines
29 KiB
Python
#
|
|
# The Qubes OS Project, http://www.qubes-os.org
|
|
#
|
|
# Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
|
|
#
|
|
# This library is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU Lesser General Public
|
|
# License as published by the Free Software Foundation; either
|
|
# version 2.1 of the License, or (at your option) any later version.
|
|
#
|
|
# This library 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
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this library; if not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
from multiprocessing import Queue
|
|
|
|
import asyncio
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
|
|
import unittest
|
|
import sys
|
|
import re
|
|
|
|
import qubes.tests
|
|
import qubes.tests.integ.backup
|
|
|
|
|
|
QUBESXML_R2 = '''
|
|
<QubesVmCollection updatevm="3" default_kernel="3.7.6-2" default_netvm="3" default_fw_netvm="2" default_template="1" clockvm="2">
|
|
<QubesTemplateVm installed_by_rpm="True" kernel="3.7.6-2" uses_default_kernelopts="True" qid="1" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="fedora-20-x64.conf" label="black" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{ 'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="fedora-20-x64" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/vm-templates/fedora-20-x64"/>
|
|
<QubesNetVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="2" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="netvm.conf" label="red" template_qid="1" kernelopts="iommu=soft swiotlb=4096" memory="200" default_user="user" volatile_img="volatile.img" services="{'ntpd': False, 'meminfo-writer': False}" maxmem="1535" pcidevs="['02:00.0', '03:00.0']" name="netvm" netid="1" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/netvm"/>
|
|
<QubesProxyVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="3" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="firewallvm.conf" label="green" template_qid="1" kernelopts="" memory="200" default_user="user" netvm_qid="2" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="firewallvm" netid="2" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/firewallvm"/>
|
|
<QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="4" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="True" conf_file="fedora-20-x64-dvm.conf" label="gray" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{ 'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="fedora-20-x64-dvm" private_img="private.img" vcpus="1" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/fedora-20-x64-dvm"/>
|
|
<QubesAppVm backup_content="True" backup_path="appvms/test-work" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="5" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-work.conf" label="green" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-work" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-work"/>
|
|
<QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="6" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="banking.conf" label="green" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="banking" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/banking"/>
|
|
<QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="7" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="personal.conf" label="yellow" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="personal" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/personal"/>
|
|
<QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="8" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="untrusted.conf" label="red" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="12" uses_default_netvm="False" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="untrusted" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/untrusted"/>
|
|
<QubesTemplateVm backup_size="104857600" backup_content="True" backup_path="vm-templates/test-template-clone" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="9" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-template-clone.conf" label="green" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-template-clone" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/vm-templates/test-template-clone"/>
|
|
<QubesAppVm backup_size="104857600" backup_content="True" backup_path="appvms/test-custom-template-appvm" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="10" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-custom-template-appvm.conf" label="yellow" template_qid="9" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-custom-template-appvm" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-custom-template-appvm"/>
|
|
<QubesAppVm backup_size="104857600" backup_content="True" backup_path="appvms/test-standalonevm" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="11" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-standalonevm.conf" label="blue" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-standalonevm" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-standalonevm"/>
|
|
<QubesProxyVm backup_size="104857600" backup_content="True" backup_path="servicevms/test-testproxy" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="12" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-testproxy.conf" label="red" template_qid="1" kernelopts="" memory="200" default_user="user" netvm_qid="3" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-testproxy" netid="3" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/test-testproxy"/>
|
|
<QubesProxyVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="13" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="testproxy2.conf" label="red" template_qid="9" kernelopts="" memory="200" default_user="user" netvm_qid="2" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="testproxy2" netid="4" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/testproxy2"/>
|
|
<QubesHVm backup_size="104857600" backup_content="True" backup_path="appvms/test-testhvm" installed_by_rpm="False" netvm_qid="none" qid="14" include_in_backups="True" timezone="localtime" qrexec_timeout="60" conf_file="test-testhvm.conf" label="purple" template_qid="none" internal="False" memory="512" uses_default_netvm="True" services="{'meminfo-writer': False}" default_user="user" pcidevs="[]" name="test-testhvm" qrexec_installed="False" private_img="private.img" drive="None" vcpus="2" root_img="root.img" guiagent_installed="False" debug="False" dir_path="/var/lib/qubes/appvms/test-testhvm"/>
|
|
<QubesDisposableVm dispid="50" firewall_conf="firewall.xml" label="red" name="disp50" netvm_qid="2" qid="15" template_qid="1"/>
|
|
</QubesVmCollection>
|
|
'''
|
|
|
|
MANGLED_SUBDIRS_R2 = {
|
|
"test-work": "vm5",
|
|
"test-template-clone": "vm9",
|
|
"test-custom-template-appvm": "vm10",
|
|
"test-standalonevm": "vm11",
|
|
"test-testproxy": "vm12",
|
|
"test-testhvm": "vm14",
|
|
}
|
|
|
|
APPTEMPLATE_R2B2 = '''
|
|
[Desktop Entry]
|
|
Name=%VMNAME%: {name}
|
|
GenericName=%VMNAME%: {name}
|
|
GenericName[ca]=%VMNAME%: Navegador web
|
|
GenericName[cs]=%VMNAME%: Webový prohlížeč
|
|
GenericName[es]=%VMNAME%: Navegador web
|
|
GenericName[fa]=%VMNAME%: مرورر اینترنتی
|
|
GenericName[fi]=%VMNAME%: WWW-selain
|
|
GenericName[fr]=%VMNAME%: Navigateur Web
|
|
GenericName[hu]=%VMNAME%: Webböngésző
|
|
GenericName[it]=%VMNAME%: Browser Web
|
|
GenericName[ja]=%VMNAME%: ウェブ・ブラウザ
|
|
GenericName[ko]=%VMNAME%: 웹 브라우저
|
|
GenericName[nb]=%VMNAME%: Nettleser
|
|
GenericName[nl]=%VMNAME%: Webbrowser
|
|
GenericName[nn]=%VMNAME%: Nettlesar
|
|
GenericName[no]=%VMNAME%: Nettleser
|
|
GenericName[pl]=%VMNAME%: Przeglądarka WWW
|
|
GenericName[pt]=%VMNAME%: Navegador Web
|
|
GenericName[pt_BR]=%VMNAME%: Navegador Web
|
|
GenericName[sk]=%VMNAME%: Internetový prehliadač
|
|
GenericName[sv]=%VMNAME%: Webbläsare
|
|
Comment={comment}
|
|
Comment[ca]=Navegueu per el web
|
|
Comment[cs]=Prohlížení stránek World Wide Webu
|
|
Comment[de]=Im Internet surfen
|
|
Comment[es]=Navegue por la web
|
|
Comment[fa]=صفحات شبه جهانی اینترنت را مرور نمایید
|
|
Comment[fi]=Selaa Internetin WWW-sivuja
|
|
Comment[fr]=Navigue sur Internet
|
|
Comment[hu]=A világháló böngészése
|
|
Comment[it]=Esplora il web
|
|
Comment[ja]=ウェブを閲覧します
|
|
Comment[ko]=웹을 돌아 다닙니다
|
|
Comment[nb]=Surf på nettet
|
|
Comment[nl]=Verken het internet
|
|
Comment[nn]=Surf på nettet
|
|
Comment[no]=Surf på nettet
|
|
Comment[pl]=Przeglądanie stron WWW
|
|
Comment[pt]=Navegue na Internet
|
|
Comment[pt_BR]=Navegue na Internet
|
|
Comment[sk]=Prehliadanie internetu
|
|
Comment[sv]=Surfa på webben
|
|
Exec=qvm-run -q --tray -a %VMNAME% '{command} %u'
|
|
Categories=Network;WebBrowser;
|
|
X-Qubes-VmName=%VMNAME%
|
|
Icon=%VMDIR%/icon.png
|
|
'''
|
|
|
|
BACKUP_HEADER_R2 = '''version=3
|
|
hmac-algorithm=SHA512
|
|
crypto-algorithm=aes-256-cbc
|
|
encrypted={encrypted}
|
|
compressed={compressed}
|
|
compression-filter=gzip
|
|
'''
|
|
|
|
class TC_00_BackupCompatibility(
|
|
qubes.tests.integ.backup.BackupTestsMixin, qubes.tests.SystemTestCase):
|
|
|
|
def tearDown(self):
|
|
prefixes = ["test-", "disp-test-"]
|
|
if 'disp-no-netvm' not in self.host_app.domains:
|
|
prefixes.append('disp-no-netvm')
|
|
self.remove_test_vms(prefix=prefixes)
|
|
super(TC_00_BackupCompatibility, self).tearDown()
|
|
|
|
def create_whitelisted_appmenus(self, filename):
|
|
f = open(filename, "w")
|
|
f.write("gnome-terminal.desktop\n")
|
|
f.write("nautilus.desktop\n")
|
|
f.write("firefox.desktop\n")
|
|
f.write("mozilla-thunderbird.desktop\n")
|
|
f.write("libreoffice-startcenter.desktop\n")
|
|
f.close()
|
|
|
|
def create_appmenus(self, dir, template, list):
|
|
for name in list:
|
|
f = open(os.path.join(dir, name + ".desktop"), "w")
|
|
f.write(template.format(name=name, comment=name, command=name))
|
|
f.close()
|
|
|
|
def create_private_img(self, filename):
|
|
self.create_sparse(filename, 2*2**30)
|
|
subprocess.check_call(["/usr/sbin/mkfs.ext4", "-q", "-F", filename])
|
|
|
|
def create_volatile_img(self, filename):
|
|
self.create_sparse(filename, 11.5*2**30)
|
|
# here used to be sfdisk call with "0,1024,S\n,10240,L\n" input,
|
|
# but since sfdisk folks like to change command arguments in
|
|
# incompatible way, have an partition table verbatim here
|
|
ptable = (
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\xab\x39\xd5\xd4\x00\x00\x20\x00'
|
|
'\x00\x21\xaa\x82\x82\x28\x08\x00\x00\x00\x00\x00\x00\x20\xaa\x00'
|
|
'\x82\x29\x15\x83\x9c\x79\x08\x00\x00\x20\x00\x00\x01\x40\x00\x00'
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
|
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x55'
|
|
)
|
|
with open(filename, 'r+') as f:
|
|
f.seek(0x1b0)
|
|
f.write(ptable)
|
|
|
|
# TODO: mkswap
|
|
|
|
def fullpath(self, name):
|
|
return os.path.join(self.backupdir, name)
|
|
|
|
def create_v1_files(self, r2b2=False):
|
|
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("servicevms"))
|
|
os.mkdir(self.fullpath("vm-templates"))
|
|
|
|
# normal AppVM
|
|
os.mkdir(self.fullpath("appvms/test-work"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"appvms/test-work/whitelisted-appmenus.list"))
|
|
os.symlink("/usr/share/qubes/icons/green.png",
|
|
self.fullpath("appvms/test-work/icon.png"))
|
|
self.create_private_img(self.fullpath("appvms/test-work/private.img"))
|
|
|
|
# StandaloneVM
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"appvms/test-standalonevm/whitelisted-appmenus.list"))
|
|
os.symlink("/usr/share/qubes/icons/blue.png",
|
|
self.fullpath("appvms/test-standalonevm/icon.png"))
|
|
self.create_private_img(self.fullpath(
|
|
"appvms/test-standalonevm/private.img"))
|
|
self.create_sparse(
|
|
self.fullpath("appvms/test-standalonevm/root.img"), 10*2**30)
|
|
self.fill_image(self.fullpath("appvms/test-standalonevm/root.img"),
|
|
100*1024*1024, True)
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm/apps.templates"))
|
|
self.create_appmenus(self.fullpath("appvms/test-standalonevm/apps"
|
|
".templates"),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
os.mkdir(self.fullpath("appvms/test-standalonevm/kernels"))
|
|
for k_file in ["initramfs", "vmlinuz", "modules.img"]:
|
|
self.fill_image(self.fullpath("appvms/test-standalonevm/kernels/"
|
|
+ k_file), 10*1024*1024)
|
|
|
|
# VM based on custom template
|
|
subprocess.check_call(
|
|
["/bin/cp", "-a", self.fullpath("appvms/test-work"),
|
|
self.fullpath("appvms/test-custom-template-appvm")])
|
|
|
|
# HVM
|
|
if r2b2:
|
|
subprocess.check_call(
|
|
["/bin/cp", "-a", self.fullpath("appvms/test-standalonevm"),
|
|
self.fullpath("appvms/test-testhvm")])
|
|
|
|
# ProxyVM
|
|
os.mkdir(self.fullpath("servicevms/test-testproxy"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"servicevms/test-testproxy/whitelisted-appmenus.list"))
|
|
self.create_private_img(
|
|
self.fullpath("servicevms/test-testproxy/private.img"))
|
|
|
|
# Custom template
|
|
os.mkdir(self.fullpath("vm-templates/test-template-clone"))
|
|
self.create_private_img(
|
|
self.fullpath("vm-templates/test-template-clone/private.img"))
|
|
self.create_sparse(self.fullpath(
|
|
"vm-templates/test-template-clone/root-cow.img"), 10*2**30)
|
|
self.create_sparse(self.fullpath(
|
|
"vm-templates/test-template-clone/root.img"), 10*2**30)
|
|
self.fill_image(self.fullpath(
|
|
"vm-templates/test-template-clone/root.img"), 100*2**20, True)
|
|
self.create_volatile_img(self.fullpath(
|
|
"vm-templates/test-template-clone/volatile.img"))
|
|
subprocess.check_call([
|
|
"/bin/tar", "cS",
|
|
"-f", self.fullpath(
|
|
"vm-templates/test-template-clone/clean-volatile.img.tar"),
|
|
"-C", self.fullpath("vm-templates/test-template-clone"),
|
|
"volatile.img"])
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/whitelisted-appmenus.list"))
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/vm-whitelisted-appmenus.list"))
|
|
if r2b2:
|
|
self.create_whitelisted_appmenus(self.fullpath(
|
|
"vm-templates/test-template-clone/netvm-whitelisted-appmenus"
|
|
".list"))
|
|
os.symlink("/usr/share/qubes/icons/green.png",
|
|
self.fullpath("vm-templates/test-template-clone/icon.png"))
|
|
os.mkdir(
|
|
self.fullpath("vm-templates/test-template-clone/apps.templates"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-template-clone/apps.templates"),
|
|
APPTEMPLATE_R2B2,
|
|
appmenus_list)
|
|
os.mkdir(self.fullpath("vm-templates/test-template-clone/apps"))
|
|
self.create_appmenus(
|
|
self.fullpath("vm-templates/test-template-clone/apps"),
|
|
APPTEMPLATE_R2B2.replace("%VMNAME%", "test-template-clone")
|
|
.replace("%VMDIR%", self.fullpath(
|
|
"vm-templates/test-template-clone")),
|
|
appmenus_list)
|
|
|
|
def calculate_hmac(self, f_name, algorithm="sha512", password="qubes"):
|
|
subprocess.check_call(["openssl", "dgst", "-"+algorithm, "-hmac",
|
|
password],
|
|
stdin=open(self.fullpath(f_name), "r"),
|
|
stdout=open(self.fullpath(f_name+".hmac"), "w"))
|
|
|
|
def append_backup_stream(self, f_name, stream, basedir=None):
|
|
if not basedir:
|
|
basedir = self.backupdir
|
|
subprocess.check_call(["tar", "-cO", "--posix", "-C", basedir,
|
|
f_name],
|
|
stdout=stream)
|
|
|
|
def handle_v3_file(self, f_name, subdir, stream, compressed=True,
|
|
encrypted=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)
|
|
if encrypted:
|
|
encryptor = subprocess.Popen(
|
|
["openssl", "enc", "-e", "-aes-256-cbc", "-pass", "pass:qubes"],
|
|
stdin=tar.stdout,
|
|
stdout=subprocess.PIPE)
|
|
data = encryptor.stdout
|
|
else:
|
|
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)
|
|
|
|
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)
|
|
self.calculate_hmac(os.path.join("stage1", part_with_dir))
|
|
self.append_backup_stream(part_with_dir, stream,
|
|
basedir=self.fullpath("stage1"))
|
|
self.append_backup_stream(part_with_dir+".hmac", 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
|
|
|
|
:param encrypt: Should the backup be encrypted
|
|
:return:
|
|
"""
|
|
output = open(self.fullpath("backup.bin"), "w")
|
|
f = open(self.fullpath("backup-header"), "w")
|
|
f.write(BACKUP_HEADER_R2.format(
|
|
encrypted=str(encrypted),
|
|
compressed=str(compressed)
|
|
))
|
|
f.close()
|
|
self.calculate_hmac("backup-header")
|
|
self.append_backup_stream("backup-header", output)
|
|
self.append_backup_stream("backup-header.hmac", output)
|
|
f = open(self.fullpath("qubes.xml"), "w")
|
|
if encrypted:
|
|
qubesxml = QUBESXML_R2
|
|
for vmname, subdir in MANGLED_SUBDIRS_R2.items():
|
|
qubesxml = re.sub(r"[a-z-]*/{}".format(vmname),
|
|
subdir, qubesxml)
|
|
f.write(qubesxml)
|
|
else:
|
|
f.write(QUBESXML_R2)
|
|
f.close()
|
|
|
|
self.handle_v3_file("qubes.xml", "", output, encrypted=encrypted,
|
|
compressed=compressed)
|
|
|
|
self.create_v1_files(r2b2=True)
|
|
for vm_type in ["appvms", "servicevms"]:
|
|
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)):
|
|
if encrypted:
|
|
subdir = MANGLED_SUBDIRS_R2[vm_name]
|
|
else:
|
|
subdir = vm_dir
|
|
self.handle_v3_file(
|
|
os.path.join(vm_dir, f_name),
|
|
subdir+'/', output, encrypted=encrypted)
|
|
|
|
for vm_name in os.listdir(self.fullpath("vm-templates")):
|
|
vm_dir = os.path.join("vm-templates", vm_name)
|
|
if encrypted:
|
|
subdir = MANGLED_SUBDIRS_R2[vm_name]
|
|
else:
|
|
subdir = vm_dir
|
|
self.handle_v3_file(
|
|
os.path.join(vm_dir, "."),
|
|
subdir+'/', output, encrypted=encrypted)
|
|
|
|
output.close()
|
|
|
|
def assertRestored(self, name, **kwargs):
|
|
# ignore automatically added features
|
|
ignore_features = ['servicevm']
|
|
|
|
with self.assertNotRaises((KeyError, qubes.exc.QubesException)):
|
|
vm = self.app.domains[name]
|
|
asyncio.get_event_loop().run_until_complete(vm.storage.verify())
|
|
for prop, value in kwargs.items():
|
|
if prop == 'klass':
|
|
self.assertIsInstance(vm, value)
|
|
elif prop == 'features':
|
|
self.assertEqual(
|
|
dict((k, v) for k, v in vm.features.items()
|
|
if k not in ignore_features),
|
|
value,
|
|
'VM {} - features mismatch'.format(vm.name))
|
|
elif value is qubes.property.DEFAULT:
|
|
self.assertTrue(vm.property_is_default(prop),
|
|
'VM {} - property {} not default'.format(vm.name, prop))
|
|
else:
|
|
actual_value = getattr(vm, prop)
|
|
if isinstance(actual_value, qubes.vm.BaseVM):
|
|
self.assertEqual(value, actual_value.name,
|
|
'VM {} - property {}'.format(vm.name, prop))
|
|
elif isinstance(actual_value, qubes.Label):
|
|
self.assertEqual(value, actual_value.name,
|
|
'VM {} - property {}'.format(vm.name, prop))
|
|
else:
|
|
self.assertEqual(value, actual_value,
|
|
'VM {} - property {}'.format(vm.name, prop))
|
|
|
|
def test_210_r2(self):
|
|
self.create_v3_backup(False)
|
|
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
common_props = {
|
|
'installed_by_rpm': False,
|
|
'kernel': qubes.property.DEFAULT,
|
|
'kernelopts': qubes.property.DEFAULT,
|
|
'qrexec_timeout': qubes.property.DEFAULT,
|
|
'netvm': qubes.property.DEFAULT,
|
|
'default_user': qubes.property.DEFAULT,
|
|
'include_in_backups': True,
|
|
'debug': False,
|
|
'maxmem': min(int(self.app.host.memory_total / 1024 / 2), 4000),
|
|
'memory': 400,
|
|
'features': {},
|
|
}
|
|
template_standalone_props = common_props.copy()
|
|
template_standalone_props['features'] = {
|
|
'qrexec': '1',
|
|
'gui': '1',
|
|
}
|
|
self.assertRestored("test-template-clone",
|
|
klass=qubes.vm.templatevm.TemplateVM,
|
|
label='green',
|
|
provides_network=False,
|
|
**template_standalone_props)
|
|
testproxy_props = common_props.copy()
|
|
testproxy_props.update(
|
|
label='red',
|
|
provides_network=True,
|
|
memory=200,
|
|
template=self.app.default_template.name,
|
|
)
|
|
self.assertRestored("test-testproxy",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
**testproxy_props)
|
|
self.assertRestored("test-work",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
template=self.app.default_template.name,
|
|
label='green',
|
|
**common_props)
|
|
self.assertRestored("test-standalonevm",
|
|
klass=qubes.vm.standalonevm.StandaloneVM,
|
|
label='blue',
|
|
**template_standalone_props)
|
|
self.assertRestored("test-custom-template-appvm",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
template='test-template-clone',
|
|
label='yellow',
|
|
**common_props)
|
|
|
|
def test_220_r2_encrypted(self):
|
|
self.create_v3_backup(True)
|
|
|
|
self.restore_backup(self.fullpath("backup.bin"), options={
|
|
'use-default-template': True,
|
|
'use-default-netvm': True,
|
|
})
|
|
common_props = {
|
|
'installed_by_rpm': False,
|
|
'kernel': qubes.property.DEFAULT,
|
|
'kernelopts': qubes.property.DEFAULT,
|
|
'qrexec_timeout': qubes.property.DEFAULT,
|
|
'netvm': qubes.property.DEFAULT,
|
|
'default_user': qubes.property.DEFAULT,
|
|
'include_in_backups': True,
|
|
'debug': False,
|
|
'maxmem': min(int(self.app.host.memory_total / 1024 / 2), 4000),
|
|
'memory': 400,
|
|
'features': {},
|
|
}
|
|
template_standalone_props = common_props.copy()
|
|
template_standalone_props['features'] = {
|
|
'qrexec': '1',
|
|
'gui': '1',
|
|
}
|
|
self.assertRestored("test-template-clone",
|
|
klass=qubes.vm.templatevm.TemplateVM,
|
|
label='green',
|
|
provides_network=False,
|
|
**template_standalone_props)
|
|
testproxy_props = common_props.copy()
|
|
testproxy_props.update(
|
|
label='red',
|
|
provides_network=True,
|
|
memory=200,
|
|
template=self.app.default_template.name,
|
|
)
|
|
self.assertRestored("test-testproxy",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
**testproxy_props)
|
|
self.assertRestored("test-work",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
template=self.app.default_template.name,
|
|
label='green',
|
|
**common_props)
|
|
self.assertRestored("test-standalonevm",
|
|
klass=qubes.vm.standalonevm.StandaloneVM,
|
|
label='blue',
|
|
**template_standalone_props)
|
|
self.assertRestored("test-custom-template-appvm",
|
|
klass=qubes.vm.appvm.AppVM,
|
|
template='test-template-clone',
|
|
label='yellow',
|
|
**common_props)
|
|
|
|
|
|
class TC_01_BackupCompatibilityIntoLVM(TC_00_BackupCompatibility):
|
|
def setUp(self):
|
|
super(TC_01_BackupCompatibilityIntoLVM, self).setUp()
|
|
self.init_lvm_pool()
|
|
|
|
def restore_backup(self, source=None, appvm=None, options=None,
|
|
**kwargs):
|
|
if options is None:
|
|
options = {}
|
|
options['override_pool'] = self.pool.name
|
|
super(TC_01_BackupCompatibilityIntoLVM, self).restore_backup(source,
|
|
appvm, options, **kwargs)
|