backupcompatibility.py 86 KB

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