backupcompatibility.py 72 KB

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