backupcompatibility.py 75 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813
  1. # -*- encoding: utf8 -*-
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU Lesser General Public License
  9. # as published by the Free Software Foundation; either version 2.1
  10. # of the License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Lesser General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Lesser General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. #
  21. import functools
  22. import shutil
  23. import tempfile
  24. import unittest
  25. from distutils import spawn
  26. from multiprocessing import Queue
  27. import os
  28. import subprocess
  29. import logging
  30. import time
  31. try:
  32. import unittest.mock as mock
  33. except ImportError:
  34. import mock
  35. import re
  36. import multiprocessing
  37. import pkg_resources
  38. import sys
  39. import qubesadmin.backup.core2
  40. import qubesadmin.backup.core3
  41. import qubesadmin.firewall
  42. import qubesadmin.storage
  43. import qubesadmin.tests
  44. import qubesadmin.tests.backup
  45. MANGLED_SUBDIRS_R2 = {
  46. "test-work": "vm5",
  47. "test-template-clone": "vm9",
  48. "test-custom-template-appvm": "vm10",
  49. "test-standalonevm": "vm11",
  50. "test-testproxy": "vm12",
  51. "test-testhvm": "vm14",
  52. "test-net": "vm16",
  53. }
  54. MANGLED_SUBDIRS_R4 = {
  55. "test-work": "vm3",
  56. "test-fedora-25-clone": "vm7",
  57. "test-custom-template-appvm": "vm31",
  58. "test-standalonevm": "vm4",
  59. "test-proxy": "vm30",
  60. "test-hvm": "vm9",
  61. "test-net": "vm6",
  62. "test-d8test": "vm20",
  63. }
  64. APPTEMPLATE_R2B2 = '''
  65. [Desktop Entry]
  66. Name=%VMNAME%: {name}
  67. GenericName=%VMNAME%: {name}
  68. GenericName[ca]=%VMNAME%: Navegador web
  69. GenericName[cs]=%VMNAME%: Webový prohlížeč
  70. GenericName[es]=%VMNAME%: Navegador web
  71. GenericName[fa]=%VMNAME%: مرورر اینترنتی
  72. GenericName[fi]=%VMNAME%: WWW-selain
  73. GenericName[fr]=%VMNAME%: Navigateur Web
  74. GenericName[hu]=%VMNAME%: Webböngésző
  75. GenericName[it]=%VMNAME%: Browser Web
  76. GenericName[ja]=%VMNAME%: ウェブ・ブラウザ
  77. GenericName[ko]=%VMNAME%: 웹 브라우저
  78. GenericName[nb]=%VMNAME%: Nettleser
  79. GenericName[nl]=%VMNAME%: Webbrowser
  80. GenericName[nn]=%VMNAME%: Nettlesar
  81. GenericName[no]=%VMNAME%: Nettleser
  82. GenericName[pl]=%VMNAME%: Przeglądarka WWW
  83. GenericName[pt]=%VMNAME%: Navegador Web
  84. GenericName[pt_BR]=%VMNAME%: Navegador Web
  85. GenericName[sk]=%VMNAME%: Internetový prehliadač
  86. GenericName[sv]=%VMNAME%: Webbläsare
  87. Comment={comment}
  88. Comment[ca]=Navegueu per el web
  89. Comment[cs]=Prohlížení stránek World Wide Webu
  90. Comment[de]=Im Internet surfen
  91. Comment[es]=Navegue por la web
  92. Comment[fa]=صفحات شبه جهانی اینترنت را مرور نمایید
  93. Comment[fi]=Selaa Internetin WWW-sivuja
  94. Comment[fr]=Navigue sur Internet
  95. Comment[hu]=A világháló böngészése
  96. Comment[it]=Esplora il web
  97. Comment[ja]=ウェブを閲覧します
  98. Comment[ko]=웹을 돌아 다닙니다
  99. Comment[nb]=Surf på nettet
  100. Comment[nl]=Verken het internet
  101. Comment[nn]=Surf på nettet
  102. Comment[no]=Surf på nettet
  103. Comment[pl]=Przeglądanie stron WWW
  104. Comment[pt]=Navegue na Internet
  105. Comment[pt_BR]=Navegue na Internet
  106. Comment[sk]=Prehliadanie internetu
  107. Comment[sv]=Surfa på webben
  108. Exec=qvm-run -q --tray -a %VMNAME% '{command} %u'
  109. Categories=Network;WebBrowser;
  110. X-Qubes-VmName=%VMNAME%
  111. Icon=%VMDIR%/icon.png
  112. '''
  113. QUBESXML_R1 = '''<?xml version='1.0' encoding='UTF-8'?>
  114. <QubesVmCollection clockvm="2" default_fw_netvm="2" default_kernel="3.2.7-10" default_netvm="3" default_template="1" updatevm="3"><QubesTemplateVm conf_file="fedora-17-x64.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/vm-templates/fedora-17-x64" include_in_backups="True" installed_by_rpm="True" internal="False" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="fedora-17-x64" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="1" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="none" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesNetVm conf_file="netvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/netvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="iommu=soft swiotlb=2048" label="red" maxmem="4063" memory="200" name="netvm" netid="1" pcidevs="[&apos;00:19.0&apos;, &apos;03:00.0&apos;]" private_img="private.img" qid="2" root_img="root.img" services="{&apos;ntpd&apos;: False, &apos;meminfo-writer&apos;: False}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /><QubesProxyVm conf_file="firewallvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/firewallvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="200" name="firewallvm" netid="2" netvm_qid="2" pcidevs="[]" private_img="private.img" qid="3" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="fedora-17-x64-dvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/fedora-17-x64-dvm" include_in_backups="True" installed_by_rpm="False" internal="True" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="fedora-17-x64-dvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="4" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="1" volatile_img="volatile.img" /><QubesAppVm conf_file="test-work.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-work" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="400" name="test-work" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="5" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="personal.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/personal" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="400" name="personal" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="6" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="banking.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/banking" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="green" maxmem="4063" memory="400" name="banking" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="7" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="untrusted.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/untrusted" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="red" maxmem="4063" memory="400" name="untrusted" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="8" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-standalonevm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-standalonevm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="None" kernelopts="" label="red" maxmem="4063" memory="400" name="test-standalonevm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="9" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="none" uses_default_kernel="False" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-testvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-testvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="red" mac="00:16:3E:5E:6C:55" maxmem="4063" memory="400" name="test-testvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="10" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesTemplateVm conf_file="test-template-clone.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/vm-templates/test-template-clone" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="gray" maxmem="4063" memory="400" name="test-template-clone" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="11" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="none" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesAppVm conf_file="test-custom-template-appvm.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/appvms/test-custom-template-appvm" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="400" name="test-custom-template-appvm" netvm_qid="3" pcidevs="[]" private_img="private.img" qid="12" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="11" uses_default_kernel="True" uses_default_kernelopts="True" uses_default_netvm="True" vcpus="2" volatile_img="volatile.img" /><QubesProxyVm conf_file="test-testproxy.conf" debug="False" default_user="user" dir_path="/var/lib/qubes/servicevms/test-testproxy" include_in_backups="True" installed_by_rpm="False" internal="False" kernel="3.2.7-10" kernelopts="" label="yellow" maxmem="4063" memory="200" name="test-testproxy" netid="3" netvm_qid="2" pcidevs="[]" private_img="private.img" qid="13" root_img="root.img" services="{&apos;meminfo-writer&apos;: True}" template_qid="1" uses_default_kernel="True" uses_default_kernelopts="True" vcpus="2" volatile_img="volatile.img" /></QubesVmCollection>
  115. '''
  116. BACKUP_HEADER_R2 = '''version=3
  117. hmac-algorithm=SHA512
  118. crypto-algorithm=aes-256-cbc
  119. encrypted={encrypted}
  120. compressed={compressed}
  121. compression-filter=gzip
  122. '''
  123. BACKUP_HEADER_R4 = '''version=4
  124. hmac-algorithm=scrypt
  125. encrypted=True
  126. compressed={compressed}
  127. compression-filter=gzip
  128. backup-id=20161020T123455-1234
  129. '''
  130. parsed_qubes_xml_r2 = {
  131. 'domains': {
  132. 'dom0': {
  133. 'klass': 'AdminVM',
  134. 'label': 'black',
  135. 'properties': {},
  136. 'devices': {},
  137. 'tags': set(),
  138. 'features': {},
  139. 'template': None,
  140. 'backup_path': 'dom0-home/user',
  141. 'included_in_backup': True,
  142. },
  143. 'fedora-20-x64': {
  144. 'klass': 'TemplateVM',
  145. 'label': 'black',
  146. 'properties': {
  147. 'maxmem': '1535',
  148. },
  149. 'devices': {},
  150. 'tags': set(),
  151. 'features': {
  152. 'service.meminfo-writer': True,
  153. 'qrexec': True,
  154. 'gui': True,
  155. },
  156. 'template': None,
  157. 'backup_path': None,
  158. 'included_in_backup': False,
  159. },
  160. 'netvm': {
  161. 'klass': 'AppVM',
  162. 'label': 'red',
  163. 'properties': {
  164. 'maxmem': '1535',
  165. 'memory': '200',
  166. 'netvm': None,
  167. 'default_dispvm': 'disp-no-netvm',
  168. 'provides_network': True},
  169. 'devices': {
  170. 'pci': {
  171. ('dom0', '02_00.0'): {},
  172. ('dom0', '03_00.0'): {},
  173. }
  174. },
  175. 'tags': set(),
  176. 'features': {
  177. 'service.ntpd': False,
  178. 'service.meminfo-writer': False
  179. },
  180. 'template': 'fedora-20-x64',
  181. 'backup_path': None,
  182. 'included_in_backup': False,
  183. },
  184. 'firewallvm': {
  185. 'klass': 'AppVM',
  186. 'label': 'green',
  187. 'properties': {
  188. 'maxmem': '1535',
  189. 'memory': '200',
  190. 'provides_network': True
  191. },
  192. 'devices': {},
  193. 'tags': set(),
  194. 'features': {'service.meminfo-writer': True},
  195. 'template': 'fedora-20-x64',
  196. 'backup_path': None,
  197. 'included_in_backup': False,
  198. },
  199. 'fedora-20-x64-dvm': {
  200. 'klass': 'AppVM',
  201. 'label': 'gray',
  202. 'properties': {
  203. 'maxmem': '1535',
  204. 'vcpus': '1'
  205. },
  206. 'devices': {},
  207. 'tags': set(),
  208. 'features': {
  209. 'internal': True, 'service.meminfo-writer': True},
  210. 'template': 'fedora-20-x64',
  211. 'backup_path': None,
  212. 'included_in_backup': False,
  213. },
  214. 'banking': {
  215. 'klass': 'AppVM',
  216. 'label': 'green',
  217. 'properties': {'maxmem': '1535'},
  218. 'devices': {},
  219. 'tags': set(),
  220. 'features': {'service.meminfo-writer': True},
  221. 'template': 'fedora-20-x64',
  222. 'backup_path': None,
  223. 'included_in_backup': False,
  224. },
  225. 'personal': {
  226. 'klass': 'AppVM',
  227. 'label': 'yellow',
  228. 'properties': {'maxmem': '1535'},
  229. 'devices': {},
  230. 'tags': set(),
  231. 'features': {'service.meminfo-writer': True},
  232. 'template': 'fedora-20-x64',
  233. 'backup_path': None,
  234. 'included_in_backup': False,
  235. },
  236. 'untrusted': {
  237. 'klass': 'AppVM',
  238. 'label': 'red',
  239. 'properties': {
  240. 'maxmem': '1535',
  241. 'netvm': 'test-testproxy',
  242. 'default_dispvm': 'disp-test-testproxy',
  243. },
  244. 'devices': {},
  245. 'tags': set(),
  246. 'features': {'service.meminfo-writer': True},
  247. 'template': 'fedora-20-x64',
  248. 'backup_path': None,
  249. 'included_in_backup': False,
  250. },
  251. 'testproxy2': {
  252. 'klass': 'AppVM',
  253. 'label': 'red',
  254. 'properties': {
  255. 'maxmem': '1535',
  256. 'memory': '200',
  257. 'provides_network': True},
  258. 'devices': {},
  259. 'tags': set(),
  260. 'features': {'service.meminfo-writer': True},
  261. 'template': 'test-template-clone',
  262. 'backup_path': None,
  263. 'included_in_backup': False,
  264. },
  265. 'test-testproxy': {
  266. 'klass': 'AppVM',
  267. 'label': 'red',
  268. 'properties': {
  269. 'maxmem': '1535',
  270. 'memory': '200',
  271. 'provides_network': True},
  272. 'devices': {},
  273. 'tags': set(),
  274. 'features': {'service.meminfo-writer': True},
  275. 'template': 'fedora-20-x64',
  276. 'backup_path': 'servicevms/test-testproxy',
  277. 'included_in_backup': True,
  278. },
  279. 'test-testhvm': {
  280. 'klass': 'StandaloneVM',
  281. 'label': 'purple',
  282. 'properties': {'kernel': '', 'virt_mode': 'hvm', 'memory': '512'},
  283. 'devices': {},
  284. 'tags': set(),
  285. 'features': {
  286. 'service.meminfo-writer': False,
  287. 'linux-stubdom': False},
  288. 'template': None,
  289. 'backup_path': 'appvms/test-testhvm',
  290. 'included_in_backup': True,
  291. 'root_size': 2097664,
  292. },
  293. 'test-work': {
  294. 'klass': 'AppVM',
  295. 'label': 'green',
  296. 'properties': {'maxmem': '1535'},
  297. 'devices': {},
  298. 'tags': set(),
  299. 'features': {'service.meminfo-writer': True},
  300. 'template': 'fedora-20-x64',
  301. 'backup_path': 'appvms/test-work',
  302. 'included_in_backup': True,
  303. },
  304. 'test-template-clone': {
  305. 'klass': 'TemplateVM',
  306. 'label': 'green',
  307. 'properties': {'maxmem': '1535'},
  308. 'devices': {},
  309. 'tags': set(),
  310. 'features': {
  311. 'service.meminfo-writer': True,
  312. 'qrexec': True,
  313. 'gui': True,
  314. },
  315. 'template': None,
  316. 'backup_path': 'vm-templates/test-template-clone',
  317. 'included_in_backup': True,
  318. 'root_size': 209715712,
  319. },
  320. 'test-custom-template-appvm': {
  321. 'klass': 'AppVM',
  322. 'label': 'yellow',
  323. 'properties': {'maxmem': '1535'},
  324. 'devices': {},
  325. 'tags': set(),
  326. 'features': {'service.meminfo-writer': True},
  327. 'template': 'test-template-clone',
  328. 'backup_path': 'appvms/test-custom-template-appvm',
  329. 'included_in_backup': True,
  330. },
  331. 'test-standalonevm': {
  332. 'klass': 'StandaloneVM',
  333. 'label': 'blue',
  334. 'properties': {'maxmem': '1535'},
  335. 'devices': {},
  336. 'tags': set(),
  337. 'features': {
  338. 'service.meminfo-writer': True,
  339. 'qrexec': True,
  340. 'gui': True,
  341. },
  342. 'template': None,
  343. 'backup_path': 'appvms/test-standalonevm',
  344. 'included_in_backup': True,
  345. 'root_size': 2097664,
  346. },
  347. 'test-net': {
  348. 'klass': 'AppVM',
  349. 'label': 'red',
  350. 'properties': {
  351. 'maxmem': '1535',
  352. 'memory': '200',
  353. 'netvm': None,
  354. 'default_dispvm': 'disp-no-netvm',
  355. 'provides_network': True},
  356. 'devices': {
  357. 'pci': {
  358. ('dom0', '02_00.0'): {},
  359. ('dom0', '03_00.0'): {},
  360. }
  361. },
  362. 'tags': set(),
  363. 'features': {
  364. 'service.ntpd': False,
  365. 'service.meminfo-writer': False
  366. },
  367. 'template': 'fedora-20-x64',
  368. 'backup_path': 'servicevms/test-net',
  369. 'included_in_backup': True,
  370. },
  371. 'disp-no-netvm': {
  372. 'klass': 'AppVM',
  373. 'label': 'red',
  374. 'properties': {
  375. 'netvm': None,
  376. 'template_for_dispvms': True,
  377. },
  378. 'devices': {},
  379. 'features': {},
  380. 'tags': set(),
  381. 'template': None, # default
  382. 'backup_path': None,
  383. 'included_in_backup': True,
  384. },
  385. 'disp-test-testproxy': {
  386. 'klass': 'AppVM',
  387. 'label': 'red',
  388. 'properties': {
  389. 'netvm': 'test-testproxy',
  390. 'template_for_dispvms': True,
  391. },
  392. 'devices': {},
  393. 'features': {},
  394. 'tags': set(),
  395. 'template': None, # default
  396. 'backup_path': None,
  397. 'included_in_backup': True,
  398. },
  399. },
  400. 'globals': {
  401. 'default_template': 'fedora-20-x64',
  402. 'default_kernel': '3.7.6-2',
  403. 'default_netvm': 'firewallvm',
  404. 'clockvm': 'netvm',
  405. 'updatevm': 'firewallvm'
  406. },
  407. }
  408. parsed_qubes_xml_v4 = {
  409. 'domains': {
  410. 'dom0': {
  411. 'klass': 'AdminVM',
  412. 'label': 'black',
  413. 'properties': {},
  414. 'devices': {},
  415. 'tags': set(),
  416. 'features': {},
  417. 'template': None,
  418. 'backup_path': 'dom0-home/user',
  419. 'included_in_backup': True,
  420. },
  421. 'fedora-25': {
  422. 'klass': 'TemplateVM',
  423. 'label': 'black',
  424. 'properties': {},
  425. 'devices': {},
  426. 'tags': {'created-by-test-work'},
  427. 'features': {
  428. 'gui': '1',
  429. 'qrexec': 'True',
  430. 'updates-available': False
  431. },
  432. 'template': None,
  433. 'backup_path': None,
  434. 'included_in_backup': False,
  435. },
  436. 'fedora-25-lvm': {
  437. 'klass': 'TemplateVM',
  438. 'label': 'black',
  439. 'properties': {
  440. 'maxmem': '4000',
  441. },
  442. 'devices': {},
  443. 'tags': set(),
  444. 'features': {},
  445. 'template': None,
  446. 'backup_path': None,
  447. 'included_in_backup': False,
  448. },
  449. 'debian-8': {
  450. 'klass': 'TemplateVM',
  451. 'label': 'black',
  452. 'properties': {},
  453. 'devices': {},
  454. 'tags': {'created-by-dom0'},
  455. 'features': {
  456. 'gui': '1',
  457. 'qrexec': 'True',
  458. 'updates-available': False},
  459. 'template': None,
  460. 'backup_path': None,
  461. 'included_in_backup': False,
  462. },
  463. 'sys-net': {
  464. 'klass': 'AppVM',
  465. 'label': 'red',
  466. 'properties': {
  467. 'virt_mode': 'hvm',
  468. 'kernelopts': 'nopat i8042.nokbd i8042.noaux',
  469. 'maxmem': '300',
  470. 'memory': '300',
  471. 'netvm': None,
  472. 'default_user': 'user',
  473. 'provides_network': 'True'},
  474. 'devices': {
  475. 'pci': {
  476. ('dom0', '02_00.0'): {},
  477. }
  478. },
  479. 'tags': set(),
  480. 'features': {
  481. 'service.clocksync': '1',
  482. 'service.meminfo-writer': False
  483. },
  484. 'template': 'fedora-25',
  485. 'backup_path': None,
  486. 'included_in_backup': False,
  487. },
  488. 'sys-firewall': {
  489. 'klass': 'AppVM',
  490. 'label': 'green',
  491. 'properties': {
  492. 'autostart': 'True',
  493. 'memory': '500',
  494. 'provides_network': 'True'
  495. },
  496. 'devices': {},
  497. 'tags': set(),
  498. 'features': {},
  499. 'template': 'fedora-25',
  500. 'backup_path': None,
  501. 'included_in_backup': False,
  502. },
  503. 'test-d8test': {
  504. 'klass': 'AppVM',
  505. 'label': 'gray',
  506. 'properties': {'debug': 'True', 'kernel': None},
  507. 'devices': {},
  508. 'tags': {'created-by-dom0'},
  509. 'features': {},
  510. 'template': 'debian-8',
  511. 'backup_path': 'appvms/test-d8test',
  512. 'included_in_backup': True,
  513. },
  514. 'fedora-25-dvm': {
  515. 'klass': 'AppVM',
  516. 'label': 'red',
  517. 'properties': {
  518. 'template_for_dispvms': 'True',
  519. 'vcpus': '1',
  520. },
  521. 'devices': {},
  522. 'tags': set(),
  523. 'features': {
  524. 'internal': '1', 'service.meminfo-writer': '1'},
  525. 'template': 'fedora-25',
  526. 'backup_path': None,
  527. 'included_in_backup': False,
  528. },
  529. 'fedora-25-clone-dvm': {
  530. 'klass': 'AppVM',
  531. 'label': 'red',
  532. 'properties': {
  533. 'vcpus': '1',
  534. 'template_for_dispvms': 'True',
  535. },
  536. 'devices': {},
  537. 'tags': set(),
  538. 'features': {
  539. 'internal': '1', 'service.meminfo-writer': '1'},
  540. 'template': 'test-fedora-25-clone',
  541. 'backup_path': None,
  542. 'included_in_backup': False,
  543. },
  544. 'vault': {
  545. 'klass': 'AppVM',
  546. 'label': 'black',
  547. 'properties': {'virt_mode': 'pv', 'maxmem': '1536', 'netvm': None},
  548. 'devices': {},
  549. 'tags': set(),
  550. 'features': {},
  551. 'template': 'fedora-25',
  552. 'backup_path': None,
  553. 'included_in_backup': False,
  554. },
  555. 'personal': {
  556. 'klass': 'AppVM',
  557. 'label': 'yellow',
  558. 'properties': {'netvm': 'sys-firewall'},
  559. 'devices': {},
  560. 'tags': set(),
  561. 'features': {
  562. 'feat1': '1',
  563. 'feat2': False,
  564. 'feat32': '1',
  565. 'featdis': False,
  566. 'xxx': '1'
  567. },
  568. 'template': 'fedora-25',
  569. 'backup_path': None,
  570. 'included_in_backup': False,
  571. },
  572. 'untrusted': {
  573. 'klass': 'AppVM',
  574. 'label': 'red',
  575. 'properties': {
  576. 'netvm': None,
  577. 'backup_timestamp': '1474318497',
  578. 'default_dispvm': 'fedora-25-clone-dvm',
  579. },
  580. 'devices': {},
  581. 'tags': set(),
  582. 'features': {'service.meminfo-writer': '1'},
  583. 'template': 'fedora-25',
  584. 'backup_path': None,
  585. 'included_in_backup': False,
  586. },
  587. 'sys-usb': {
  588. 'klass': 'AppVM',
  589. 'label': 'red',
  590. 'properties': {
  591. 'autostart': 'True',
  592. 'maxmem': '400',
  593. 'provides_network': 'True',
  594. },
  595. 'devices': {},
  596. 'tags': set(),
  597. 'features': {
  598. 'service.meminfo-writer': False,
  599. 'service.network-manager': False,
  600. },
  601. 'template': 'fedora-25',
  602. 'backup_path': None,
  603. 'included_in_backup': False,
  604. },
  605. 'test-proxy': {
  606. 'klass': 'AppVM',
  607. 'label': 'red',
  608. 'properties': {'netvm': 'sys-net', 'provides_network': 'True'},
  609. 'devices': {},
  610. 'tags': {'created-by-dom0'},
  611. 'features': {},
  612. 'template': 'debian-8',
  613. 'backup_path': 'appvms/test-proxy',
  614. 'included_in_backup': True,
  615. },
  616. 'test-hvm': {
  617. 'klass': 'StandaloneVM',
  618. 'label': 'purple',
  619. 'properties': {
  620. 'kernel': None,
  621. 'virt_mode': 'hvm',
  622. 'maxmem': '4000'},
  623. 'devices': {},
  624. 'tags': set(),
  625. 'features': {'service.meminfo-writer': False},
  626. 'template': None,
  627. 'backup_path': 'appvms/test-hvm',
  628. 'included_in_backup': True,
  629. 'root_size': 2097664,
  630. },
  631. 'test-work': {
  632. 'klass': 'AppVM',
  633. 'label': 'green',
  634. 'properties': {
  635. 'ip': '192.168.0.1',
  636. 'maxmem': '4000',
  637. 'memory': '400'},
  638. 'devices': {},
  639. 'tags': {'tag1', 'tag2'},
  640. 'features': {'service.meminfo-writer': '1'},
  641. 'template': 'fedora-25',
  642. 'backup_path': 'appvms/test-work',
  643. 'included_in_backup': True,
  644. },
  645. 'test-fedora-25-clone': {
  646. 'klass': 'TemplateVM',
  647. 'label': 'black',
  648. 'properties': {'maxmem': '4000'},
  649. 'devices': {},
  650. 'tags': set(),
  651. 'features': {'service.meminfo-writer': '1'},
  652. 'template': None,
  653. 'backup_path': 'vm-templates/test-fedora-25-clone',
  654. 'included_in_backup': True,
  655. },
  656. 'test-custom-template-appvm': {
  657. 'klass': 'AppVM',
  658. 'label': 'yellow',
  659. 'properties': {'debug': 'True', 'kernel': None},
  660. 'devices': {},
  661. 'tags': {'created-by-dom0'},
  662. 'features': {},
  663. 'template': 'test-fedora-25-clone',
  664. 'backup_path': 'appvms/test-custom-template-appvm',
  665. 'included_in_backup': True,
  666. },
  667. 'test-standalonevm': {
  668. 'klass': 'StandaloneVM',
  669. 'label': 'blue',
  670. 'properties': {'maxmem': '4000'},
  671. 'devices': {},
  672. 'tags': set(),
  673. 'features': {},
  674. 'template': None,
  675. 'backup_path': 'appvms/test-standalonevm',
  676. 'included_in_backup': True,
  677. 'root_size': 2097664,
  678. },
  679. 'test-net': {
  680. 'klass': 'AppVM',
  681. 'label': 'red',
  682. 'properties': {
  683. 'maxmem': '300',
  684. 'memory': '300',
  685. 'netvm': None,
  686. 'provides_network': 'True'
  687. },
  688. 'devices': {
  689. 'pci': {
  690. ('dom0', '03_00.0'): {},
  691. }
  692. },
  693. 'tags': set(),
  694. 'features': {
  695. 'service.ntpd': False,
  696. 'service.meminfo-writer': False
  697. },
  698. 'template': 'fedora-25',
  699. 'backup_path': 'appvms/test-net',
  700. 'included_in_backup': True,
  701. },
  702. },
  703. 'globals': {
  704. 'default_template': 'fedora-25',
  705. 'default_kernel': '4.9.31-17',
  706. 'default_netvm': 'sys-firewall',
  707. 'default_dispvm': 'fedora-25-dvm',
  708. #'default_fw_netvm': 'sys-net',
  709. 'clockvm': 'sys-net',
  710. 'updatevm': 'sys-firewall'
  711. },
  712. }
  713. class TC_00_QubesXML(qubesadmin.tests.QubesTestCase):
  714. def assertCorrectlyConverted(self, backup_app, expected_data):
  715. self.assertCountEqual(backup_app.domains.keys(),
  716. expected_data['domains'].keys())
  717. for vm in expected_data['domains']:
  718. self.assertEqual(backup_app.domains[vm].name, vm)
  719. self.assertEqual(backup_app.domains[vm].properties,
  720. expected_data['domains'][vm]['properties'], vm)
  721. for devtype in expected_data['domains'][vm]['devices']:
  722. self.assertEqual(backup_app.domains[vm].devices[devtype],
  723. expected_data['domains'][vm]['devices'][devtype], vm)
  724. self.assertEqual(backup_app.domains[vm].tags,
  725. expected_data['domains'][vm]['tags'], vm)
  726. self.assertEqual(backup_app.domains[vm].features,
  727. expected_data['domains'][vm]['features'], vm)
  728. self.assertEqual(backup_app.domains[vm].template,
  729. expected_data['domains'][vm]['template'], vm)
  730. self.assertEqual(backup_app.domains[vm].klass,
  731. expected_data['domains'][vm]['klass'], vm)
  732. self.assertEqual(backup_app.domains[vm].label,
  733. expected_data['domains'][vm]['label'], vm)
  734. self.assertEqual(backup_app.domains[vm].backup_path,
  735. expected_data['domains'][vm]['backup_path'], vm)
  736. self.assertEqual(backup_app.domains[vm].included_in_backup,
  737. expected_data['domains'][vm]['included_in_backup'], vm)
  738. self.assertEqual(backup_app.globals, expected_data['globals'])
  739. def test_000_qubes_xml_r2(self):
  740. xml_data = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
  741. with tempfile.NamedTemporaryFile() as qubes_xml:
  742. qubes_xml.file.write(xml_data)
  743. backup_app = qubesadmin.backup.core2.Core2Qubes(qubes_xml.name)
  744. self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_r2)
  745. def test_010_qubes_xml_r4(self):
  746. self.maxDiff = None
  747. xml_data = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
  748. with tempfile.NamedTemporaryFile() as qubes_xml:
  749. qubes_xml.file.write(xml_data)
  750. backup_app = qubesadmin.backup.core3.Core3Qubes(qubes_xml.name)
  751. self.assertCorrectlyConverted(backup_app, parsed_qubes_xml_v4)
  752. # backup code use multiprocessing, synchronize with main process
  753. class AppProxy(object):
  754. def __init__(self, app, sync_queue):
  755. self._app = app
  756. self._sync_queue = sync_queue
  757. def qubesd_call(self, dest, method, arg=None, payload=None,
  758. payload_stream=None):
  759. if payload_stream:
  760. signature_bin = payload_stream.read(512)
  761. payload = signature_bin.split(b'\0', 1)[0]
  762. subprocess.call(['cat'], stdin=payload_stream,
  763. stdout=subprocess.DEVNULL)
  764. payload_stream.close()
  765. self._sync_queue.put((dest, method, arg, payload))
  766. return self._app.qubesd_call(dest, method, arg, payload)
  767. class MockVolume(qubesadmin.storage.Volume):
  768. def __init__(self, import_data_queue, *args, **kwargs):
  769. super(MockVolume, self).__init__(*args, **kwargs)
  770. self.app = AppProxy(self.app, import_data_queue)
  771. class MockFirewall(qubesadmin.firewall.Firewall):
  772. def __init__(self, import_data_queue, *args, **kwargs):
  773. super(MockFirewall, self).__init__(*args, **kwargs)
  774. self.vm.app = AppProxy(self.vm.app, import_data_queue)
  775. @unittest.skipUnless(os.environ.get('ENABLE_SLOW_TESTS', False),
  776. 'Set ENABLE_SLOW_TESTS=1 environment variable')
  777. class TC_10_BackupCompatibility(qubesadmin.tests.backup.BackupTestCase):
  778. storage_pool = None
  779. def tearDown(self):
  780. super(TC_10_BackupCompatibility, self).tearDown()
  781. def create_whitelisted_appmenus(self, filename):
  782. with open(filename, "w") as f:
  783. f.write("gnome-terminal.desktop\n")
  784. f.write("nautilus.desktop\n")
  785. f.write("firefox.desktop\n")
  786. f.write("mozilla-thunderbird.desktop\n")
  787. f.write("libreoffice-startcenter.desktop\n")
  788. def create_appmenus(self, dir, template, list):
  789. for name in list:
  790. with open(os.path.join(dir, name + ".desktop"), "w") as f:
  791. f.write(template.format(name=name, comment=name, command=name))
  792. def create_private_img(self, filename):
  793. signature = '/'.join(os.path.splitext(filename)[0].split('/')[-2:])
  794. self.create_sparse(filename, 2*2**20, signature=signature.encode())
  795. #subprocess.check_call(["/usr/sbin/mkfs.ext4", "-q", "-F", filename])
  796. def create_volatile_img(self, filename):
  797. self.create_sparse(filename, 11.5*2**20)
  798. # here used to be sfdisk call with "0,1024,S\n,10240,L\n" input,
  799. # but since sfdisk folks like to change command arguments in
  800. # incompatible way, have an partition table verbatim here
  801. ptable = (
  802. '\x00\x00\x00\x00\x00\x00\x00\x00\xab\x39\xd5\xd4\x00\x00\x20\x00'
  803. '\x00\x21\xaa\x82\x82\x28\x08\x00\x00\x00\x00\x00\x00\x20\xaa\x00'
  804. '\x82\x29\x15\x83\x9c\x79\x08\x00\x00\x20\x00\x00\x01\x40\x00\x00'
  805. '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  806. '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x55'
  807. )
  808. with open(filename, 'r+') as f:
  809. f.seek(0x1b0)
  810. f.write(ptable)
  811. # TODO: mkswap
  812. def fullpath(self, name):
  813. return os.path.join(self.backupdir, name)
  814. def create_v1_files(self, r2b2=False):
  815. appmenus_list = [
  816. "firefox", "gnome-terminal", "evince", "evolution",
  817. "mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
  818. "gedit", "gpk-update-viewer", "gpk-application"
  819. ]
  820. os.mkdir(self.fullpath("appvms"))
  821. os.mkdir(self.fullpath("servicevms"))
  822. os.mkdir(self.fullpath("vm-templates"))
  823. # normal AppVM, with firewall
  824. os.mkdir(self.fullpath("appvms/test-work"))
  825. self.create_whitelisted_appmenus(self.fullpath(
  826. "appvms/test-work/whitelisted-appmenus.list"))
  827. os.symlink("/usr/share/qubes/icons/green.png",
  828. self.fullpath("appvms/test-work/icon.png"))
  829. self.create_private_img(self.fullpath("appvms/test-work/private.img"))
  830. with open(self.fullpath("appvms/test-work/firewall.xml"), "wb") as \
  831. f_firewall:
  832. f_firewall.write(
  833. pkg_resources.resource_string(__name__, 'v3-firewall.xml'))
  834. # StandaloneVM
  835. os.mkdir(self.fullpath("appvms/test-standalonevm"))
  836. self.create_whitelisted_appmenus(self.fullpath(
  837. "appvms/test-standalonevm/whitelisted-appmenus.list"))
  838. os.symlink("/usr/share/qubes/icons/blue.png",
  839. self.fullpath("appvms/test-standalonevm/icon.png"))
  840. self.create_private_img(self.fullpath(
  841. "appvms/test-standalonevm/private.img"))
  842. self.create_sparse(
  843. self.fullpath("appvms/test-standalonevm/root.img"), 10*2**30)
  844. self.fill_image(self.fullpath("appvms/test-standalonevm/root.img"),
  845. 1024*1024, True,
  846. signature=b'test-standalonevm/root')
  847. os.mkdir(self.fullpath("appvms/test-standalonevm/apps.templates"))
  848. self.create_appmenus(self.fullpath("appvms/test-standalonevm/apps"
  849. ".templates"),
  850. APPTEMPLATE_R2B2,
  851. appmenus_list)
  852. os.mkdir(self.fullpath("appvms/test-standalonevm/kernels"))
  853. for k_file in ["initramfs", "vmlinuz", "modules.img"]:
  854. self.fill_image(self.fullpath("appvms/test-standalonevm/kernels/"
  855. + k_file), 1024*1024)
  856. # VM based on custom template
  857. subprocess.check_call(
  858. ["/bin/cp", "-a", self.fullpath("appvms/test-work"),
  859. self.fullpath("appvms/test-custom-template-appvm")])
  860. # override for correct signature
  861. self.create_private_img(
  862. self.fullpath("appvms/test-custom-template-appvm/private.img"))
  863. # HVM
  864. if r2b2:
  865. subprocess.check_call(
  866. ["/bin/cp", "-a", self.fullpath("appvms/test-standalonevm"),
  867. self.fullpath("appvms/test-testhvm")])
  868. # override for correct signature
  869. self.create_private_img(
  870. self.fullpath("appvms/test-testhvm/private.img"))
  871. self.fill_image(self.fullpath("appvms/test-testhvm/root.img"),
  872. 1024*1024, True,
  873. signature=b'test-testhvm/root')
  874. # ProxyVM
  875. os.mkdir(self.fullpath("servicevms/test-testproxy"))
  876. self.create_whitelisted_appmenus(self.fullpath(
  877. "servicevms/test-testproxy/whitelisted-appmenus.list"))
  878. self.create_private_img(
  879. self.fullpath("servicevms/test-testproxy/private.img"))
  880. # NetVM
  881. os.mkdir(self.fullpath("servicevms/test-net"))
  882. self.create_whitelisted_appmenus(self.fullpath(
  883. "servicevms/test-net/whitelisted-appmenus.list"))
  884. self.create_private_img(
  885. self.fullpath("servicevms/test-net/private.img"))
  886. # Custom template
  887. os.mkdir(self.fullpath("vm-templates/test-template-clone"))
  888. self.create_private_img(
  889. self.fullpath("vm-templates/test-template-clone/private.img"))
  890. self.create_sparse(self.fullpath(
  891. "vm-templates/test-template-clone/root-cow.img"), 10*2**20)
  892. self.create_sparse(self.fullpath(
  893. "vm-templates/test-template-clone/root.img"), 10*2**20)
  894. self.fill_image(self.fullpath(
  895. "vm-templates/test-template-clone/root.img"), 100*2**20, True,
  896. signature=b'test-template-clone/root')
  897. self.create_volatile_img(self.fullpath(
  898. "vm-templates/test-template-clone/volatile.img"))
  899. subprocess.check_call([
  900. "/bin/tar", "cS",
  901. "-f", self.fullpath(
  902. "vm-templates/test-template-clone/clean-volatile.img.tar"),
  903. "-C", self.fullpath("vm-templates/test-template-clone"),
  904. "volatile.img"])
  905. self.create_whitelisted_appmenus(self.fullpath(
  906. "vm-templates/test-template-clone/whitelisted-appmenus.list"))
  907. self.create_whitelisted_appmenus(self.fullpath(
  908. "vm-templates/test-template-clone/vm-whitelisted-appmenus.list"))
  909. if r2b2:
  910. self.create_whitelisted_appmenus(self.fullpath(
  911. "vm-templates/test-template-clone/netvm-whitelisted-appmenus"
  912. ".list"))
  913. os.symlink("/usr/share/qubes/icons/green.png",
  914. self.fullpath("vm-templates/test-template-clone/icon.png"))
  915. os.mkdir(
  916. self.fullpath("vm-templates/test-template-clone/apps.templates"))
  917. self.create_appmenus(
  918. self.fullpath("vm-templates/test-template-clone/apps.templates"),
  919. APPTEMPLATE_R2B2,
  920. appmenus_list)
  921. os.mkdir(self.fullpath("vm-templates/test-template-clone/apps"))
  922. self.create_appmenus(
  923. self.fullpath("vm-templates/test-template-clone/apps"),
  924. APPTEMPLATE_R2B2.replace("%VMNAME%", "test-template-clone")
  925. .replace("%VMDIR%", self.fullpath(
  926. "vm-templates/test-template-clone")),
  927. appmenus_list)
  928. self.create_dom0_files()
  929. dom0_dirs = ('Downloads', 'Pictures', 'Documents', '.config', '.local')
  930. dom0_files = ('.bash_history', 'some-file.txt',
  931. 'Pictures/another-file.png')
  932. def create_dom0_files(self):
  933. # dom0 files
  934. os.mkdir(self.fullpath('dom0-home'))
  935. os.mkdir(self.fullpath('dom0-home/user'))
  936. for d in self.dom0_dirs:
  937. os.mkdir(self.fullpath('dom0-home/user/' + d))
  938. for f in self.dom0_files:
  939. with open(self.fullpath('dom0-home/user/' + f), 'w') as ff:
  940. ff.write('some content')
  941. def assertDirectoryExists(self, path):
  942. if not os.path.exists(path):
  943. self.fail(path + ' missing')
  944. if not os.path.isdir(path):
  945. self.fail(path + ' is not a directory')
  946. def assertFileExists(self, path):
  947. if not os.path.exists(path):
  948. self.fail(path + ' missing')
  949. if not os.path.isfile(path):
  950. self.fail(path + ' is not a file')
  951. def assertDom0Restored(self, timestamp):
  952. expected_dir = os.path.expanduser(
  953. '~/home-restore-' + timestamp + '/dom0-home/user')
  954. self.assertTrue(os.path.exists(expected_dir))
  955. for d in self.dom0_dirs:
  956. self.assertDirectoryExists(os.path.join(expected_dir, d))
  957. for f in self.dom0_files:
  958. self.assertFileExists(os.path.join(expected_dir, f))
  959. # cleanup
  960. shutil.rmtree(expected_dir)
  961. def create_v4_files(self):
  962. appmenus_list = [
  963. "firefox", "gnome-terminal", "evince", "evolution",
  964. "mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
  965. "gedit", "gpk-update-viewer", "gpk-application"
  966. ]
  967. os.mkdir(self.fullpath("appvms"))
  968. os.mkdir(self.fullpath("vm-templates"))
  969. # normal AppVMs
  970. for vm in ('test-work', 'test-d8test', 'test-proxy',
  971. 'test-custom-template-appvm', 'test-net'):
  972. os.mkdir(self.fullpath('appvms/{}'.format(vm)))
  973. self.create_whitelisted_appmenus(self.fullpath(
  974. 'appvms/{}/whitelisted-appmenus.list'.format(vm)))
  975. self.create_private_img(self.fullpath('appvms/{}/private.img'.format(
  976. vm)))
  977. # setup firewall only on one VM
  978. with open(self.fullpath("appvms/test-work/firewall.xml"), "wb") as \
  979. f_firewall:
  980. f_firewall.write(
  981. pkg_resources.resource_string(__name__, 'v4-firewall.xml'))
  982. # StandaloneVMs
  983. for vm in ('test-standalonevm', 'test-hvm'):
  984. os.mkdir(self.fullpath('appvms/{}'.format(vm)))
  985. self.create_whitelisted_appmenus(self.fullpath(
  986. 'appvms/{}/whitelisted-appmenus.list'.format(vm)))
  987. self.create_private_img(self.fullpath(
  988. 'appvms/{}/private.img'.format(vm)))
  989. self.create_sparse(
  990. self.fullpath('appvms/{}/root.img'.format(vm)), 10*2**30)
  991. self.fill_image(self.fullpath('appvms/{}/root.img'.format(vm)),
  992. 1024*1024, True,
  993. signature='{}/root'.format(vm).encode())
  994. # only for Linux one
  995. os.mkdir(self.fullpath('appvms/test-standalonevm/apps.templates'))
  996. self.create_appmenus(
  997. self.fullpath('appvms/test-standalonevm/apps.templates'),
  998. APPTEMPLATE_R2B2,
  999. appmenus_list)
  1000. # Custom template
  1001. os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone"))
  1002. self.create_private_img(
  1003. self.fullpath("vm-templates/test-fedora-25-clone/private.img"))
  1004. self.create_sparse(self.fullpath(
  1005. "vm-templates/test-fedora-25-clone/root.img"), 10*2**20)
  1006. self.fill_image(self.fullpath(
  1007. "vm-templates/test-fedora-25-clone/root.img"), 1*2**20, True,
  1008. signature=b'test-fedora-25-clone/root')
  1009. self.create_volatile_img(self.fullpath(
  1010. "vm-templates/test-fedora-25-clone/volatile.img"))
  1011. self.create_whitelisted_appmenus(self.fullpath(
  1012. "vm-templates/test-fedora-25-clone/whitelisted-appmenus.list"))
  1013. self.create_whitelisted_appmenus(self.fullpath(
  1014. "vm-templates/test-fedora-25-clone/vm-whitelisted-appmenus.list"))
  1015. os.mkdir(
  1016. self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"))
  1017. self.create_appmenus(
  1018. self.fullpath("vm-templates/test-fedora-25-clone/apps.templates"),
  1019. APPTEMPLATE_R2B2,
  1020. appmenus_list)
  1021. os.mkdir(self.fullpath("vm-templates/test-fedora-25-clone/apps"))
  1022. self.create_appmenus(
  1023. self.fullpath("vm-templates/test-fedora-25-clone/apps"),
  1024. APPTEMPLATE_R2B2.replace("%VMNAME%", "test-fedora-25-clone")
  1025. .replace("%VMDIR%", self.fullpath(
  1026. "vm-templates/test-fedora-25-clone")),
  1027. appmenus_list)
  1028. self.create_dom0_files()
  1029. def scrypt_encrypt(self, f_name, output_name=None, password='qubes',
  1030. basedir=None):
  1031. if basedir is None:
  1032. basedir = self.backupdir
  1033. if output_name is None:
  1034. output_name = f_name + '.enc'
  1035. if f_name == 'backup-header':
  1036. scrypt_pass = 'backup-header!' + password
  1037. else:
  1038. scrypt_pass = '20161020T123455-1234!{}!{}'.format(f_name, password)
  1039. p = subprocess.Popen(['scrypt', 'enc', '-P', '-t', '0.1',
  1040. os.path.join(basedir, f_name), os.path.join(basedir, output_name)],
  1041. stdin=subprocess.PIPE)
  1042. p.communicate(scrypt_pass.encode())
  1043. assert p.wait() == 0
  1044. return output_name
  1045. def calculate_hmac(self, f_name, algorithm="sha512", password="qubes"):
  1046. with open(self.fullpath(f_name), "r") as f_data:
  1047. with open(self.fullpath(f_name+".hmac"), "w") as f_hmac:
  1048. subprocess.check_call(
  1049. ["openssl", "dgst", "-"+algorithm, "-hmac", password],
  1050. stdin=f_data, stdout=f_hmac)
  1051. def append_backup_stream(self, f_name, stream, basedir=None):
  1052. if not basedir:
  1053. basedir = self.backupdir
  1054. subprocess.check_call(["tar", "-cO", "--posix", "-C", basedir,
  1055. f_name],
  1056. stdout=stream)
  1057. def handle_v3_file(self, f_name, subdir, stream, compressed=True,
  1058. encrypted=True):
  1059. # create inner archive
  1060. tar_cmdline = ["tar", "-Pc", '--sparse',
  1061. '-C', self.fullpath(os.path.dirname(f_name)),
  1062. '--xform', 's:^%s:%s\\0:' % (
  1063. os.path.basename(f_name),
  1064. subdir),
  1065. os.path.basename(f_name)
  1066. ]
  1067. if compressed:
  1068. tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
  1069. tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
  1070. if encrypted:
  1071. encryptor = subprocess.Popen(
  1072. ["openssl", "enc", "-e", "-aes-256-cbc", "-pass", "pass:qubes"],
  1073. stdin=tar.stdout,
  1074. stdout=subprocess.PIPE)
  1075. tar.stdout.close()
  1076. data = encryptor.stdout
  1077. else:
  1078. data = tar.stdout
  1079. encryptor = None
  1080. stage1_dir = self.fullpath(os.path.join("stage1", subdir))
  1081. if not os.path.exists(stage1_dir):
  1082. os.makedirs(stage1_dir)
  1083. subprocess.check_call(["split", "--numeric-suffixes",
  1084. "--suffix-length=3",
  1085. "--bytes="+str(100*1024*1024), "-",
  1086. os.path.join(stage1_dir,
  1087. os.path.basename(f_name+"."))],
  1088. stdin=data)
  1089. data.close()
  1090. tar.wait()
  1091. if encryptor:
  1092. encryptor.wait()
  1093. for part in sorted(os.listdir(stage1_dir)):
  1094. if not re.match(
  1095. r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
  1096. part):
  1097. continue
  1098. part_with_dir = os.path.join(subdir, part)
  1099. self.calculate_hmac(os.path.join("stage1", part_with_dir))
  1100. self.append_backup_stream(part_with_dir, stream,
  1101. basedir=self.fullpath("stage1"))
  1102. self.append_backup_stream(part_with_dir+".hmac", stream,
  1103. basedir=self.fullpath("stage1"))
  1104. def handle_v4_file(self, f_name, subdir, stream, compressed=True):
  1105. # create inner archive
  1106. tar_cmdline = ["tar", "-Pc", '--sparse',
  1107. '-C', self.fullpath(os.path.dirname(f_name)),
  1108. '--xform', 's:^%s:%s\\0:' % (
  1109. os.path.basename(f_name),
  1110. subdir),
  1111. os.path.basename(f_name)
  1112. ]
  1113. if compressed:
  1114. tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
  1115. tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
  1116. data = tar.stdout
  1117. stage1_dir = self.fullpath(os.path.join("stage1", subdir))
  1118. if not os.path.exists(stage1_dir):
  1119. os.makedirs(stage1_dir)
  1120. subprocess.check_call(["split", "--numeric-suffixes",
  1121. "--suffix-length=3",
  1122. "--bytes="+str(100*1024*1024), "-",
  1123. os.path.join(stage1_dir,
  1124. os.path.basename(f_name+"."))],
  1125. stdin=data)
  1126. data.close()
  1127. tar.wait()
  1128. for part in sorted(os.listdir(stage1_dir)):
  1129. if not re.match(
  1130. r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
  1131. part):
  1132. continue
  1133. part_with_dir = os.path.join(subdir, part)
  1134. f_name = self.scrypt_encrypt(part_with_dir,
  1135. basedir=self.fullpath('stage1'))
  1136. self.append_backup_stream(f_name, stream,
  1137. basedir=self.fullpath("stage1"))
  1138. def create_v3_backup(self, encrypted=True, compressed=True):
  1139. """
  1140. Create "backup format 3" backup - used in R2 and R3.0
  1141. :param encrypted: Should the backup be encrypted
  1142. :param compressed: Should the backup be compressed
  1143. :return:
  1144. """
  1145. output = open(self.fullpath("backup.bin"), "w")
  1146. with open(self.fullpath("backup-header"), "w") as f:
  1147. f.write(BACKUP_HEADER_R2.format(
  1148. encrypted=str(encrypted),
  1149. compressed=str(compressed)
  1150. ))
  1151. self.calculate_hmac("backup-header")
  1152. self.append_backup_stream("backup-header", output)
  1153. self.append_backup_stream("backup-header.hmac", output)
  1154. with open(self.fullpath("qubes.xml"), "wb") as f:
  1155. qubesxml = pkg_resources.resource_string(__name__, 'v3-qubes.xml')
  1156. if encrypted:
  1157. for vmname, subdir in MANGLED_SUBDIRS_R2.items():
  1158. qubesxml = re.sub(r"[a-z-]*/{}".format(vmname).encode(),
  1159. subdir.encode(), qubesxml)
  1160. f.write(qubesxml)
  1161. else:
  1162. f.write(qubesxml)
  1163. self.handle_v3_file("qubes.xml", "", output, encrypted=encrypted,
  1164. compressed=compressed)
  1165. self.create_v1_files(r2b2=True)
  1166. for vm_type in ["appvms", "servicevms"]:
  1167. for vm_name in os.listdir(self.fullpath(vm_type)):
  1168. vm_dir = os.path.join(vm_type, vm_name)
  1169. for f_name in os.listdir(self.fullpath(vm_dir)):
  1170. if encrypted:
  1171. subdir = MANGLED_SUBDIRS_R2[vm_name]
  1172. else:
  1173. subdir = vm_dir
  1174. self.handle_v3_file(
  1175. os.path.join(vm_dir, f_name),
  1176. subdir+'/', output,
  1177. compressed=compressed,
  1178. encrypted=encrypted)
  1179. for vm_name in os.listdir(self.fullpath("vm-templates")):
  1180. vm_dir = os.path.join("vm-templates", vm_name)
  1181. if encrypted:
  1182. subdir = MANGLED_SUBDIRS_R2[vm_name]
  1183. else:
  1184. subdir = vm_dir
  1185. self.handle_v3_file(
  1186. os.path.join(vm_dir, "."),
  1187. subdir+'/', output,
  1188. compressed=compressed,
  1189. encrypted=encrypted)
  1190. self.handle_v3_file(
  1191. 'dom0-home/user',
  1192. 'dom0-home/', output,
  1193. compressed=compressed,
  1194. encrypted=encrypted)
  1195. output.close()
  1196. def create_v4_backup(self, compressed=True):
  1197. """
  1198. Create "backup format 4" backup - used in R4.0
  1199. :param compressed: Should the backup be compressed
  1200. :return:
  1201. """
  1202. output = open(self.fullpath("backup.bin"), "w")
  1203. with open(self.fullpath("backup-header"), "w") as f:
  1204. f.write(BACKUP_HEADER_R4.format(
  1205. compressed=str(compressed)
  1206. ))
  1207. self.scrypt_encrypt("backup-header", output_name='backup-header.hmac')
  1208. self.append_backup_stream("backup-header", output)
  1209. self.append_backup_stream("backup-header.hmac", output)
  1210. with open(self.fullpath("qubes.xml"), "wb") as f:
  1211. qubesxml = pkg_resources.resource_string(__name__, 'v4-qubes.xml')
  1212. for vmname, subdir in MANGLED_SUBDIRS_R4.items():
  1213. qubesxml = re.sub(
  1214. r'backup-path">[a-z-]*/{}'.format(vmname).encode(),
  1215. ('backup-path">' + subdir).encode(),
  1216. qubesxml)
  1217. f.write(qubesxml)
  1218. self.handle_v4_file("qubes.xml", "", output, compressed=compressed)
  1219. self.create_v4_files()
  1220. for vm_type in ["appvms", "vm-templates"]:
  1221. for vm_name in os.listdir(self.fullpath(vm_type)):
  1222. vm_dir = os.path.join(vm_type, vm_name)
  1223. for f_name in os.listdir(self.fullpath(vm_dir)):
  1224. subdir = MANGLED_SUBDIRS_R4[vm_name]
  1225. self.handle_v4_file(
  1226. os.path.join(vm_dir, f_name),
  1227. subdir+'/', output, compressed=compressed)
  1228. self.handle_v4_file(
  1229. 'dom0-home/user',
  1230. 'dom0-home/', output,
  1231. compressed=compressed)
  1232. output.close()
  1233. def setup_expected_calls(self, parsed_qubes_xml, templates_map=None):
  1234. if templates_map is None:
  1235. templates_map = {}
  1236. extra_vm_list_lines = []
  1237. for name, vm in parsed_qubes_xml['domains'].items():
  1238. if not vm['included_in_backup']:
  1239. continue
  1240. if name == 'dom0':
  1241. continue
  1242. if self.storage_pool:
  1243. self.app.expected_calls[
  1244. ('dom0', 'admin.vm.CreateInPool.' + vm['klass'],
  1245. templates_map.get(vm['template'], vm['template']),
  1246. 'name={} label={} pool={}'.format(
  1247. name, vm['label'], self.storage_pool).encode())] = \
  1248. b'0\0'
  1249. else:
  1250. self.app.expected_calls[
  1251. ('dom0', 'admin.vm.Create.' + vm['klass'],
  1252. templates_map.get(vm['template'], vm['template']),
  1253. 'name={} label={}'.format(name, vm['label']).encode())] =\
  1254. b'0\0'
  1255. extra_vm_list_lines.append(
  1256. '{} class={} state=Halted\n'.format(name, vm['klass']).encode())
  1257. if vm['backup_path']:
  1258. self.app.expected_calls[
  1259. (name, 'admin.vm.volume.List', None, None)] = \
  1260. b'0\0root\nprivate\nvolatile\n'
  1261. if vm['klass'] == 'AppVM':
  1262. self.app.expected_calls[
  1263. (name, 'admin.vm.volume.Info', 'root', None)] = \
  1264. b'0\0' \
  1265. b'pool=default\n' \
  1266. b'vid=' + name.encode() + b'/root\n' \
  1267. b'size=1024\n' \
  1268. b'usage=512\n' \
  1269. b'rw=False\n' \
  1270. b'snap_on_start=True\n' \
  1271. b'save_on_stop=False\n' \
  1272. b'source=\n' \
  1273. b'internal=True\n' \
  1274. b'revisions_to_keep=3\n'
  1275. else:
  1276. self.app.expected_calls[
  1277. (name, 'admin.vm.volume.Info', 'root', None)] = \
  1278. b'0\0' \
  1279. b'pool=default\n' \
  1280. b'vid=' + name.encode() + b'/root\n' \
  1281. b'size=1024\n' \
  1282. b'usage=512\n' \
  1283. b'rw=True\n' \
  1284. b'snap_on_start=False\n' \
  1285. b'save_on_stop=True\n' \
  1286. b'internal=True\n' \
  1287. b'revisions_to_keep=3\n'
  1288. self.app.expected_calls[
  1289. (name, 'admin.vm.volume.Resize', 'root',
  1290. str(vm.get('root_size', 2097664)).encode())] = \
  1291. b'0\0'
  1292. self.app.expected_calls[
  1293. (name, 'admin.vm.volume.Import', 'root',
  1294. name.encode() + b'/root')] = \
  1295. b'0\0'
  1296. self.app.expected_calls[
  1297. (name, 'admin.vm.volume.Info', 'private', None)] = \
  1298. b'0\0' \
  1299. b'pool=default\n' \
  1300. b'vid=' + name.encode() + b'/private\n' \
  1301. b'size=1024\n' \
  1302. b'usage=512\n' \
  1303. b'rw=True\n' \
  1304. b'snap_on_start=False\n' \
  1305. b'save_on_stop=True\n' \
  1306. b'revisions_to_keep=3\n'
  1307. self.app.expected_calls[
  1308. (name, 'admin.vm.volume.Resize', 'private', b'2097152')] = \
  1309. b'0\0'
  1310. self.app.expected_calls[
  1311. (name, 'admin.vm.volume.Import', 'private',
  1312. name.encode() + b'/private')] = \
  1313. b'0\0'
  1314. self.app.expected_calls[
  1315. (name, 'admin.vm.volume.Info', 'volatile', None)] = \
  1316. b'0\0' \
  1317. b'pool=default\n' \
  1318. b'vid=' + name.encode() + b'/root\n' \
  1319. b'size=1024\n' \
  1320. b'usage=512\n' \
  1321. b'rw=True\n' \
  1322. b'snap_on_start=False\n' \
  1323. b'save_on_stop=False\n' \
  1324. b'revisions_to_keep=3\n'
  1325. for prop, value in vm['properties'].items():
  1326. self.app.expected_calls[
  1327. (name, 'admin.vm.property.Set', prop,
  1328. str(value).encode() if value is not None else b'')] = b'0\0'
  1329. for bus, devices in vm['devices'].items():
  1330. for (backend_domain, ident), options in devices.items():
  1331. all_options = options.copy()
  1332. all_options['persistent'] = True
  1333. encoded_options = ' '.join('{}={}'.format(key, value) for
  1334. key, value in all_options.items()).encode()
  1335. self.app.expected_calls[
  1336. (name, 'admin.vm.device.{}.Attach'.format(bus),
  1337. '{}+{}'.format(backend_domain, ident),
  1338. encoded_options)] = b'0\0'
  1339. for feature, value in vm['features'].items():
  1340. if value is False:
  1341. value = ''
  1342. elif value is True:
  1343. value = '1'
  1344. self.app.expected_calls[
  1345. (name, 'admin.vm.feature.Set', feature,
  1346. str(value).encode())] = b'0\0'
  1347. for tag in vm['tags']:
  1348. self.app.expected_calls[
  1349. (name, 'admin.vm.tag.Set', tag, None)] = b'0\0'
  1350. if vm['backup_path']:
  1351. appmenus = (
  1352. b'gnome-terminal.desktop\n'
  1353. b'nautilus.desktop\n'
  1354. b'firefox.desktop\n'
  1355. b'mozilla-thunderbird.desktop\n'
  1356. b'libreoffice-startcenter.desktop\n'
  1357. )
  1358. self.app.expected_calls[
  1359. (name, 'appmenus', None, appmenus)] = b'0\0'
  1360. orig_admin_vm_list = self.app.expected_calls[
  1361. ('dom0', 'admin.vm.List', None, None)]
  1362. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1363. [orig_admin_vm_list] + \
  1364. [orig_admin_vm_list + b''.join(extra_vm_list_lines)] * \
  1365. len(extra_vm_list_lines)
  1366. def mock_appmenus(self, queue, vm, stream):
  1367. queue.put((vm.name, 'appmenus', None, stream.read()))
  1368. def test_210_r2(self):
  1369. self.create_v3_backup(False)
  1370. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
  1371. b'0\0dom0 class=AdminVM state=Running\n'
  1372. b'fedora-25 class=TemplateVM state=Halted\n'
  1373. b'testvm class=AppVM state=Running\n'
  1374. )
  1375. self.app.expected_calls[
  1376. ('dom0', 'admin.property.Get', 'default_template', None)] = \
  1377. b'0\0default=no type=vm fedora-25'
  1378. self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
  1379. 'fedora-20-x64': 'fedora-25'
  1380. })
  1381. firewall_data = (
  1382. 'action=accept specialtarget=dns\n'
  1383. 'action=accept proto=icmp\n'
  1384. 'action=accept proto=tcp dstports=22-22\n'
  1385. 'action=accept proto=tcp dstports=9418-9418\n'
  1386. 'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
  1387. 'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
  1388. 'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
  1389. 'action=drop\n'
  1390. )
  1391. self.app.expected_calls[
  1392. ('test-work', 'admin.vm.firewall.Set', None,
  1393. firewall_data.encode())] = b'0\0'
  1394. self.app.expected_calls[
  1395. ('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
  1396. firewall_data.encode())] = b'0\0'
  1397. qubesd_calls_queue = multiprocessing.Queue()
  1398. dummy_timestamp = time.strftime("test-%Y-%m-%d-%H%M%S")
  1399. patches = [
  1400. mock.patch('qubesadmin.storage.Volume',
  1401. functools.partial(MockVolume, qubesd_calls_queue)),
  1402. mock.patch(
  1403. 'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
  1404. functools.partial(self.mock_appmenus, qubesd_calls_queue)),
  1405. mock.patch(
  1406. 'qubesadmin.firewall.Firewall',
  1407. functools.partial(MockFirewall, qubesd_calls_queue)),
  1408. mock.patch(
  1409. 'time.strftime',
  1410. return_value=dummy_timestamp)
  1411. ]
  1412. for patch in patches:
  1413. patch.start()
  1414. try:
  1415. self.restore_backup(self.fullpath("backup.bin"), options={
  1416. 'use-default-template': True,
  1417. 'use-default-netvm': True,
  1418. })
  1419. finally:
  1420. for patch in patches:
  1421. patch.stop()
  1422. # retrieve calls from other multiprocess.Process instances
  1423. while not qubesd_calls_queue.empty():
  1424. call_args = qubesd_calls_queue.get()
  1425. self.app.qubesd_call(*call_args)
  1426. qubesd_calls_queue.close()
  1427. self.assertAllCalled()
  1428. self.assertDom0Restored(dummy_timestamp)
  1429. def test_220_r2_encrypted(self):
  1430. self.create_v3_backup(True)
  1431. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
  1432. b'0\0dom0 class=AdminVM state=Running\n'
  1433. b'fedora-25 class=TemplateVM state=Halted\n'
  1434. b'testvm class=AppVM state=Running\n'
  1435. )
  1436. self.app.expected_calls[
  1437. ('dom0', 'admin.property.Get', 'default_template', None)] = \
  1438. b'0\0default=no type=vm fedora-25'
  1439. self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
  1440. 'fedora-20-x64': 'fedora-25'
  1441. })
  1442. firewall_data = (
  1443. 'action=accept specialtarget=dns\n'
  1444. 'action=accept proto=icmp\n'
  1445. 'action=accept proto=tcp dstports=22-22\n'
  1446. 'action=accept proto=tcp dstports=9418-9418\n'
  1447. 'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
  1448. 'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
  1449. 'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
  1450. 'action=drop\n'
  1451. )
  1452. self.app.expected_calls[
  1453. ('test-work', 'admin.vm.firewall.Set', None,
  1454. firewall_data.encode())] = b'0\0'
  1455. self.app.expected_calls[
  1456. ('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
  1457. firewall_data.encode())] = b'0\0'
  1458. qubesd_calls_queue = multiprocessing.Queue()
  1459. dummy_timestamp = time.strftime("test-%Y-%m-%d-%H%M%S")
  1460. patches = [
  1461. mock.patch('qubesadmin.storage.Volume',
  1462. functools.partial(MockVolume, qubesd_calls_queue)),
  1463. mock.patch(
  1464. 'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
  1465. functools.partial(self.mock_appmenus, qubesd_calls_queue)),
  1466. mock.patch(
  1467. 'qubesadmin.firewall.Firewall',
  1468. functools.partial(MockFirewall, qubesd_calls_queue)),
  1469. mock.patch(
  1470. 'time.strftime',
  1471. return_value=dummy_timestamp)
  1472. ]
  1473. for patch in patches:
  1474. patch.start()
  1475. try:
  1476. self.restore_backup(self.fullpath("backup.bin"), options={
  1477. 'use-default-template': True,
  1478. 'use-default-netvm': True,
  1479. })
  1480. finally:
  1481. for patch in patches:
  1482. patch.stop()
  1483. # retrieve calls from other multiprocess.Process instances
  1484. while not qubesd_calls_queue.empty():
  1485. call_args = qubesd_calls_queue.get()
  1486. self.app.qubesd_call(*call_args)
  1487. qubesd_calls_queue.close()
  1488. self.assertAllCalled()
  1489. self.assertDom0Restored(dummy_timestamp)
  1490. def test_230_r2_uncompressed(self):
  1491. self.create_v3_backup(False, False)
  1492. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
  1493. b'0\0dom0 class=AdminVM state=Running\n'
  1494. b'fedora-25 class=TemplateVM state=Halted\n'
  1495. b'testvm class=AppVM state=Running\n'
  1496. )
  1497. self.app.expected_calls[
  1498. ('dom0', 'admin.property.Get', 'default_template', None)] = \
  1499. b'0\0default=no type=vm fedora-25'
  1500. self.setup_expected_calls(parsed_qubes_xml_r2, templates_map={
  1501. 'fedora-20-x64': 'fedora-25'
  1502. })
  1503. firewall_data = (
  1504. 'action=accept specialtarget=dns\n'
  1505. 'action=accept proto=icmp\n'
  1506. 'action=accept proto=tcp dstports=22-22\n'
  1507. 'action=accept proto=tcp dstports=9418-9418\n'
  1508. 'action=accept proto=tcp dst4=192.168.0.1/32 dstports=1234-1234\n'
  1509. 'action=accept proto=tcp dsthost=fedorahosted.org dstports=443-443\n'
  1510. 'action=accept proto=tcp dsthost=xenbits.xen.org dstports=80-80\n'
  1511. 'action=drop\n'
  1512. )
  1513. self.app.expected_calls[
  1514. ('test-work', 'admin.vm.firewall.Set', None,
  1515. firewall_data.encode())] = b'0\0'
  1516. self.app.expected_calls[
  1517. ('test-custom-template-appvm', 'admin.vm.firewall.Set', None,
  1518. firewall_data.encode())] = b'0\0'
  1519. qubesd_calls_queue = multiprocessing.Queue()
  1520. dummy_timestamp = time.strftime("test-%Y-%m-%d-%H%M%S")
  1521. patches = [
  1522. mock.patch('qubesadmin.storage.Volume',
  1523. functools.partial(MockVolume, qubesd_calls_queue)),
  1524. mock.patch(
  1525. 'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
  1526. functools.partial(self.mock_appmenus, qubesd_calls_queue)),
  1527. mock.patch(
  1528. 'qubesadmin.firewall.Firewall',
  1529. functools.partial(MockFirewall, qubesd_calls_queue)),
  1530. mock.patch(
  1531. 'time.strftime',
  1532. return_value=dummy_timestamp)
  1533. ]
  1534. for patch in patches:
  1535. patch.start()
  1536. try:
  1537. self.restore_backup(self.fullpath("backup.bin"), options={
  1538. 'use-default-template': True,
  1539. 'use-default-netvm': True,
  1540. })
  1541. finally:
  1542. for patch in patches:
  1543. patch.stop()
  1544. # retrieve calls from other multiprocess.Process instances
  1545. while not qubesd_calls_queue.empty():
  1546. call_args = qubesd_calls_queue.get()
  1547. self.app.qubesd_call(*call_args)
  1548. qubesd_calls_queue.close()
  1549. self.assertAllCalled()
  1550. self.assertDom0Restored(dummy_timestamp)
  1551. @unittest.skipUnless(spawn.find_executable('scrypt'),
  1552. "scrypt not installed")
  1553. def test_230_r4(self):
  1554. self.create_v4_backup(False)
  1555. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
  1556. b'0\0dom0 class=AdminVM state=Running\n'
  1557. b'fedora-25 class=TemplateVM state=Halted\n'
  1558. b'testvm class=AppVM state=Running\n'
  1559. b'sys-net class=AppVM state=Running\n'
  1560. )
  1561. self.app.expected_calls[
  1562. ('dom0', 'admin.property.Get', 'default_template', None)] = \
  1563. b'0\0default=no type=vm fedora-25'
  1564. self.app.expected_calls[
  1565. ('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
  1566. b'0\0default=no type=bool True'
  1567. self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
  1568. 'debian-8': 'fedora-25'
  1569. })
  1570. firewall_data = (
  1571. 'action=accept specialtarget=dns\n'
  1572. 'action=accept proto=icmp\n'
  1573. 'action=accept proto=tcp dstports=22-22\n'
  1574. 'action=accept proto=tcp dsthost=www.qubes-os.org '
  1575. 'dstports=443-443\n'
  1576. 'action=accept proto=tcp dst4=192.168.0.0/24\n'
  1577. 'action=drop\n'
  1578. )
  1579. self.app.expected_calls[
  1580. ('test-work', 'admin.vm.firewall.Set', None,
  1581. firewall_data.encode())] = b'0\0'
  1582. qubesd_calls_queue = multiprocessing.Queue()
  1583. dummy_timestamp = time.strftime("test-%Y-%m-%d-%H%M%S")
  1584. patches = [
  1585. mock.patch('qubesadmin.storage.Volume',
  1586. functools.partial(MockVolume, qubesd_calls_queue)),
  1587. mock.patch(
  1588. 'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
  1589. functools.partial(self.mock_appmenus, qubesd_calls_queue)),
  1590. mock.patch(
  1591. 'qubesadmin.firewall.Firewall',
  1592. functools.partial(MockFirewall, qubesd_calls_queue)),
  1593. mock.patch(
  1594. 'time.strftime',
  1595. return_value=dummy_timestamp)
  1596. ]
  1597. for patch in patches:
  1598. patch.start()
  1599. try:
  1600. self.restore_backup(self.fullpath("backup.bin"), options={
  1601. 'use-default-template': True,
  1602. 'use-default-netvm': True,
  1603. })
  1604. finally:
  1605. for patch in patches:
  1606. patch.stop()
  1607. # retrieve calls from other multiprocess.Process instances
  1608. while not qubesd_calls_queue.empty():
  1609. call_args = qubesd_calls_queue.get()
  1610. self.app.qubesd_call(*call_args)
  1611. qubesd_calls_queue.close()
  1612. self.assertAllCalled()
  1613. self.assertDom0Restored(dummy_timestamp)
  1614. @unittest.skipUnless(spawn.find_executable('scrypt'),
  1615. "scrypt not installed")
  1616. def test_230_r4_compressed(self):
  1617. self.create_v4_backup(True)
  1618. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = (
  1619. b'0\0dom0 class=AdminVM state=Running\n'
  1620. b'fedora-25 class=TemplateVM state=Halted\n'
  1621. b'testvm class=AppVM state=Running\n'
  1622. b'sys-net class=AppVM state=Running\n'
  1623. )
  1624. self.app.expected_calls[
  1625. ('dom0', 'admin.property.Get', 'default_template', None)] = \
  1626. b'0\0default=no type=vm fedora-25'
  1627. self.app.expected_calls[
  1628. ('sys-net', 'admin.vm.property.Get', 'provides_network', None)] = \
  1629. b'0\0default=no type=bool True'
  1630. self.setup_expected_calls(parsed_qubes_xml_v4, templates_map={
  1631. 'debian-8': 'fedora-25'
  1632. })
  1633. firewall_data = (
  1634. 'action=accept specialtarget=dns\n'
  1635. 'action=accept proto=icmp\n'
  1636. 'action=accept proto=tcp dstports=22-22\n'
  1637. 'action=accept proto=tcp dsthost=www.qubes-os.org '
  1638. 'dstports=443-443\n'
  1639. 'action=accept proto=tcp dst4=192.168.0.0/24\n'
  1640. 'action=drop\n'
  1641. )
  1642. self.app.expected_calls[
  1643. ('test-work', 'admin.vm.firewall.Set', None,
  1644. firewall_data.encode())] = b'0\0'
  1645. qubesd_calls_queue = multiprocessing.Queue()
  1646. dummy_timestamp = time.strftime("test-%Y-%m-%d-%H%M%S")
  1647. patches = [
  1648. mock.patch('qubesadmin.storage.Volume',
  1649. functools.partial(MockVolume, qubesd_calls_queue)),
  1650. mock.patch(
  1651. 'qubesadmin.backup.restore.BackupRestore._handle_appmenus_list',
  1652. functools.partial(self.mock_appmenus, qubesd_calls_queue)),
  1653. mock.patch(
  1654. 'qubesadmin.firewall.Firewall',
  1655. functools.partial(MockFirewall, qubesd_calls_queue)),
  1656. mock.patch(
  1657. 'time.strftime',
  1658. return_value=dummy_timestamp)
  1659. ]
  1660. for patch in patches:
  1661. patch.start()
  1662. try:
  1663. self.restore_backup(self.fullpath("backup.bin"), options={
  1664. 'use-default-template': True,
  1665. 'use-default-netvm': True,
  1666. })
  1667. finally:
  1668. for patch in patches:
  1669. patch.stop()
  1670. # retrieve calls from other multiprocess.Process instances
  1671. while not qubesd_calls_queue.empty():
  1672. call_args = qubesd_calls_queue.get()
  1673. self.app.qubesd_call(*call_args)
  1674. qubesd_calls_queue.close()
  1675. self.assertAllCalled()
  1676. self.assertDom0Restored(dummy_timestamp)
  1677. class TC_11_BackupCompatibilityIntoLVM(TC_10_BackupCompatibility):
  1678. storage_pool = 'some-pool'
  1679. def restore_backup(self, source=None, appvm=None, options=None,
  1680. expect_errors=None, manipulate_restore_info=None,
  1681. passphrase='qubes'):
  1682. if options is None:
  1683. options = {}
  1684. options['override_pool'] = self.storage_pool
  1685. super(TC_11_BackupCompatibilityIntoLVM, self).restore_backup(source,
  1686. appvm, options, expect_errors, manipulate_restore_info)