qvm_template.py 144 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530
  1. from unittest import mock
  2. import argparse
  3. import asyncio
  4. import datetime
  5. import io
  6. import os
  7. import pathlib
  8. import subprocess
  9. import tempfile
  10. import rpm
  11. import qubesadmin.tests
  12. import qubesadmin.tools.qvm_template
  13. class TC_00_qvm_template(qubesadmin.tests.QubesTestCase):
  14. def setUp(self):
  15. # Print str(list) directly so that the output is consistent no matter
  16. # which implementation of `column` we use
  17. self.mock_table = mock.patch('qubesadmin.tools.print_table')
  18. mock_table = self.mock_table.start()
  19. def print_table(table, *args):
  20. print(str(table))
  21. mock_table.side_effect = print_table
  22. super().setUp()
  23. def tearDown(self):
  24. self.mock_table.stop()
  25. super().tearDown()
  26. def test_000_verify_rpm_success(self):
  27. ts = mock.MagicMock()
  28. # Just return a dict instead of rpm.hdr
  29. hdr = {
  30. rpm.RPMTAG_SIGPGP: 'xxx', # non-empty
  31. rpm.RPMTAG_SIGGPG: 'xxx', # non-empty
  32. }
  33. ts.hdrFromFdno.return_value = hdr
  34. ret = qubesadmin.tools.qvm_template.verify_rpm('/dev/null', ts)
  35. ts.hdrFromFdno.assert_called_once()
  36. self.assertEqual(hdr, ret)
  37. self.assertAllCalled()
  38. def test_001_verify_rpm_nosig_fail(self):
  39. ts = mock.MagicMock()
  40. # Just return a dict instead of rpm.hdr
  41. hdr = {
  42. rpm.RPMTAG_SIGPGP: None, # empty
  43. rpm.RPMTAG_SIGGPG: None, # empty
  44. }
  45. ts.hdrFromFdno.return_value = hdr
  46. ret = qubesadmin.tools.qvm_template.verify_rpm('/dev/null', ts)
  47. ts.hdrFromFdno.assert_called_once()
  48. self.assertEqual(ret, None)
  49. self.assertAllCalled()
  50. def test_002_verify_rpm_nosig_success(self):
  51. ts = mock.MagicMock()
  52. # Just return a dict instead of rpm.hdr
  53. hdr = {
  54. rpm.RPMTAG_SIGPGP: None, # empty
  55. rpm.RPMTAG_SIGGPG: None, # empty
  56. }
  57. ts.hdrFromFdno.return_value = hdr
  58. ret = qubesadmin.tools.qvm_template.verify_rpm('/dev/null', ts, True)
  59. ts.hdrFromFdno.assert_called_once()
  60. self.assertEqual(ret, hdr)
  61. self.assertAllCalled()
  62. def test_003_verify_rpm_badsig_fail(self):
  63. ts = mock.MagicMock()
  64. def f(*args):
  65. raise rpm.error('public key not trusted')
  66. ts.hdrFromFdno.side_effect = f
  67. ret = qubesadmin.tools.qvm_template.verify_rpm('/dev/null', ts)
  68. ts.hdrFromFdno.assert_called_once()
  69. self.assertEqual(ret, None)
  70. self.assertAllCalled()
  71. @mock.patch('subprocess.Popen')
  72. def test_010_extract_rpm_success(self, mock_popen):
  73. pipe = mock.Mock()
  74. mock_popen.return_value.stdout = pipe
  75. mock_popen.return_value.wait.return_value = 0
  76. with tempfile.NamedTemporaryFile() as fd, \
  77. tempfile.TemporaryDirectory() as dir:
  78. path = fd.name
  79. dirpath = dir
  80. ret = qubesadmin.tools.qvm_template.extract_rpm(
  81. 'test-vm', path, dirpath)
  82. self.assertEqual(ret, True)
  83. self.assertEqual(mock_popen.mock_calls, [
  84. mock.call(['rpm2cpio', path], stdout=subprocess.PIPE),
  85. mock.call([
  86. 'cpio',
  87. '-idm',
  88. '-D',
  89. dirpath,
  90. './var/lib/qubes/vm-templates/test-vm/*'
  91. ], stdin=pipe, stdout=subprocess.DEVNULL),
  92. mock.call().wait(),
  93. mock.call().wait()
  94. ])
  95. self.assertAllCalled()
  96. @mock.patch('subprocess.Popen')
  97. def test_011_extract_rpm_fail(self, mock_popen):
  98. pipe = mock.Mock()
  99. mock_popen.return_value.stdout = pipe
  100. mock_popen.return_value.wait.return_value = 1
  101. with tempfile.NamedTemporaryFile() as fd, \
  102. tempfile.TemporaryDirectory() as dir:
  103. path = fd.name
  104. dirpath = dir
  105. ret = qubesadmin.tools.qvm_template.extract_rpm(
  106. 'test-vm', path, dirpath)
  107. self.assertEqual(ret, False)
  108. self.assertEqual(mock_popen.mock_calls, [
  109. mock.call(['rpm2cpio', path], stdout=subprocess.PIPE),
  110. mock.call([
  111. 'cpio',
  112. '-idm',
  113. '-D',
  114. dirpath,
  115. './var/lib/qubes/vm-templates/test-vm/*'
  116. ], stdin=pipe, stdout=subprocess.DEVNULL),
  117. mock.call().wait()
  118. ])
  119. self.assertAllCalled()
  120. def add_new_vm_side_effect(self, *args, **kwargs):
  121. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  122. b'0\0test-vm class=TemplateVM state=Halted\n'
  123. self.app.domains.clear_cache()
  124. return self.app.domains['test-vm']
  125. @mock.patch('os.remove')
  126. @mock.patch('os.rename')
  127. @mock.patch('os.makedirs')
  128. @mock.patch('subprocess.check_call')
  129. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  130. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  131. @mock.patch('qubesadmin.tools.qvm_template.download')
  132. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  133. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  134. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  135. def test_100_install_local_success(
  136. self,
  137. mock_ts,
  138. mock_verify,
  139. mock_dl_list,
  140. mock_dl,
  141. mock_extract,
  142. mock_confirm,
  143. mock_call,
  144. mock_mkdirs,
  145. mock_rename,
  146. mock_remove):
  147. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = b'0\0'
  148. build_time = '2020-09-01 14:30:00' # 1598970600
  149. install_time = '2020-09-01 15:30:00'
  150. for key, val in [
  151. ('name', 'test-vm'),
  152. ('epoch', '2'),
  153. ('version', '4.1'),
  154. ('release', '2020'),
  155. ('reponame', '@commandline'),
  156. ('buildtime', build_time),
  157. ('installtime', install_time),
  158. ('license', 'GPL'),
  159. ('url', 'https://qubes-os.org'),
  160. ('summary', 'Summary'),
  161. ('description', 'Desc|desc')]:
  162. self.app.expected_calls[(
  163. 'test-vm',
  164. 'admin.vm.feature.Set',
  165. f'template-{key}',
  166. val.encode())] = b'0\0'
  167. mock_verify.return_value = {
  168. rpm.RPMTAG_NAME : 'qubes-template-test-vm',
  169. rpm.RPMTAG_BUILDTIME : 1598970600,
  170. rpm.RPMTAG_DESCRIPTION : 'Desc\ndesc',
  171. rpm.RPMTAG_EPOCHNUM : 2,
  172. rpm.RPMTAG_LICENSE : 'GPL',
  173. rpm.RPMTAG_RELEASE : '2020',
  174. rpm.RPMTAG_SUMMARY : 'Summary',
  175. rpm.RPMTAG_URL : 'https://qubes-os.org',
  176. rpm.RPMTAG_VERSION : '4.1'
  177. }
  178. mock_dl_list.return_value = {}
  179. mock_call.side_effect = self.add_new_vm_side_effect
  180. mock_time = mock.Mock(wraps=datetime.datetime)
  181. mock_time.now.return_value = \
  182. datetime.datetime(2020, 9, 1, 15, 30, tzinfo=datetime.timezone.utc)
  183. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  184. mock.patch('datetime.datetime', new=mock_time), \
  185. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  186. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  187. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  188. path = template_file.name
  189. args = argparse.Namespace(
  190. templates=[path],
  191. keyring='/usr/share/qubes/repo-templates/keys',
  192. nogpgcheck=False,
  193. cachedir='/var/cache/qvm-template',
  194. yes=False,
  195. allow_pv=False,
  196. pool=None
  197. )
  198. mock_tmpdir.return_value.__enter__.return_value = \
  199. '/var/tmp/qvm-template-tmpdir'
  200. qubesadmin.tools.qvm_template.install(args, self.app)
  201. # Lock file created
  202. self.assertEqual(mock_open.mock_calls, [
  203. mock.call('/var/tmp/qvm-template.lck', 'x'),
  204. mock.call().__enter__(),
  205. mock.call().__exit__(None, None, None)
  206. ])
  207. # Keyring created
  208. self.assertEqual(mock_ts.mock_calls, [
  209. mock.call('/usr/share/qubes/repo-templates/keys')
  210. ])
  211. # Package verified
  212. self.assertEqual(mock_verify.mock_calls, [
  213. mock.call(path, mock_ts('/usr/share/qubes/repo-templates/keys'),
  214. False)
  215. ])
  216. # Attempt to get download list
  217. selector = qubesadmin.tools.qvm_template.VersionSelector.LATEST
  218. self.assertEqual(mock_dl_list.mock_calls, [
  219. mock.call(args, self.app, version_selector=selector)
  220. ])
  221. # Nothing downloaded
  222. self.assertEqual(mock_dl.mock_calls, [
  223. mock.call(args, self.app, path_override='/var/cache/qvm-template',
  224. dl_list={}, suffix='.unverified', version_selector=selector)
  225. ])
  226. # Package is extracted
  227. self.assertEqual(mock_extract.mock_calls, [
  228. mock.call('test-vm', path, '/var/tmp/qvm-template-tmpdir')
  229. ])
  230. # No packages overwritten, so no confirm needed
  231. self.assertEqual(mock_confirm.mock_calls, [])
  232. # qvm-template-postprocess is called
  233. self.assertEqual(mock_call.mock_calls, [
  234. mock.call([
  235. 'qvm-template-postprocess',
  236. '--really',
  237. '--no-installed-by-rpm',
  238. 'post-install',
  239. 'test-vm',
  240. '/var/tmp/qvm-template-tmpdir'
  241. '/var/lib/qubes/vm-templates/test-vm'
  242. ])
  243. ])
  244. # Cache directory created
  245. self.assertEqual(mock_mkdirs.mock_calls, [
  246. mock.call(args.cachedir, exist_ok=True)
  247. ])
  248. # No templates downloaded, thus no renames needed
  249. self.assertEqual(mock_rename.mock_calls, [])
  250. # Lock file removed
  251. self.assertEqual(mock_remove.mock_calls, [
  252. mock.call('/var/tmp/qvm-template.lck')
  253. ])
  254. self.assertAllCalled()
  255. @mock.patch('os.remove')
  256. @mock.patch('os.rename')
  257. @mock.patch('os.makedirs')
  258. @mock.patch('subprocess.check_call')
  259. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  260. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  261. @mock.patch('qubesadmin.tools.qvm_template.download')
  262. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  263. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  264. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  265. def test_101_install_local_postprocargs_success(
  266. self,
  267. mock_ts,
  268. mock_verify,
  269. mock_dl_list,
  270. mock_dl,
  271. mock_extract,
  272. mock_confirm,
  273. mock_call,
  274. mock_mkdirs,
  275. mock_rename,
  276. mock_remove):
  277. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = b'0\0'
  278. build_time = '2020-09-01 14:30:00' # 1598970600
  279. install_time = '2020-09-01 15:30:00'
  280. for key, val in [
  281. ('name', 'test-vm'),
  282. ('epoch', '2'),
  283. ('version', '4.1'),
  284. ('release', '2020'),
  285. ('reponame', '@commandline'),
  286. ('buildtime', build_time),
  287. ('installtime', install_time),
  288. ('license', 'GPL'),
  289. ('url', 'https://qubes-os.org'),
  290. ('summary', 'Summary'),
  291. ('description', 'Desc|desc')]:
  292. self.app.expected_calls[(
  293. 'test-vm',
  294. 'admin.vm.feature.Set',
  295. f'template-{key}',
  296. val.encode())] = b'0\0'
  297. mock_verify.return_value = {
  298. rpm.RPMTAG_NAME : 'qubes-template-test-vm',
  299. rpm.RPMTAG_BUILDTIME : 1598970600,
  300. rpm.RPMTAG_DESCRIPTION : 'Desc\ndesc',
  301. rpm.RPMTAG_EPOCHNUM : 2,
  302. rpm.RPMTAG_LICENSE : 'GPL',
  303. rpm.RPMTAG_RELEASE : '2020',
  304. rpm.RPMTAG_SUMMARY : 'Summary',
  305. rpm.RPMTAG_URL : 'https://qubes-os.org',
  306. rpm.RPMTAG_VERSION : '4.1'
  307. }
  308. mock_dl_list.return_value = {}
  309. mock_call.side_effect = self.add_new_vm_side_effect
  310. mock_time = mock.Mock(wraps=datetime.datetime)
  311. mock_time.now.return_value = \
  312. datetime.datetime(2020, 9, 1, 15, 30, tzinfo=datetime.timezone.utc)
  313. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  314. mock.patch('datetime.datetime', new=mock_time), \
  315. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  316. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  317. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  318. path = template_file.name
  319. args = argparse.Namespace(
  320. templates=[path],
  321. keyring='/usr/share/qubes/repo-templates/keys',
  322. nogpgcheck=False,
  323. cachedir='/var/cache/qvm-template',
  324. yes=False,
  325. allow_pv=True,
  326. pool='my-pool'
  327. )
  328. mock_tmpdir.return_value.__enter__.return_value = \
  329. '/var/tmp/qvm-template-tmpdir'
  330. qubesadmin.tools.qvm_template.install(args, self.app)
  331. # Lock file created
  332. self.assertEqual(mock_open.mock_calls, [
  333. mock.call('/var/tmp/qvm-template.lck', 'x'),
  334. mock.call().__enter__(),
  335. mock.call().__exit__(None, None, None)
  336. ])
  337. # Keyring created
  338. self.assertEqual(mock_ts.mock_calls, [
  339. mock.call('/usr/share/qubes/repo-templates/keys')
  340. ])
  341. # Package verified
  342. self.assertEqual(mock_verify.mock_calls, [
  343. mock.call(path, mock_ts('/usr/share/qubes/repo-templates/keys'),
  344. False)
  345. ])
  346. # Attempt to get download list
  347. selector = qubesadmin.tools.qvm_template.VersionSelector.LATEST
  348. self.assertEqual(mock_dl_list.mock_calls, [
  349. mock.call(args, self.app, version_selector=selector)
  350. ])
  351. # Nothing downloaded
  352. self.assertEqual(mock_dl.mock_calls, [
  353. mock.call(args, self.app, path_override='/var/cache/qvm-template',
  354. dl_list={}, suffix='.unverified', version_selector=selector)
  355. ])
  356. # Package is extracted
  357. self.assertEqual(mock_extract.mock_calls, [
  358. mock.call('test-vm', path, '/var/tmp/qvm-template-tmpdir')
  359. ])
  360. # No packages overwritten, so no confirm needed
  361. self.assertEqual(mock_confirm.mock_calls, [])
  362. # qvm-template-postprocess is called
  363. self.assertEqual(mock_call.mock_calls, [
  364. mock.call([
  365. 'qvm-template-postprocess',
  366. '--really',
  367. '--no-installed-by-rpm',
  368. '--allow-pv',
  369. '--pool',
  370. 'my-pool',
  371. 'post-install',
  372. 'test-vm',
  373. '/var/tmp/qvm-template-tmpdir'
  374. '/var/lib/qubes/vm-templates/test-vm'
  375. ])
  376. ])
  377. # Cache directory created
  378. self.assertEqual(mock_mkdirs.mock_calls, [
  379. mock.call(args.cachedir, exist_ok=True)
  380. ])
  381. # No templates downloaded, thus no renames needed
  382. self.assertEqual(mock_rename.mock_calls, [])
  383. # Lock file removed
  384. self.assertEqual(mock_remove.mock_calls, [
  385. mock.call('/var/tmp/qvm-template.lck')
  386. ])
  387. self.assertAllCalled()
  388. @mock.patch('os.remove')
  389. @mock.patch('os.rename')
  390. @mock.patch('os.makedirs')
  391. @mock.patch('subprocess.check_call')
  392. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  393. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  394. @mock.patch('qubesadmin.tools.qvm_template.download')
  395. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  396. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  397. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  398. def test_102_install_local_badsig_fail(
  399. self,
  400. mock_ts,
  401. mock_verify,
  402. mock_dl_list,
  403. mock_dl,
  404. mock_extract,
  405. mock_confirm,
  406. mock_call,
  407. mock_mkdirs,
  408. mock_rename,
  409. mock_remove):
  410. mock_verify.return_value = None
  411. mock_time = mock.Mock(wraps=datetime.datetime)
  412. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  413. mock.patch('datetime.datetime', new=mock_time), \
  414. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  415. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  416. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  417. path = template_file.name
  418. args = argparse.Namespace(
  419. templates=[path],
  420. keyring='/usr/share/qubes/repo-templates/keys',
  421. nogpgcheck=False,
  422. cachedir='/var/cache/qvm-template',
  423. yes=False,
  424. allow_pv=False,
  425. pool=None
  426. )
  427. mock_tmpdir.return_value.__enter__.return_value = \
  428. '/var/tmp/qvm-template-tmpdir'
  429. # Should raise parser.error
  430. with self.assertRaises(SystemExit):
  431. qubesadmin.tools.qvm_template.install(args, self.app)
  432. # Lock file created
  433. self.assertEqual(mock_open.mock_calls, [
  434. mock.call('/var/tmp/qvm-template.lck', 'x'),
  435. mock.call().__enter__(),
  436. mock.call().__exit__(None, None, None)
  437. ])
  438. # Check error message
  439. self.assertTrue('verification failed' in mock_err.getvalue())
  440. # Keyring created
  441. self.assertEqual(mock_ts.mock_calls, [
  442. mock.call('/usr/share/qubes/repo-templates/keys')
  443. ])
  444. # Package verified
  445. self.assertEqual(mock_verify.mock_calls, [
  446. mock.call(path, mock_ts('/usr/share/qubes/repo-templates/keys'),
  447. False)
  448. ])
  449. # Should not be executed:
  450. self.assertEqual(mock_dl_list.mock_calls, [])
  451. self.assertEqual(mock_dl.mock_calls, [])
  452. self.assertEqual(mock_extract.mock_calls, [])
  453. self.assertEqual(mock_confirm.mock_calls, [])
  454. self.assertEqual(mock_call.mock_calls, [])
  455. self.assertEqual(mock_rename.mock_calls, [])
  456. # Lock file removed
  457. self.assertEqual(mock_remove.mock_calls, [
  458. mock.call('/var/tmp/qvm-template.lck')
  459. ])
  460. self.assertAllCalled()
  461. @mock.patch('os.remove')
  462. @mock.patch('os.rename')
  463. @mock.patch('os.makedirs')
  464. @mock.patch('subprocess.check_call')
  465. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  466. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  467. @mock.patch('qubesadmin.tools.qvm_template.download')
  468. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  469. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  470. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  471. def test_103_install_local_exists_fail(
  472. self,
  473. mock_ts,
  474. mock_verify,
  475. mock_dl_list,
  476. mock_dl,
  477. mock_extract,
  478. mock_confirm,
  479. mock_call,
  480. mock_mkdirs,
  481. mock_rename,
  482. mock_remove):
  483. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  484. b'0\0test-vm class=TemplateVM state=Halted\n'
  485. mock_verify.return_value = {
  486. rpm.RPMTAG_NAME : 'qubes-template-test-vm',
  487. rpm.RPMTAG_BUILDTIME : 1598970600,
  488. rpm.RPMTAG_DESCRIPTION : 'Desc\ndesc',
  489. rpm.RPMTAG_EPOCHNUM : 2,
  490. rpm.RPMTAG_LICENSE : 'GPL',
  491. rpm.RPMTAG_RELEASE : '2020',
  492. rpm.RPMTAG_SUMMARY : 'Summary',
  493. rpm.RPMTAG_URL : 'https://qubes-os.org',
  494. rpm.RPMTAG_VERSION : '4.1'
  495. }
  496. mock_dl_list.return_value = {}
  497. mock_time = mock.Mock(wraps=datetime.datetime)
  498. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  499. mock.patch('datetime.datetime', new=mock_time), \
  500. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  501. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  502. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  503. path = template_file.name
  504. args = argparse.Namespace(
  505. templates=[path],
  506. keyring='/usr/share/qubes/repo-templates/keys',
  507. nogpgcheck=False,
  508. cachedir='/var/cache/qvm-template',
  509. yes=False,
  510. allow_pv=False,
  511. pool=None
  512. )
  513. mock_tmpdir.return_value.__enter__.return_value = \
  514. '/var/tmp/qvm-template-tmpdir'
  515. qubesadmin.tools.qvm_template.install(args, self.app)
  516. # Lock file created
  517. self.assertEqual(mock_open.mock_calls, [
  518. mock.call('/var/tmp/qvm-template.lck', 'x'),
  519. mock.call().__enter__(),
  520. mock.call().__exit__(None, None, None)
  521. ])
  522. # Check warning message
  523. self.assertTrue('already installed' in mock_err.getvalue())
  524. # Keyring created
  525. self.assertEqual(mock_ts.mock_calls, [
  526. mock.call('/usr/share/qubes/repo-templates/keys')
  527. ])
  528. # Package verified
  529. self.assertEqual(mock_verify.mock_calls, [
  530. mock.call(path, mock_ts('/usr/share/qubes/repo-templates/keys'),
  531. False)
  532. ])
  533. # Attempt to get download list
  534. selector = qubesadmin.tools.qvm_template.VersionSelector.LATEST
  535. self.assertEqual(mock_dl_list.mock_calls, [
  536. mock.call(args, self.app, version_selector=selector)
  537. ])
  538. # Nothing downloaded
  539. self.assertEqual(mock_dl.mock_calls, [
  540. mock.call(args, self.app, path_override='/var/cache/qvm-template',
  541. dl_list={}, suffix='.unverified', version_selector=selector)
  542. ])
  543. # Should not be executed:
  544. self.assertEqual(mock_extract.mock_calls, [])
  545. self.assertEqual(mock_confirm.mock_calls, [])
  546. self.assertEqual(mock_call.mock_calls, [])
  547. self.assertEqual(mock_rename.mock_calls, [])
  548. # Lock file removed
  549. self.assertEqual(mock_remove.mock_calls, [
  550. mock.call('/var/tmp/qvm-template.lck')
  551. ])
  552. self.assertAllCalled()
  553. @mock.patch('os.remove')
  554. @mock.patch('os.rename')
  555. @mock.patch('os.makedirs')
  556. @mock.patch('subprocess.check_call')
  557. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  558. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  559. @mock.patch('qubesadmin.tools.qvm_template.download')
  560. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  561. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  562. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  563. def test_104_install_local_badpkgname_fail(
  564. self,
  565. mock_ts,
  566. mock_verify,
  567. mock_dl_list,
  568. mock_dl,
  569. mock_extract,
  570. mock_confirm,
  571. mock_call,
  572. mock_mkdirs,
  573. mock_rename,
  574. mock_remove):
  575. mock_verify.return_value = {
  576. rpm.RPMTAG_NAME : 'Xqubes-template-test-vm',
  577. rpm.RPMTAG_BUILDTIME : 1598970600,
  578. rpm.RPMTAG_DESCRIPTION : 'Desc\ndesc',
  579. rpm.RPMTAG_EPOCHNUM : 2,
  580. rpm.RPMTAG_LICENSE : 'GPL',
  581. rpm.RPMTAG_RELEASE : '2020',
  582. rpm.RPMTAG_SUMMARY : 'Summary',
  583. rpm.RPMTAG_URL : 'https://qubes-os.org',
  584. rpm.RPMTAG_VERSION : '4.1'
  585. }
  586. mock_time = mock.Mock(wraps=datetime.datetime)
  587. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  588. mock.patch('datetime.datetime', new=mock_time), \
  589. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  590. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  591. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  592. path = template_file.name
  593. args = argparse.Namespace(
  594. templates=[path],
  595. keyring='/usr/share/qubes/repo-templates/keys',
  596. nogpgcheck=False,
  597. cachedir='/var/cache/qvm-template',
  598. yes=False,
  599. allow_pv=False,
  600. pool=None
  601. )
  602. mock_tmpdir.return_value.__enter__.return_value = \
  603. '/var/tmp/qvm-template-tmpdir'
  604. with self.assertRaises(SystemExit):
  605. qubesadmin.tools.qvm_template.install(args, self.app)
  606. # Lock file created
  607. self.assertEqual(mock_open.mock_calls, [
  608. mock.call('/var/tmp/qvm-template.lck', 'x'),
  609. mock.call().__enter__(),
  610. mock.call().__exit__(None, None, None)
  611. ])
  612. # Check error message
  613. self.assertTrue('Illegal package name' in mock_err.getvalue())
  614. # Keyring created
  615. self.assertEqual(mock_ts.mock_calls, [
  616. mock.call('/usr/share/qubes/repo-templates/keys')
  617. ])
  618. # Package verified
  619. self.assertEqual(mock_verify.mock_calls, [
  620. mock.call(path, mock_ts('/usr/share/qubes/repo-templates/keys'),
  621. False)
  622. ])
  623. # Should not be executed:
  624. self.assertEqual(mock_dl_list.mock_calls, [])
  625. self.assertEqual(mock_dl.mock_calls, [])
  626. self.assertEqual(mock_extract.mock_calls, [])
  627. self.assertEqual(mock_confirm.mock_calls, [])
  628. self.assertEqual(mock_call.mock_calls, [])
  629. self.assertEqual(mock_rename.mock_calls, [])
  630. # Lock file removed
  631. self.assertEqual(mock_remove.mock_calls, [
  632. mock.call('/var/tmp/qvm-template.lck')
  633. ])
  634. self.assertAllCalled()
  635. @mock.patch('os.rename')
  636. @mock.patch('os.makedirs')
  637. @mock.patch('subprocess.check_call')
  638. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  639. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  640. @mock.patch('qubesadmin.tools.qvm_template.download')
  641. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  642. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  643. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  644. def test_105_install_local_existinginstance_fail(
  645. self,
  646. mock_ts,
  647. mock_verify,
  648. mock_dl_list,
  649. mock_dl,
  650. mock_extract,
  651. mock_confirm,
  652. mock_call,
  653. mock_mkdirs,
  654. mock_rename):
  655. mock_time = mock.Mock(wraps=datetime.datetime)
  656. with mock.patch('datetime.datetime', new=mock_time), \
  657. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  658. mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  659. tempfile.NamedTemporaryFile(suffix='.rpm') as template_file:
  660. path = template_file.name
  661. args = argparse.Namespace(
  662. templates=[path],
  663. keyring='/usr/share/qubes/repo-templates/keys',
  664. nogpgcheck=False,
  665. cachedir='/var/cache/qvm-template',
  666. yes=False,
  667. allow_pv=False,
  668. pool=None
  669. )
  670. mock_tmpdir.return_value.__enter__.return_value = \
  671. '/var/tmp/qvm-template-tmpdir'
  672. pathlib.Path('/var/tmp/qvm-template.lck').touch()
  673. try:
  674. with mock.patch('os.remove') as mock_remove:
  675. with self.assertRaises(SystemExit):
  676. qubesadmin.tools.qvm_template.install(args, self.app)
  677. self.assertEqual(mock_remove.mock_calls, [])
  678. finally:
  679. # Lock file not removed
  680. self.assertTrue(os.path.exists('/var/tmp/qvm-template.lck'))
  681. os.remove('/var/tmp/qvm-template.lck')
  682. # Check error message
  683. self.assertTrue('another instance of qvm-template is running' \
  684. in mock_err.getvalue())
  685. # Should not be executed:
  686. self.assertEqual(mock_ts.mock_calls, [])
  687. self.assertEqual(mock_verify.mock_calls, [])
  688. self.assertEqual(mock_dl_list.mock_calls, [])
  689. self.assertEqual(mock_dl.mock_calls, [])
  690. self.assertEqual(mock_extract.mock_calls, [])
  691. self.assertEqual(mock_confirm.mock_calls, [])
  692. self.assertEqual(mock_call.mock_calls, [])
  693. self.assertEqual(mock_rename.mock_calls, [])
  694. self.assertAllCalled()
  695. @mock.patch('os.remove')
  696. @mock.patch('os.rename')
  697. @mock.patch('os.makedirs')
  698. @mock.patch('subprocess.check_call')
  699. @mock.patch('qubesadmin.tools.qvm_template.confirm_action')
  700. @mock.patch('qubesadmin.tools.qvm_template.extract_rpm')
  701. @mock.patch('qubesadmin.tools.qvm_template.download')
  702. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  703. @mock.patch('qubesadmin.tools.qvm_template.verify_rpm')
  704. @mock.patch('qubesadmin.tools.qvm_template.rpm_transactionset')
  705. def test_106_install_local_badpath_fail(
  706. self,
  707. mock_ts,
  708. mock_verify,
  709. mock_dl_list,
  710. mock_dl,
  711. mock_extract,
  712. mock_confirm,
  713. mock_call,
  714. mock_mkdirs,
  715. mock_rename,
  716. mock_remove):
  717. mock_time = mock.Mock(wraps=datetime.datetime)
  718. with mock.patch('builtins.open', mock.mock_open()) as mock_open, \
  719. mock.patch('datetime.datetime', new=mock_time), \
  720. mock.patch('tempfile.TemporaryDirectory') as mock_tmpdir, \
  721. mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  722. path = '/var/tmp/ShOulD-NoT-ExIsT.rpm'
  723. args = argparse.Namespace(
  724. templates=[path],
  725. keyring='/usr/share/qubes/repo-templates/keys',
  726. nogpgcheck=False,
  727. cachedir='/var/cache/qvm-template',
  728. yes=False,
  729. allow_pv=False,
  730. pool=None
  731. )
  732. mock_tmpdir.return_value.__enter__.return_value = \
  733. '/var/tmp/qvm-template-tmpdir'
  734. with self.assertRaises(SystemExit):
  735. qubesadmin.tools.qvm_template.install(args, self.app)
  736. # Lock file created
  737. self.assertEqual(mock_open.mock_calls, [
  738. mock.call('/var/tmp/qvm-template.lck', 'x'),
  739. mock.call().__enter__(),
  740. mock.call().__exit__(None, None, None)
  741. ])
  742. # Check error message
  743. self.assertTrue(f"RPM file '{path}' not found" \
  744. in mock_err.getvalue())
  745. # Keyring created
  746. self.assertEqual(mock_ts.mock_calls, [
  747. mock.call('/usr/share/qubes/repo-templates/keys')
  748. ])
  749. # Should not be executed:
  750. self.assertEqual(mock_verify.mock_calls, [])
  751. self.assertEqual(mock_dl_list.mock_calls, [])
  752. self.assertEqual(mock_dl.mock_calls, [])
  753. self.assertEqual(mock_extract.mock_calls, [])
  754. self.assertEqual(mock_confirm.mock_calls, [])
  755. self.assertEqual(mock_call.mock_calls, [])
  756. self.assertEqual(mock_rename.mock_calls, [])
  757. # Lock file removed
  758. self.assertEqual(mock_remove.mock_calls, [
  759. mock.call('/var/tmp/qvm-template.lck')
  760. ])
  761. self.assertAllCalled()
  762. def test_110_qrexec_payload_refresh_success(self):
  763. with tempfile.NamedTemporaryFile() as repo_conf1, \
  764. tempfile.NamedTemporaryFile() as repo_conf2:
  765. repo_str1 = \
  766. '''[qubes-templates-itl]
  767. name = Qubes Templates repository
  768. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  769. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  770. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  771. enabled = 1
  772. fastestmirror = 1
  773. metadata_expire = 7d
  774. gpgcheck = 1
  775. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  776. '''
  777. repo_str2 = \
  778. '''[qubes-templates-itl-testing]
  779. name = Qubes Templates repository
  780. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl-testing
  781. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl-testing
  782. metalink = https://yum.qubes-os.org/r$releasever/templates-itl-testing/repodata/repomd.xml.metalink
  783. enabled = 0
  784. fastestmirror = 1
  785. gpgcheck = 1
  786. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  787. '''
  788. repo_conf1.write(repo_str1.encode())
  789. repo_conf1.flush()
  790. repo_conf2.write(repo_str2.encode())
  791. repo_conf2.flush()
  792. args = argparse.Namespace(
  793. enablerepo=['repo1', 'repo2'],
  794. disablerepo=['repo3', 'repo4', 'repo5'],
  795. repoid=[],
  796. releasever='4.1',
  797. repo_files=[repo_conf1.name, repo_conf2.name]
  798. )
  799. res = qubesadmin.tools.qvm_template.qrexec_payload(args, self.app,
  800. 'qubes-template-fedora-32', True)
  801. self.assertEqual(res,
  802. '''--enablerepo=repo1
  803. --enablerepo=repo2
  804. --disablerepo=repo3
  805. --disablerepo=repo4
  806. --disablerepo=repo5
  807. --refresh
  808. --releasever=4.1
  809. qubes-template-fedora-32
  810. ---
  811. ''' + repo_str1 + '\n' + repo_str2 + '\n')
  812. self.assertAllCalled()
  813. def test_111_qrexec_payload_norefresh_success(self):
  814. with tempfile.NamedTemporaryFile() as repo_conf1:
  815. repo_str1 = \
  816. '''[qubes-templates-itl]
  817. name = Qubes Templates repository
  818. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  819. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  820. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  821. enabled = 1
  822. fastestmirror = 1
  823. metadata_expire = 7d
  824. gpgcheck = 1
  825. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  826. '''
  827. repo_conf1.write(repo_str1.encode())
  828. repo_conf1.flush()
  829. args = argparse.Namespace(
  830. enablerepo=[],
  831. disablerepo=[],
  832. repoid=['repo1', 'repo2'],
  833. releasever='4.1',
  834. repo_files=[repo_conf1.name]
  835. )
  836. res = qubesadmin.tools.qvm_template.qrexec_payload(args, self.app,
  837. 'qubes-template-fedora-32', False)
  838. self.assertEqual(res,
  839. '''--repoid=repo1
  840. --repoid=repo2
  841. --releasever=4.1
  842. qubes-template-fedora-32
  843. ---
  844. ''' + repo_str1 + '\n')
  845. self.assertAllCalled()
  846. def test_112_qrexec_payload_specnewline_fail(self):
  847. with tempfile.NamedTemporaryFile() as repo_conf1:
  848. repo_str1 = \
  849. '''[qubes-templates-itl]
  850. name = Qubes Templates repository
  851. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  852. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  853. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  854. enabled = 1
  855. fastestmirror = 1
  856. metadata_expire = 7d
  857. gpgcheck = 1
  858. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  859. '''
  860. repo_conf1.write(repo_str1.encode())
  861. repo_conf1.flush()
  862. args = argparse.Namespace(
  863. enablerepo=[],
  864. disablerepo=[],
  865. repoid=['repo1', 'repo2'],
  866. releasever='4.1',
  867. repo_files=[repo_conf1.name]
  868. )
  869. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  870. with self.assertRaises(SystemExit):
  871. qubesadmin.tools.qvm_template.qrexec_payload(args,
  872. self.app, 'qubes-template-fedora\n-32', False)
  873. # Check error message
  874. self.assertTrue('Malformed template name'
  875. in mock_err.getvalue())
  876. self.assertTrue("argument should not contain '\\n'"
  877. in mock_err.getvalue())
  878. self.assertAllCalled()
  879. def test_113_qrexec_payload_enablereponewline_fail(self):
  880. with tempfile.NamedTemporaryFile() as repo_conf1:
  881. repo_str1 = \
  882. '''[qubes-templates-itl]
  883. name = Qubes Templates repository
  884. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  885. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  886. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  887. enabled = 1
  888. fastestmirror = 1
  889. metadata_expire = 7d
  890. gpgcheck = 1
  891. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  892. '''
  893. repo_conf1.write(repo_str1.encode())
  894. repo_conf1.flush()
  895. args = argparse.Namespace(
  896. enablerepo=['repo\n0'],
  897. disablerepo=[],
  898. repoid=['repo1', 'repo2'],
  899. releasever='4.1',
  900. repo_files=[repo_conf1.name]
  901. )
  902. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  903. with self.assertRaises(SystemExit):
  904. qubesadmin.tools.qvm_template.qrexec_payload(args,
  905. self.app, 'qubes-template-fedora-32', False)
  906. # Check error message
  907. self.assertTrue('Malformed --enablerepo'
  908. in mock_err.getvalue())
  909. self.assertTrue("argument should not contain '\\n'"
  910. in mock_err.getvalue())
  911. self.assertAllCalled()
  912. def test_114_qrexec_payload_disablereponewline_fail(self):
  913. with tempfile.NamedTemporaryFile() as repo_conf1:
  914. repo_str1 = \
  915. '''[qubes-templates-itl]
  916. name = Qubes Templates repository
  917. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  918. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  919. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  920. enabled = 1
  921. fastestmirror = 1
  922. metadata_expire = 7d
  923. gpgcheck = 1
  924. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  925. '''
  926. repo_conf1.write(repo_str1.encode())
  927. repo_conf1.flush()
  928. args = argparse.Namespace(
  929. enablerepo=[],
  930. disablerepo=['repo\n0'],
  931. repoid=['repo1', 'repo2'],
  932. releasever='4.1',
  933. repo_files=[repo_conf1.name]
  934. )
  935. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  936. with self.assertRaises(SystemExit):
  937. qubesadmin.tools.qvm_template.qrexec_payload(args,
  938. self.app, 'qubes-template-fedora-32', False)
  939. # Check error message
  940. self.assertTrue('Malformed --disablerepo'
  941. in mock_err.getvalue())
  942. self.assertTrue("argument should not contain '\\n'"
  943. in mock_err.getvalue())
  944. self.assertAllCalled()
  945. def test_115_qrexec_payload_repoidnewline_fail(self):
  946. with tempfile.NamedTemporaryFile() as repo_conf1:
  947. repo_str1 = \
  948. '''[qubes-templates-itl]
  949. name = Qubes Templates repository
  950. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  951. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  952. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  953. enabled = 1
  954. fastestmirror = 1
  955. metadata_expire = 7d
  956. gpgcheck = 1
  957. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  958. '''
  959. repo_conf1.write(repo_str1.encode())
  960. repo_conf1.flush()
  961. args = argparse.Namespace(
  962. enablerepo=[],
  963. disablerepo=[],
  964. repoid=['repo\n1', 'repo2'],
  965. releasever='4.1',
  966. repo_files=[repo_conf1.name]
  967. )
  968. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  969. with self.assertRaises(SystemExit):
  970. qubesadmin.tools.qvm_template.qrexec_payload(args,
  971. self.app, 'qubes-template-fedora-32', False)
  972. # Check error message
  973. self.assertTrue('Malformed --repoid'
  974. in mock_err.getvalue())
  975. self.assertTrue("argument should not contain '\\n'"
  976. in mock_err.getvalue())
  977. self.assertAllCalled()
  978. def test_116_qrexec_payload_releasevernewline_fail(self):
  979. with tempfile.NamedTemporaryFile() as repo_conf1:
  980. repo_str1 = \
  981. '''[qubes-templates-itl]
  982. name = Qubes Templates repository
  983. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  984. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  985. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  986. enabled = 1
  987. fastestmirror = 1
  988. metadata_expire = 7d
  989. gpgcheck = 1
  990. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  991. '''
  992. repo_conf1.write(repo_str1.encode())
  993. repo_conf1.flush()
  994. args = argparse.Namespace(
  995. enablerepo=[],
  996. disablerepo=[],
  997. repoid=['repo1', 'repo2'],
  998. releasever='4\n.1',
  999. repo_files=[repo_conf1.name]
  1000. )
  1001. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1002. with self.assertRaises(SystemExit):
  1003. qubesadmin.tools.qvm_template.qrexec_payload(args,
  1004. self.app, 'qubes-template-fedora-32', False)
  1005. # Check error message
  1006. self.assertTrue('Malformed --releasever'
  1007. in mock_err.getvalue())
  1008. self.assertTrue("argument should not contain '\\n'"
  1009. in mock_err.getvalue())
  1010. self.assertAllCalled()
  1011. def test_117_qrexec_payload_specdash_fail(self):
  1012. with tempfile.NamedTemporaryFile() as repo_conf1:
  1013. repo_str1 = \
  1014. '''[qubes-templates-itl]
  1015. name = Qubes Templates repository
  1016. #baseurl = https://yum.qubes-os.org/r$releasever/templates-itl
  1017. #baseurl = http://yum.qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion/r$releasever/templates-itl
  1018. metalink = https://yum.qubes-os.org/r$releasever/templates-itl/repodata/repomd.xml.metalink
  1019. enabled = 1
  1020. fastestmirror = 1
  1021. metadata_expire = 7d
  1022. gpgcheck = 1
  1023. gpgkey = file:///usr/share/qubes/repo-templates/keys/RPM-GPG-KEY-qubes-$releasever-primary
  1024. '''
  1025. repo_conf1.write(repo_str1.encode())
  1026. repo_conf1.flush()
  1027. args = argparse.Namespace(
  1028. enablerepo=[],
  1029. disablerepo=[],
  1030. repoid=['repo1', 'repo2'],
  1031. releasever='4.1',
  1032. repo_files=[repo_conf1.name]
  1033. )
  1034. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1035. with self.assertRaises(SystemExit):
  1036. qubesadmin.tools.qvm_template.qrexec_payload(args,
  1037. self.app, '---', False)
  1038. # Check error message
  1039. self.assertTrue('Malformed template name'
  1040. in mock_err.getvalue())
  1041. self.assertTrue("argument should not be '---'"
  1042. in mock_err.getvalue())
  1043. self.assertAllCalled()
  1044. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1045. def test_120_qrexec_repoquery_success(self, mock_payload):
  1046. args = argparse.Namespace(updatevm='test-vm')
  1047. mock_payload.return_value = 'str1\nstr2'
  1048. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1049. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1050. self.app.expected_service_calls[
  1051. ('test-vm', 'qubes.TemplateSearch')] = \
  1052. b'''qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template\n for fedora-32\n|
  1053. qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1054. '''
  1055. res = qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1056. 'qubes-template-fedora-32')
  1057. self.assertEqual(res, [
  1058. qubesadmin.tools.qvm_template.Template(
  1059. 'fedora-32',
  1060. '0',
  1061. '4.1',
  1062. '20200101',
  1063. 'qubes-templates-itl',
  1064. 1048576,
  1065. datetime.datetime(2020, 1, 23, 4, 56),
  1066. 'GPL',
  1067. 'https://qubes-os.org',
  1068. 'Qubes template for fedora-32',
  1069. 'Qubes template\n for fedora-32\n'
  1070. ),
  1071. qubesadmin.tools.qvm_template.Template(
  1072. 'fedora-32',
  1073. '1',
  1074. '4.2',
  1075. '20200201',
  1076. 'qubes-templates-itl-testing',
  1077. 2048576,
  1078. datetime.datetime(2020, 2, 23, 4, 56),
  1079. 'GPLv2',
  1080. 'https://qubes-os.org/?',
  1081. 'Qubes template for fedora-32 v2',
  1082. 'Qubes template\n for fedora-32 v2\n'
  1083. )
  1084. ])
  1085. self.assertEqual(self.app.service_calls, [
  1086. ('test-vm', 'qubes.TemplateSearch',
  1087. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1088. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1089. ])
  1090. self.assertEqual(mock_payload.mock_calls, [
  1091. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1092. ])
  1093. self.assertAllCalled()
  1094. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1095. def test_121_qrexec_repoquery_refresh_success(self, mock_payload):
  1096. args = argparse.Namespace(updatevm='test-vm')
  1097. mock_payload.return_value = 'str1\nstr2'
  1098. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1099. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1100. self.app.expected_service_calls[
  1101. ('test-vm', 'qubes.TemplateSearch')] = \
  1102. b'''qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template\n for fedora-32\n|
  1103. qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1104. '''
  1105. res = qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1106. 'qubes-template-fedora-32', True)
  1107. self.assertEqual(res, [
  1108. qubesadmin.tools.qvm_template.Template(
  1109. 'fedora-32',
  1110. '0',
  1111. '4.1',
  1112. '20200101',
  1113. 'qubes-templates-itl',
  1114. 1048576,
  1115. datetime.datetime(2020, 1, 23, 4, 56),
  1116. 'GPL',
  1117. 'https://qubes-os.org',
  1118. 'Qubes template for fedora-32',
  1119. 'Qubes template\n for fedora-32\n'
  1120. ),
  1121. qubesadmin.tools.qvm_template.Template(
  1122. 'fedora-32',
  1123. '1',
  1124. '4.2',
  1125. '20200201',
  1126. 'qubes-templates-itl-testing',
  1127. 2048576,
  1128. datetime.datetime(2020, 2, 23, 4, 56),
  1129. 'GPLv2',
  1130. 'https://qubes-os.org/?',
  1131. 'Qubes template for fedora-32 v2',
  1132. 'Qubes template\n for fedora-32 v2\n'
  1133. )
  1134. ])
  1135. self.assertEqual(self.app.service_calls, [
  1136. ('test-vm', 'qubes.TemplateSearch',
  1137. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1138. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1139. ])
  1140. self.assertEqual(mock_payload.mock_calls, [
  1141. mock.call(args, self.app, 'qubes-template-fedora-32', True)
  1142. ])
  1143. self.assertAllCalled()
  1144. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1145. def test_122_qrexec_repoquery_ignorenonspec_success(self, mock_payload):
  1146. args = argparse.Namespace(updatevm='test-vm')
  1147. mock_payload.return_value = 'str1\nstr2'
  1148. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1149. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1150. self.app.expected_service_calls[
  1151. ('test-vm', 'qubes.TemplateSearch')] = \
  1152. b'''qubes-template-debian-10|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for debian-10|Qubes template for debian-10\n|
  1153. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1154. '''
  1155. res = qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1156. 'qubes-template-fedora-32')
  1157. self.assertEqual(res, [
  1158. qubesadmin.tools.qvm_template.Template(
  1159. 'fedora-32',
  1160. '0',
  1161. '4.1',
  1162. '20200101',
  1163. 'qubes-templates-itl',
  1164. 1048576,
  1165. datetime.datetime(2020, 1, 23, 4, 56),
  1166. 'GPL',
  1167. 'https://qubes-os.org',
  1168. 'Qubes template for fedora-32',
  1169. 'Qubes template for fedora-32\n'
  1170. )
  1171. ])
  1172. self.assertEqual(self.app.service_calls, [
  1173. ('test-vm', 'qubes.TemplateSearch',
  1174. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1175. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1176. ])
  1177. self.assertEqual(mock_payload.mock_calls, [
  1178. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1179. ])
  1180. self.assertAllCalled()
  1181. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1182. def test_123_qrexec_repoquery_ignorebadname_success(self, mock_payload):
  1183. args = argparse.Namespace(updatevm='test-vm')
  1184. mock_payload.return_value = 'str1\nstr2'
  1185. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1186. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1187. self.app.expected_service_calls[
  1188. ('test-vm', 'qubes.TemplateSearch')] = \
  1189. b'''template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1190. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1191. '''
  1192. res = qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1193. 'qubes-template-fedora-32')
  1194. self.assertEqual(res, [
  1195. qubesadmin.tools.qvm_template.Template(
  1196. 'fedora-32',
  1197. '0',
  1198. '4.1',
  1199. '20200101',
  1200. 'qubes-templates-itl',
  1201. 1048576,
  1202. datetime.datetime(2020, 1, 23, 4, 56),
  1203. 'GPL',
  1204. 'https://qubes-os.org',
  1205. 'Qubes template for fedora-32',
  1206. 'Qubes template for fedora-32\n'
  1207. )
  1208. ])
  1209. self.assertEqual(self.app.service_calls, [
  1210. ('test-vm', 'qubes.TemplateSearch',
  1211. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1212. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1213. ])
  1214. self.assertEqual(mock_payload.mock_calls, [
  1215. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1216. ])
  1217. self.assertAllCalled()
  1218. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1219. def test_124_qrexec_repoquery_searchfail_fail(self, mock_payload):
  1220. args = argparse.Namespace(updatevm='test-vm')
  1221. mock_payload.return_value = 'str1\nstr2'
  1222. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1223. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1224. with mock.patch('qubesadmin.tests.TestProcess.wait') \
  1225. as mock_wait:
  1226. mock_wait.return_value = 1
  1227. with self.assertRaises(ConnectionError):
  1228. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1229. 'qubes-template-fedora-32')
  1230. self.assertEqual(self.app.service_calls, [
  1231. ('test-vm', 'qubes.TemplateSearch',
  1232. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1233. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1234. ])
  1235. self.assertEqual(mock_payload.mock_calls, [
  1236. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1237. ])
  1238. self.assertAllCalled()
  1239. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1240. def test_125_qrexec_repoquery_extrafield_fail(self, mock_payload):
  1241. args = argparse.Namespace(updatevm='test-vm')
  1242. mock_payload.return_value = 'str1\nstr2'
  1243. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1244. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1245. self.app.expected_service_calls[
  1246. ('test-vm', 'qubes.TemplateSearch')] = \
  1247. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Extra field|Qubes template\n for fedora-32 v2\n|
  1248. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1249. '''
  1250. with self.assertRaisesRegex(ConnectionError,
  1251. "unexpected data format"):
  1252. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1253. 'qubes-template-fedora-32')
  1254. self.assertEqual(self.app.service_calls, [
  1255. ('test-vm', 'qubes.TemplateSearch',
  1256. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1257. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1258. ])
  1259. self.assertEqual(mock_payload.mock_calls, [
  1260. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1261. ])
  1262. self.assertAllCalled()
  1263. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1264. def test_125_qrexec_repoquery_missingfield_fail(self, mock_payload):
  1265. args = argparse.Namespace(updatevm='test-vm')
  1266. mock_payload.return_value = 'str1\nstr2'
  1267. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1268. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1269. self.app.expected_service_calls[
  1270. ('test-vm', 'qubes.TemplateSearch')] = \
  1271. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1272. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1273. '''
  1274. with self.assertRaisesRegex(ConnectionError,
  1275. "unexpected data format"):
  1276. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1277. 'qubes-template-fedora-32')
  1278. self.assertEqual(self.app.service_calls, [
  1279. ('test-vm', 'qubes.TemplateSearch',
  1280. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1281. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1282. ])
  1283. self.assertEqual(mock_payload.mock_calls, [
  1284. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1285. ])
  1286. self.assertAllCalled()
  1287. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1288. def test_126_qrexec_repoquery_badfieldname_fail(self, mock_payload):
  1289. args = argparse.Namespace(updatevm='test-vm')
  1290. mock_payload.return_value = 'str1\nstr2'
  1291. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1292. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1293. self.app.expected_service_calls[
  1294. ('test-vm', 'qubes.TemplateSearch')] = \
  1295. b'''qubes-template-fedora-(32)|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1296. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1297. '''
  1298. with self.assertRaisesRegex(ConnectionError,
  1299. "unexpected data format"):
  1300. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1301. 'qubes-template-fedora-32')
  1302. self.assertEqual(self.app.service_calls, [
  1303. ('test-vm', 'qubes.TemplateSearch',
  1304. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1305. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1306. ])
  1307. self.assertEqual(mock_payload.mock_calls, [
  1308. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1309. ])
  1310. self.assertAllCalled()
  1311. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1312. def test_126_qrexec_repoquery_badfieldepoch_fail(self, mock_payload):
  1313. args = argparse.Namespace(updatevm='test-vm')
  1314. mock_payload.return_value = 'str1\nstr2'
  1315. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1316. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1317. self.app.expected_service_calls[
  1318. ('test-vm', 'qubes.TemplateSearch')] = \
  1319. b'''qubes-template-fedora-32|!1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1320. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1321. '''
  1322. with self.assertRaisesRegex(ConnectionError,
  1323. "unexpected data format"):
  1324. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1325. 'qubes-template-fedora-32')
  1326. self.assertEqual(self.app.service_calls, [
  1327. ('test-vm', 'qubes.TemplateSearch',
  1328. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1329. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1330. ])
  1331. self.assertEqual(mock_payload.mock_calls, [
  1332. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1333. ])
  1334. self.assertAllCalled()
  1335. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1336. def test_126_qrexec_repoquery_badfieldreponame_fail(self, mock_payload):
  1337. args = argparse.Namespace(updatevm='test-vm')
  1338. mock_payload.return_value = 'str1\nstr2'
  1339. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1340. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1341. self.app.expected_service_calls[
  1342. ('test-vm', 'qubes.TemplateSearch')] = \
  1343. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-<testing>|2048576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1344. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1345. '''
  1346. with self.assertRaisesRegex(ConnectionError,
  1347. "unexpected data format"):
  1348. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1349. 'qubes-template-fedora-32')
  1350. self.assertEqual(self.app.service_calls, [
  1351. ('test-vm', 'qubes.TemplateSearch',
  1352. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1353. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1354. ])
  1355. self.assertEqual(mock_payload.mock_calls, [
  1356. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1357. ])
  1358. self.assertAllCalled()
  1359. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1360. def test_126_qrexec_repoquery_badfielddlsize_fail(self, mock_payload):
  1361. args = argparse.Namespace(updatevm='test-vm')
  1362. mock_payload.return_value = 'str1\nstr2'
  1363. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1364. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1365. self.app.expected_service_calls[
  1366. ('test-vm', 'qubes.TemplateSearch')] = \
  1367. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048a576|2020-02-23 04:56|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1368. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1369. '''
  1370. with self.assertRaisesRegex(ConnectionError,
  1371. "unexpected data format"):
  1372. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1373. 'qubes-template-fedora-32')
  1374. self.assertEqual(self.app.service_calls, [
  1375. ('test-vm', 'qubes.TemplateSearch',
  1376. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1377. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1378. ])
  1379. self.assertEqual(mock_payload.mock_calls, [
  1380. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1381. ])
  1382. self.assertAllCalled()
  1383. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1384. def test_126_qrexec_repoquery_badfielddate_fail(self, mock_payload):
  1385. args = argparse.Namespace(updatevm='test-vm')
  1386. mock_payload.return_value = 'str1\nstr2'
  1387. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1388. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1389. self.app.expected_service_calls[
  1390. ('test-vm', 'qubes.TemplateSearch')] = \
  1391. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23|GPLv2|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1392. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1393. '''
  1394. with self.assertRaisesRegex(ConnectionError,
  1395. "unexpected data format"):
  1396. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1397. 'qubes-template-fedora-32')
  1398. self.assertEqual(self.app.service_calls, [
  1399. ('test-vm', 'qubes.TemplateSearch',
  1400. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1401. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1402. ])
  1403. self.assertEqual(mock_payload.mock_calls, [
  1404. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1405. ])
  1406. self.assertAllCalled()
  1407. @mock.patch('qubesadmin.tools.qvm_template.qrexec_payload')
  1408. def test_126_qrexec_repoquery_license_fail(self, mock_payload):
  1409. args = argparse.Namespace(updatevm='test-vm')
  1410. mock_payload.return_value = 'str1\nstr2'
  1411. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1412. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1413. self.app.expected_service_calls[
  1414. ('test-vm', 'qubes.TemplateSearch')] = \
  1415. b'''qubes-template-fedora-32|1|4.2|20200201|qubes-templates-itl-testing|2048576|2020-02-23 04:56|GPLv2:)|https://qubes-os.org/?|Qubes template for fedora-32 v2|Qubes template\n for fedora-32 v2\n|
  1416. qubes-template-fedora-32|0|4.1|20200101|qubes-templates-itl|1048576|2020-01-23 04:56|GPL|https://qubes-os.org|Qubes template for fedora-32|Qubes template for fedora-32\n|
  1417. '''
  1418. with self.assertRaisesRegex(ConnectionError,
  1419. "unexpected data format"):
  1420. qubesadmin.tools.qvm_template.qrexec_repoquery(args, self.app,
  1421. 'qubes-template-fedora-32')
  1422. self.assertEqual(self.app.service_calls, [
  1423. ('test-vm', 'qubes.TemplateSearch',
  1424. {'filter_esc': True, 'stdout': subprocess.PIPE}),
  1425. ('test-vm', 'qubes.TemplateSearch', b'str1\nstr2')
  1426. ])
  1427. self.assertEqual(mock_payload.mock_calls, [
  1428. mock.call(args, self.app, 'qubes-template-fedora-32', False)
  1429. ])
  1430. self.assertAllCalled()
  1431. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1432. def test_130_get_dl_list_latest_success(self, mock_query):
  1433. mock_query.return_value = [
  1434. qubesadmin.tools.qvm_template.Template(
  1435. 'fedora-32',
  1436. '1',
  1437. '4.1',
  1438. '20200101',
  1439. 'qubes-templates-itl',
  1440. 1048576,
  1441. datetime.datetime(2020, 1, 23, 4, 56),
  1442. 'GPL',
  1443. 'https://qubes-os.org',
  1444. 'Qubes template for fedora-32',
  1445. 'Qubes template\n for fedora-32\n'
  1446. ),
  1447. qubesadmin.tools.qvm_template.Template(
  1448. 'fedora-32',
  1449. '0',
  1450. '4.2',
  1451. '20200201',
  1452. 'qubes-templates-itl-testing',
  1453. 2048576,
  1454. datetime.datetime(2020, 2, 23, 4, 56),
  1455. 'GPLv2',
  1456. 'https://qubes-os.org/?',
  1457. 'Qubes template for fedora-32 v2',
  1458. 'Qubes template\n for fedora-32 v2\n'
  1459. )
  1460. ]
  1461. args = argparse.Namespace(
  1462. templates=['some.local.file.rpm', 'fedora-32']
  1463. )
  1464. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app)
  1465. self.assertEqual(ret, {
  1466. 'fedora-32': qubesadmin.tools.qvm_template.DlEntry(
  1467. ('1', '4.1', '20200101'), 'qubes-templates-itl', 1048576)
  1468. })
  1469. self.assertEqual(mock_query.mock_calls, [
  1470. mock.call(args, self.app, 'qubes-template-fedora-32')
  1471. ])
  1472. self.assertAllCalled()
  1473. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1474. def test_131_get_dl_list_latest_notfound_fail(self, mock_query):
  1475. mock_query.return_value = []
  1476. args = argparse.Namespace(
  1477. templates=['some.local.file.rpm', 'fedora-31']
  1478. )
  1479. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1480. with self.assertRaises(SystemExit):
  1481. qubesadmin.tools.qvm_template.get_dl_list(args, self.app)
  1482. self.assertTrue('not found' in mock_err.getvalue())
  1483. self.assertEqual(mock_query.mock_calls, [
  1484. mock.call(args, self.app, 'qubes-template-fedora-31')
  1485. ])
  1486. self.assertAllCalled()
  1487. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1488. def test_132_get_dl_list_multimerge0_success(self, mock_query):
  1489. counter = 0
  1490. def f(*args):
  1491. nonlocal counter
  1492. counter += 1
  1493. if counter == 1:
  1494. return [
  1495. qubesadmin.tools.qvm_template.Template(
  1496. 'fedora-32',
  1497. '0',
  1498. '4.2',
  1499. '20200201',
  1500. 'qubes-templates-itl-testing',
  1501. 2048576,
  1502. datetime.datetime(2020, 2, 23, 4, 56),
  1503. 'GPLv2',
  1504. 'https://qubes-os.org/?',
  1505. 'Qubes template for fedora-32 v2',
  1506. 'Qubes template\n for fedora-32 v2\n'
  1507. )
  1508. ]
  1509. return [
  1510. qubesadmin.tools.qvm_template.Template(
  1511. 'fedora-32',
  1512. '1',
  1513. '4.1',
  1514. '20200101',
  1515. 'qubes-templates-itl',
  1516. 1048576,
  1517. datetime.datetime(2020, 1, 23, 4, 56),
  1518. 'GPL',
  1519. 'https://qubes-os.org',
  1520. 'Qubes template for fedora-32',
  1521. 'Qubes template\n for fedora-32\n'
  1522. )
  1523. ]
  1524. mock_query.side_effect = f
  1525. args = argparse.Namespace(
  1526. templates=['some.local.file.rpm', 'fedora-32:0', 'fedora-32:1']
  1527. )
  1528. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app)
  1529. self.assertEqual(ret, {
  1530. 'fedora-32': qubesadmin.tools.qvm_template.DlEntry(
  1531. ('1', '4.1', '20200101'), 'qubes-templates-itl', 1048576)
  1532. })
  1533. self.assertEqual(mock_query.mock_calls, [
  1534. mock.call(args, self.app, 'qubes-template-fedora-32:0'),
  1535. mock.call(args, self.app, 'qubes-template-fedora-32:1')
  1536. ])
  1537. self.assertAllCalled()
  1538. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1539. def test_132_get_dl_list_multimerge1_success(self, mock_query):
  1540. counter = 0
  1541. def f(*args):
  1542. nonlocal counter
  1543. counter += 1
  1544. if counter == 1:
  1545. return [
  1546. qubesadmin.tools.qvm_template.Template(
  1547. 'fedora-32',
  1548. '2',
  1549. '4.2',
  1550. '20200201',
  1551. 'qubes-templates-itl-testing',
  1552. 2048576,
  1553. datetime.datetime(2020, 2, 23, 4, 56),
  1554. 'GPLv2',
  1555. 'https://qubes-os.org/?',
  1556. 'Qubes template for fedora-32 v2',
  1557. 'Qubes template\n for fedora-32 v2\n'
  1558. )
  1559. ]
  1560. return [
  1561. qubesadmin.tools.qvm_template.Template(
  1562. 'fedora-32',
  1563. '1',
  1564. '4.1',
  1565. '20200101',
  1566. 'qubes-templates-itl',
  1567. 1048576,
  1568. datetime.datetime(2020, 1, 23, 4, 56),
  1569. 'GPL',
  1570. 'https://qubes-os.org',
  1571. 'Qubes template for fedora-32',
  1572. 'Qubes template\n for fedora-32\n'
  1573. )
  1574. ]
  1575. mock_query.side_effect = f
  1576. args = argparse.Namespace(
  1577. templates=['some.local.file.rpm', 'fedora-32:2', 'fedora-32:1']
  1578. )
  1579. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app)
  1580. self.assertEqual(ret, {
  1581. 'fedora-32': qubesadmin.tools.qvm_template.DlEntry(
  1582. ('2', '4.2', '20200201'),
  1583. 'qubes-templates-itl-testing',
  1584. 2048576)
  1585. })
  1586. self.assertEqual(mock_query.mock_calls, [
  1587. mock.call(args, self.app, 'qubes-template-fedora-32:2'),
  1588. mock.call(args, self.app, 'qubes-template-fedora-32:1')
  1589. ])
  1590. self.assertAllCalled()
  1591. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1592. def test_133_get_dl_list_reinstall_success(self, mock_query):
  1593. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1594. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1595. self.app.expected_calls[(
  1596. 'test-vm',
  1597. 'admin.vm.feature.Get',
  1598. f'template-name',
  1599. None)] = b'0\0test-vm'
  1600. self.app.expected_calls[(
  1601. 'test-vm',
  1602. 'admin.vm.feature.Get',
  1603. f'template-epoch',
  1604. None)] = b'0\x000'
  1605. self.app.expected_calls[(
  1606. 'test-vm',
  1607. 'admin.vm.feature.Get',
  1608. f'template-version',
  1609. None)] = b'0\x004.2'
  1610. self.app.expected_calls[(
  1611. 'test-vm',
  1612. 'admin.vm.feature.Get',
  1613. f'template-release',
  1614. None)] = b'0\x0020200201'
  1615. mock_query.return_value = [
  1616. qubesadmin.tools.qvm_template.Template(
  1617. 'test-vm',
  1618. '1',
  1619. '4.1',
  1620. '20200101',
  1621. 'qubes-templates-itl',
  1622. 1048576,
  1623. datetime.datetime(2020, 1, 23, 4, 56),
  1624. 'GPL',
  1625. 'https://qubes-os.org',
  1626. 'Qubes template for test-vm',
  1627. 'Qubes template\n for test-vm\n'
  1628. ),
  1629. qubesadmin.tools.qvm_template.Template(
  1630. 'test-vm',
  1631. '0',
  1632. '4.2',
  1633. '20200201',
  1634. 'qubes-templates-itl-testing',
  1635. 2048576,
  1636. datetime.datetime(2020, 2, 23, 4, 56),
  1637. 'GPLv2',
  1638. 'https://qubes-os.org/?',
  1639. 'Qubes template for test-vm v2',
  1640. 'Qubes template\n for test-vm v2\n'
  1641. )
  1642. ]
  1643. args = argparse.Namespace(
  1644. templates=['some.local.file.rpm', 'test-vm']
  1645. )
  1646. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1647. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  1648. self.assertEqual(ret, {
  1649. 'test-vm': qubesadmin.tools.qvm_template.DlEntry(
  1650. ('0', '4.2', '20200201'),
  1651. 'qubes-templates-itl-testing',
  1652. 2048576
  1653. )
  1654. })
  1655. self.assertEqual(mock_query.mock_calls, [
  1656. mock.call(args, self.app, 'qubes-template-test-vm')
  1657. ])
  1658. self.assertAllCalled()
  1659. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1660. def test_134_get_dl_list_reinstall_nolocal_fail(self, mock_query):
  1661. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1662. b'0\x00'
  1663. mock_query.return_value = [
  1664. qubesadmin.tools.qvm_template.Template(
  1665. 'test-vm',
  1666. '1',
  1667. '4.1',
  1668. '20200101',
  1669. 'qubes-templates-itl',
  1670. 1048576,
  1671. datetime.datetime(2020, 1, 23, 4, 56),
  1672. 'GPL',
  1673. 'https://qubes-os.org',
  1674. 'Qubes template for test-vm',
  1675. 'Qubes template\n for test-vm\n'
  1676. ),
  1677. qubesadmin.tools.qvm_template.Template(
  1678. 'test-vm',
  1679. '0',
  1680. '4.2',
  1681. '20200201',
  1682. 'qubes-templates-itl-testing',
  1683. 2048576,
  1684. datetime.datetime(2020, 2, 23, 4, 56),
  1685. 'GPLv2',
  1686. 'https://qubes-os.org/?',
  1687. 'Qubes template for test-vm v2',
  1688. 'Qubes template\n for test-vm v2\n'
  1689. )
  1690. ]
  1691. args = argparse.Namespace(templates=['test-vm'])
  1692. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1693. with self.assertRaises(SystemExit):
  1694. qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1695. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  1696. self.assertTrue('not already installed' in mock_err.getvalue())
  1697. self.assertEqual(mock_query.mock_calls, [
  1698. mock.call(args, self.app, 'qubes-template-test-vm')
  1699. ])
  1700. self.assertAllCalled()
  1701. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1702. def test_135_get_dl_list_reinstall_nonmanaged_fail(self, mock_query):
  1703. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1704. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1705. mock_query.return_value = [
  1706. qubesadmin.tools.qvm_template.Template(
  1707. 'test-vm',
  1708. '1',
  1709. '4.1',
  1710. '20200101',
  1711. 'qubes-templates-itl',
  1712. 1048576,
  1713. datetime.datetime(2020, 1, 23, 4, 56),
  1714. 'GPL',
  1715. 'https://qubes-os.org',
  1716. 'Qubes template for test-vm',
  1717. 'Qubes template\n for test-vm\n'
  1718. ),
  1719. qubesadmin.tools.qvm_template.Template(
  1720. 'test-vm',
  1721. '0',
  1722. '4.2',
  1723. '20200201',
  1724. 'qubes-templates-itl-testing',
  1725. 2048576,
  1726. datetime.datetime(2020, 2, 23, 4, 56),
  1727. 'GPLv2',
  1728. 'https://qubes-os.org/?',
  1729. 'Qubes template for test-vm v2',
  1730. 'Qubes template\n for test-vm v2\n'
  1731. )
  1732. ]
  1733. args = argparse.Namespace(templates=['test-vm'])
  1734. def qubesd_call(dest, method,
  1735. arg=None, payload=None, payload_stream=None,
  1736. orig_func=self.app.qubesd_call):
  1737. if method == 'admin.vm.feature.Get':
  1738. raise KeyError
  1739. return orig_func(dest, method, arg, payload, payload_stream)
  1740. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  1741. mock.patch.object(self.app, 'qubesd_call') as mock_call:
  1742. mock_call.side_effect = qubesd_call
  1743. with self.assertRaises(SystemExit):
  1744. qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1745. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  1746. self.assertTrue('not managed' in mock_err.getvalue())
  1747. self.assertEqual(mock_query.mock_calls, [
  1748. mock.call(args, self.app, 'qubes-template-test-vm')
  1749. ])
  1750. self.assertAllCalled()
  1751. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1752. def test_135_get_dl_list_reinstall_nonmanagednoname_fail(self, mock_query):
  1753. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1754. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1755. self.app.expected_calls[(
  1756. 'test-vm',
  1757. 'admin.vm.feature.Get',
  1758. f'template-name',
  1759. None)] = b'0\0test-vm-2'
  1760. mock_query.return_value = [
  1761. qubesadmin.tools.qvm_template.Template(
  1762. 'test-vm',
  1763. '1',
  1764. '4.1',
  1765. '20200101',
  1766. 'qubes-templates-itl',
  1767. 1048576,
  1768. datetime.datetime(2020, 1, 23, 4, 56),
  1769. 'GPL',
  1770. 'https://qubes-os.org',
  1771. 'Qubes template for test-vm',
  1772. 'Qubes template\n for test-vm\n'
  1773. ),
  1774. qubesadmin.tools.qvm_template.Template(
  1775. 'test-vm',
  1776. '0',
  1777. '4.2',
  1778. '20200201',
  1779. 'qubes-templates-itl-testing',
  1780. 2048576,
  1781. datetime.datetime(2020, 2, 23, 4, 56),
  1782. 'GPLv2',
  1783. 'https://qubes-os.org/?',
  1784. 'Qubes template for test-vm v2',
  1785. 'Qubes template\n for test-vm v2\n'
  1786. )
  1787. ]
  1788. args = argparse.Namespace(templates=['test-vm'])
  1789. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1790. with self.assertRaises(SystemExit):
  1791. qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1792. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  1793. self.assertTrue('not managed' in mock_err.getvalue())
  1794. self.assertEqual(mock_query.mock_calls, [
  1795. mock.call(args, self.app, 'qubes-template-test-vm')
  1796. ])
  1797. self.assertAllCalled()
  1798. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1799. def test_136_get_dl_list_downgrade_success(self, mock_query):
  1800. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1801. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1802. self.app.expected_calls[(
  1803. 'test-vm',
  1804. 'admin.vm.feature.Get',
  1805. f'template-name',
  1806. None)] = b'0\0test-vm'
  1807. self.app.expected_calls[(
  1808. 'test-vm',
  1809. 'admin.vm.feature.Get',
  1810. f'template-epoch',
  1811. None)] = b'0\x000'
  1812. self.app.expected_calls[(
  1813. 'test-vm',
  1814. 'admin.vm.feature.Get',
  1815. f'template-version',
  1816. None)] = b'0\x004.3'
  1817. self.app.expected_calls[(
  1818. 'test-vm',
  1819. 'admin.vm.feature.Get',
  1820. f'template-release',
  1821. None)] = b'0\x0020200201'
  1822. mock_query.return_value = [
  1823. qubesadmin.tools.qvm_template.Template(
  1824. 'test-vm',
  1825. '0',
  1826. '4.2',
  1827. '20200201',
  1828. 'qubes-templates-itl-testing',
  1829. 2048576,
  1830. datetime.datetime(2020, 2, 23, 4, 56),
  1831. 'GPLv2',
  1832. 'https://qubes-os.org/?',
  1833. 'Qubes template for test-vm v2',
  1834. 'Qubes template\n for test-vm v2\n'
  1835. ),
  1836. qubesadmin.tools.qvm_template.Template(
  1837. 'test-vm',
  1838. '0',
  1839. '4.1',
  1840. '20200101',
  1841. 'qubes-templates-itl',
  1842. 1048576,
  1843. datetime.datetime(2020, 1, 23, 4, 56),
  1844. 'GPL',
  1845. 'https://qubes-os.org',
  1846. 'Qubes template for test-vm',
  1847. 'Qubes template\n for test-vm\n'
  1848. ),
  1849. qubesadmin.tools.qvm_template.Template(
  1850. 'test-vm',
  1851. '1',
  1852. '4.1',
  1853. '20200101',
  1854. 'qubes-templates-itl',
  1855. 1048576,
  1856. datetime.datetime(2020, 1, 23, 4, 56),
  1857. 'GPL',
  1858. 'https://qubes-os.org',
  1859. 'Qubes template for test-vm',
  1860. 'Qubes template\n for test-vm\n'
  1861. )
  1862. ]
  1863. args = argparse.Namespace(templates=['test-vm'])
  1864. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1865. qubesadmin.tools.qvm_template.VersionSelector.LATEST_LOWER)
  1866. self.assertEqual(ret, {
  1867. 'test-vm': qubesadmin.tools.qvm_template.DlEntry(
  1868. ('0', '4.2', '20200201'),
  1869. 'qubes-templates-itl-testing',
  1870. 2048576
  1871. )
  1872. })
  1873. self.assertEqual(mock_query.mock_calls, [
  1874. mock.call(args, self.app, 'qubes-template-test-vm')
  1875. ])
  1876. self.assertAllCalled()
  1877. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1878. def test_137_get_dl_list_downgrade_nonmanaged_fail(self, mock_query):
  1879. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1880. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1881. self.app.expected_calls[(
  1882. 'test-vm',
  1883. 'admin.vm.feature.Get',
  1884. f'template-name',
  1885. None)] = b'0\0test-vm-2'
  1886. mock_query.return_value = [
  1887. qubesadmin.tools.qvm_template.Template(
  1888. 'test-vm',
  1889. '0',
  1890. '4.2',
  1891. '20200201',
  1892. 'qubes-templates-itl-testing',
  1893. 2048576,
  1894. datetime.datetime(2020, 2, 23, 4, 56),
  1895. 'GPLv2',
  1896. 'https://qubes-os.org/?',
  1897. 'Qubes template for test-vm v2',
  1898. 'Qubes template\n for test-vm v2\n'
  1899. ),
  1900. qubesadmin.tools.qvm_template.Template(
  1901. 'test-vm',
  1902. '0',
  1903. '4.1',
  1904. '20200101',
  1905. 'qubes-templates-itl',
  1906. 1048576,
  1907. datetime.datetime(2020, 1, 23, 4, 56),
  1908. 'GPL',
  1909. 'https://qubes-os.org',
  1910. 'Qubes template for test-vm',
  1911. 'Qubes template\n for test-vm\n'
  1912. ),
  1913. qubesadmin.tools.qvm_template.Template(
  1914. 'test-vm',
  1915. '1',
  1916. '4.1',
  1917. '20200101',
  1918. 'qubes-templates-itl',
  1919. 1048576,
  1920. datetime.datetime(2020, 1, 23, 4, 56),
  1921. 'GPL',
  1922. 'https://qubes-os.org',
  1923. 'Qubes template for test-vm',
  1924. 'Qubes template\n for test-vm\n'
  1925. )
  1926. ]
  1927. args = argparse.Namespace(templates=['test-vm'])
  1928. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1929. with self.assertRaises(SystemExit):
  1930. qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1931. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  1932. self.assertTrue('not managed' in mock_err.getvalue())
  1933. self.assertEqual(mock_query.mock_calls, [
  1934. mock.call(args, self.app, 'qubes-template-test-vm')
  1935. ])
  1936. self.assertAllCalled()
  1937. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1938. def test_138_get_dl_list_downgrade_notfound_skip(self, mock_query):
  1939. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1940. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1941. self.app.expected_calls[(
  1942. 'test-vm',
  1943. 'admin.vm.feature.Get',
  1944. f'template-name',
  1945. None)] = b'0\0test-vm'
  1946. self.app.expected_calls[(
  1947. 'test-vm',
  1948. 'admin.vm.feature.Get',
  1949. f'template-epoch',
  1950. None)] = b'0\x000'
  1951. self.app.expected_calls[(
  1952. 'test-vm',
  1953. 'admin.vm.feature.Get',
  1954. f'template-version',
  1955. None)] = b'0\x004.3'
  1956. self.app.expected_calls[(
  1957. 'test-vm',
  1958. 'admin.vm.feature.Get',
  1959. f'template-release',
  1960. None)] = b'0\x0020200201'
  1961. mock_query.return_value = [
  1962. qubesadmin.tools.qvm_template.Template(
  1963. 'test-vm',
  1964. '1',
  1965. '4.1',
  1966. '20200101',
  1967. 'qubes-templates-itl',
  1968. 1048576,
  1969. datetime.datetime(2020, 1, 23, 4, 56),
  1970. 'GPL',
  1971. 'https://qubes-os.org',
  1972. 'Qubes template for test-vm',
  1973. 'Qubes template\n for test-vm\n'
  1974. )
  1975. ]
  1976. args = argparse.Namespace(templates=['test-vm'])
  1977. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  1978. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  1979. qubesadmin.tools.qvm_template.VersionSelector.LATEST_LOWER)
  1980. self.assertTrue('lowest version' in mock_err.getvalue())
  1981. self.assertEqual(ret, {})
  1982. self.assertEqual(mock_query.mock_calls, [
  1983. mock.call(args, self.app, 'qubes-template-test-vm')
  1984. ])
  1985. self.assertAllCalled()
  1986. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  1987. def test_139_get_dl_list_upgrade_success(self, mock_query):
  1988. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  1989. b'0\x00test-vm class=TemplateVM state=Halted\n'
  1990. self.app.expected_calls[(
  1991. 'test-vm',
  1992. 'admin.vm.feature.Get',
  1993. f'template-name',
  1994. None)] = b'0\0test-vm'
  1995. self.app.expected_calls[(
  1996. 'test-vm',
  1997. 'admin.vm.feature.Get',
  1998. f'template-epoch',
  1999. None)] = b'0\x000'
  2000. self.app.expected_calls[(
  2001. 'test-vm',
  2002. 'admin.vm.feature.Get',
  2003. f'template-version',
  2004. None)] = b'0\x004.3'
  2005. self.app.expected_calls[(
  2006. 'test-vm',
  2007. 'admin.vm.feature.Get',
  2008. f'template-release',
  2009. None)] = b'0\x0020200201'
  2010. mock_query.return_value = [
  2011. qubesadmin.tools.qvm_template.Template(
  2012. 'test-vm',
  2013. '1',
  2014. '4.1',
  2015. '20200101',
  2016. 'qubes-templates-itl',
  2017. 1048576,
  2018. datetime.datetime(2020, 1, 23, 4, 56),
  2019. 'GPL',
  2020. 'https://qubes-os.org',
  2021. 'Qubes template for test-vm',
  2022. 'Qubes template\n for test-vm\n'
  2023. )
  2024. ]
  2025. args = argparse.Namespace(templates=['test-vm'])
  2026. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  2027. qubesadmin.tools.qvm_template.VersionSelector.LATEST_HIGHER)
  2028. self.assertEqual(ret, {
  2029. 'test-vm': qubesadmin.tools.qvm_template.DlEntry(
  2030. ('1', '4.1', '20200101'), 'qubes-templates-itl', 1048576
  2031. )
  2032. })
  2033. self.assertEqual(mock_query.mock_calls, [
  2034. mock.call(args, self.app, 'qubes-template-test-vm')
  2035. ])
  2036. self.assertAllCalled()
  2037. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2038. def test_140_get_dl_list_downgrade_notfound_skip(self, mock_query):
  2039. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2040. b'0\x00test-vm class=TemplateVM state=Halted\n'
  2041. self.app.expected_calls[(
  2042. 'test-vm',
  2043. 'admin.vm.feature.Get',
  2044. f'template-name',
  2045. None)] = b'0\0test-vm'
  2046. self.app.expected_calls[(
  2047. 'test-vm',
  2048. 'admin.vm.feature.Get',
  2049. f'template-epoch',
  2050. None)] = b'0\x000'
  2051. self.app.expected_calls[(
  2052. 'test-vm',
  2053. 'admin.vm.feature.Get',
  2054. f'template-version',
  2055. None)] = b'0\x004.3'
  2056. self.app.expected_calls[(
  2057. 'test-vm',
  2058. 'admin.vm.feature.Get',
  2059. f'template-release',
  2060. None)] = b'0\x0020200201'
  2061. mock_query.return_value = [
  2062. qubesadmin.tools.qvm_template.Template(
  2063. 'test-vm',
  2064. '0',
  2065. '4.1',
  2066. '20200101',
  2067. 'qubes-templates-itl',
  2068. 1048576,
  2069. datetime.datetime(2020, 1, 23, 4, 56),
  2070. 'GPL',
  2071. 'https://qubes-os.org',
  2072. 'Qubes template for test-vm',
  2073. 'Qubes template\n for test-vm\n'
  2074. )
  2075. ]
  2076. args = argparse.Namespace(templates=['test-vm'])
  2077. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  2078. ret = qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  2079. qubesadmin.tools.qvm_template.VersionSelector.LATEST_HIGHER)
  2080. self.assertTrue('highest version' in mock_err.getvalue())
  2081. self.assertEqual(ret, {})
  2082. self.assertEqual(mock_query.mock_calls, [
  2083. mock.call(args, self.app, 'qubes-template-test-vm')
  2084. ])
  2085. self.assertAllCalled()
  2086. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2087. def test_141_get_dl_list_reinstall_notfound_fail(self, mock_query):
  2088. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2089. b'0\x00test-vm class=TemplateVM state=Halted\n'
  2090. self.app.expected_calls[(
  2091. 'test-vm',
  2092. 'admin.vm.feature.Get',
  2093. f'template-name',
  2094. None)] = b'0\0test-vm'
  2095. self.app.expected_calls[(
  2096. 'test-vm',
  2097. 'admin.vm.feature.Get',
  2098. f'template-epoch',
  2099. None)] = b'0\x000'
  2100. self.app.expected_calls[(
  2101. 'test-vm',
  2102. 'admin.vm.feature.Get',
  2103. f'template-version',
  2104. None)] = b'0\x004.3'
  2105. self.app.expected_calls[(
  2106. 'test-vm',
  2107. 'admin.vm.feature.Get',
  2108. f'template-release',
  2109. None)] = b'0\x0020200201'
  2110. mock_query.return_value = [
  2111. qubesadmin.tools.qvm_template.Template(
  2112. 'test-vm',
  2113. '0',
  2114. '4.1',
  2115. '20200101',
  2116. 'qubes-templates-itl',
  2117. 1048576,
  2118. datetime.datetime(2020, 1, 23, 4, 56),
  2119. 'GPL',
  2120. 'https://qubes-os.org',
  2121. 'Qubes template for test-vm',
  2122. 'Qubes template\n for test-vm\n'
  2123. )
  2124. ]
  2125. args = argparse.Namespace(templates=['test-vm'])
  2126. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  2127. with self.assertRaises(SystemExit):
  2128. qubesadmin.tools.qvm_template.get_dl_list(args, self.app,
  2129. qubesadmin.tools.qvm_template.VersionSelector.REINSTALL)
  2130. self.assertTrue('Same version' in mock_err.getvalue())
  2131. self.assertTrue('not found' in mock_err.getvalue())
  2132. self.assertEqual(mock_query.mock_calls, [
  2133. mock.call(args, self.app, 'qubes-template-test-vm')
  2134. ])
  2135. self.assertAllCalled()
  2136. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2137. def test_150_list_templates_installed_success(self, mock_query):
  2138. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2139. b'0\x00test-vm class=TemplateVM state=Halted\n' \
  2140. b'test-vm-2 class=TemplateVM state=Halted\n' \
  2141. b'non-spec class=TemplateVM state=Halted\n'
  2142. build_time = '2020-09-01 14:30:00' # 1598970600
  2143. install_time = '2020-09-01 15:30:00'
  2144. for key, val in [
  2145. ('name', 'test-vm'),
  2146. ('epoch', '2'),
  2147. ('version', '4.1'),
  2148. ('release', '2020'),
  2149. ('reponame', '@commandline'),
  2150. ('buildtime', build_time),
  2151. ('installtime', install_time),
  2152. ('license', 'GPL'),
  2153. ('url', 'https://qubes-os.org'),
  2154. ('summary', 'Summary'),
  2155. ('description', 'Desc|desc')]:
  2156. self.app.expected_calls[(
  2157. 'test-vm',
  2158. 'admin.vm.feature.Get',
  2159. f'template-{key}',
  2160. None)] = b'0\0' + val.encode()
  2161. for key, val in [('name', 'test-vm-2-not-managed')]:
  2162. self.app.expected_calls[(
  2163. 'test-vm-2',
  2164. 'admin.vm.feature.Get',
  2165. f'template-{key}',
  2166. None)] = b'0\0' + val.encode()
  2167. for key, val in [
  2168. ('name', 'non-spec'),
  2169. ('epoch', '0'),
  2170. ('version', '4.3'),
  2171. ('release', '20200201')]:
  2172. self.app.expected_calls[(
  2173. 'non-spec',
  2174. 'admin.vm.feature.Get',
  2175. f'template-{key}',
  2176. None)] = b'0\0' + val.encode()
  2177. args = argparse.Namespace(
  2178. all=False,
  2179. installed=True,
  2180. available=False,
  2181. extras=False,
  2182. upgrades=False,
  2183. machine_readable=False,
  2184. machine_readable_json=False,
  2185. templates=['test-vm*']
  2186. )
  2187. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out, \
  2188. mock.patch.object(self.app.domains['test-vm'],
  2189. 'get_disk_utilization') as mock_disk:
  2190. mock_disk.return_value = 1234321
  2191. qubesadmin.tools.qvm_template.list_templates(
  2192. args, self.app, 'list')
  2193. self.assertEqual(mock_out.getvalue(),
  2194. '''Installed Templates
  2195. [('test-vm', '2:4.1-2020', '@commandline')]
  2196. ''')
  2197. self.assertEqual(mock_disk.mock_calls, [mock.call()])
  2198. self.assertEqual(mock_query.mock_calls, [])
  2199. self.assertAllCalled()
  2200. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2201. def test_151_list_templates_available_success(self, mock_query):
  2202. counter = 0
  2203. def f(*args):
  2204. nonlocal counter
  2205. counter += 1
  2206. if counter == 1:
  2207. return [
  2208. qubesadmin.tools.qvm_template.Template(
  2209. 'fedora-32',
  2210. '0',
  2211. '4.2',
  2212. '20200201',
  2213. 'qubes-templates-itl-testing',
  2214. 2048576,
  2215. datetime.datetime(2020, 2, 23, 4, 56),
  2216. 'GPLv2',
  2217. 'https://qubes-os.org/?',
  2218. 'Qubes template for fedora-32 v2',
  2219. 'Qubes template\n for fedora-32 v2\n'
  2220. )
  2221. ]
  2222. return [
  2223. qubesadmin.tools.qvm_template.Template(
  2224. 'fedora-31',
  2225. '1',
  2226. '4.1',
  2227. '20200101',
  2228. 'qubes-templates-itl',
  2229. 1048576,
  2230. datetime.datetime(2020, 1, 23, 4, 56),
  2231. 'GPL',
  2232. 'https://qubes-os.org',
  2233. 'Qubes template for fedora-31',
  2234. 'Qubes template\n for fedora-31\n'
  2235. )
  2236. ]
  2237. mock_query.side_effect = f
  2238. args = argparse.Namespace(
  2239. all=False,
  2240. installed=False,
  2241. available=True,
  2242. extras=False,
  2243. upgrades=False,
  2244. machine_readable=False,
  2245. machine_readable_json=False,
  2246. templates=['fedora-32', 'fedora-31']
  2247. )
  2248. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  2249. qubesadmin.tools.qvm_template.list_templates(
  2250. args, self.app, 'list')
  2251. # Order not determinstic because of sets
  2252. expected = [
  2253. ('fedora-31', '1:4.1-20200101', 'qubes-templates-itl'),
  2254. ('fedora-32', '0:4.2-20200201', 'qubes-templates-itl-testing')
  2255. ]
  2256. self.assertTrue(mock_out.getvalue() == \
  2257. f'''Available Templates
  2258. {str([expected[1], expected[0]])}
  2259. ''' \
  2260. or mock_out.getvalue() == \
  2261. f'''Available Templates
  2262. {str([expected[0], expected[1]])}
  2263. ''')
  2264. self.assertEqual(mock_query.mock_calls, [
  2265. mock.call(args, self.app, 'fedora-32'),
  2266. mock.call(args, self.app, 'fedora-31')
  2267. ])
  2268. self.assertAllCalled()
  2269. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2270. def test_151_list_templates_available_all_success(self, mock_query):
  2271. mock_query.return_value = [
  2272. qubesadmin.tools.qvm_template.Template(
  2273. 'fedora-31',
  2274. '1',
  2275. '4.1',
  2276. '20200101',
  2277. 'qubes-templates-itl',
  2278. 1048576,
  2279. datetime.datetime(2020, 1, 23, 4, 56),
  2280. 'GPL',
  2281. 'https://qubes-os.org',
  2282. 'Qubes template for fedora-31',
  2283. 'Qubes template\n for fedora-31\n'
  2284. )
  2285. ]
  2286. args = argparse.Namespace(
  2287. all=False,
  2288. installed=False,
  2289. available=True,
  2290. extras=False,
  2291. upgrades=False,
  2292. machine_readable=False,
  2293. machine_readable_json=False,
  2294. templates=[]
  2295. )
  2296. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  2297. qubesadmin.tools.qvm_template.list_templates(
  2298. args, self.app, 'list')
  2299. self.assertEqual(mock_out.getvalue(),
  2300. '''Available Templates
  2301. [('fedora-31', '1:4.1-20200101', 'qubes-templates-itl')]
  2302. ''')
  2303. self.assertEqual(mock_query.mock_calls, [
  2304. mock.call(args, self.app)
  2305. ])
  2306. self.assertAllCalled()
  2307. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2308. def test_152_list_templates_extras_success(self, mock_query):
  2309. mock_query.return_value = [
  2310. qubesadmin.tools.qvm_template.Template(
  2311. 'test-vm',
  2312. '2',
  2313. '4.1',
  2314. '2020',
  2315. 'qubes-templates-itl',
  2316. 1048576,
  2317. datetime.datetime(2020, 9, 1, 14, 30,
  2318. tzinfo=datetime.timezone.utc),
  2319. 'GPL',
  2320. 'https://qubes-os.org',
  2321. 'Qubes template for fedora-31',
  2322. 'Qubes template\n for fedora-31\n'
  2323. )
  2324. ]
  2325. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2326. b'0\x00test-vm class=TemplateVM state=Halted\n' \
  2327. b'test-vm-2 class=TemplateVM state=Halted\n' \
  2328. b'test-vm-3 class=TemplateVM state=Halted\n' \
  2329. b'non-spec class=TemplateVM state=Halted\n'
  2330. for key, val in [('name', 'test-vm')]:
  2331. self.app.expected_calls[(
  2332. 'test-vm',
  2333. 'admin.vm.feature.Get',
  2334. f'template-{key}',
  2335. None)] = b'0\0' + val.encode()
  2336. for key, val in [
  2337. ('name', 'test-vm-2'),
  2338. ('epoch', '1'),
  2339. ('version', '4.0'),
  2340. ('release', '2019'),
  2341. ('reponame', 'qubes-template-itl'),
  2342. ('buildtime', '2020-09-02 14:30:00'),
  2343. ('installtime', '2020-09-02 15:30:00'),
  2344. ('license', 'GPLv2'),
  2345. ('url', 'https://qubes-os.org/?'),
  2346. ('summary', 'Summary2'),
  2347. ('description', 'Desc|desc|2')]:
  2348. self.app.expected_calls[(
  2349. 'test-vm-2',
  2350. 'admin.vm.feature.Get',
  2351. f'template-{key}',
  2352. None)] = b'0\0' + val.encode()
  2353. for key, val in [('name', 'test-vm-3-non-managed')]:
  2354. self.app.expected_calls[(
  2355. 'test-vm-3',
  2356. 'admin.vm.feature.Get',
  2357. f'template-{key}',
  2358. None)] = b'0\0' + val.encode()
  2359. for key, val in [
  2360. ('name', 'non-spec'),
  2361. ('epoch', '1'),
  2362. ('version', '4.0'),
  2363. ('release', '2019')]:
  2364. self.app.expected_calls[(
  2365. 'non-spec',
  2366. 'admin.vm.feature.Get',
  2367. f'template-{key}',
  2368. None)] = b'0\0' + val.encode()
  2369. args = argparse.Namespace(
  2370. all=False,
  2371. installed=False,
  2372. available=False,
  2373. extras=True,
  2374. upgrades=False,
  2375. machine_readable=False,
  2376. machine_readable_json=False,
  2377. templates=['test-vm*']
  2378. )
  2379. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out, \
  2380. mock.patch.object(self.app.domains['test-vm-2'],
  2381. 'get_disk_utilization') as mock_disk:
  2382. mock_disk.return_value = 1234321
  2383. qubesadmin.tools.qvm_template.list_templates(
  2384. args, self.app, 'list')
  2385. self.assertEqual(mock_out.getvalue(),
  2386. '''Extra Templates
  2387. [('test-vm-2', '1:4.0-2019', 'qubes-template-itl')]
  2388. ''')
  2389. self.assertEqual(mock_disk.mock_calls, [mock.call()])
  2390. self.assertEqual(mock_query.mock_calls, [
  2391. mock.call(args, self.app, 'test-vm*')
  2392. ])
  2393. self.assertAllCalled()
  2394. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2395. def test_153_list_templates_upgrades_success(self, mock_query):
  2396. mock_query.return_value = [
  2397. qubesadmin.tools.qvm_template.Template(
  2398. 'test-vm',
  2399. '2',
  2400. '4.1',
  2401. '2020',
  2402. 'qubes-templates-itl',
  2403. 1048576,
  2404. datetime.datetime(2020, 9, 1, 14, 30,
  2405. tzinfo=datetime.timezone.utc),
  2406. 'GPL',
  2407. 'https://qubes-os.org',
  2408. 'Qubes template for fedora-31',
  2409. 'Qubes template\n for fedora-31\n'
  2410. ),
  2411. qubesadmin.tools.qvm_template.Template(
  2412. 'test-vm',
  2413. '0',
  2414. '4.1',
  2415. '2020',
  2416. 'qubes-templates-itl',
  2417. 1048576,
  2418. datetime.datetime(2020, 9, 1, 14, 30,
  2419. tzinfo=datetime.timezone.utc),
  2420. 'GPL',
  2421. 'https://qubes-os.org',
  2422. 'Qubes template for fedora-31',
  2423. 'Qubes template\n for fedora-31\n'
  2424. ),
  2425. qubesadmin.tools.qvm_template.Template(
  2426. 'test-vm-3',
  2427. '0',
  2428. '4.1',
  2429. '2020',
  2430. 'qubes-templates-itl',
  2431. 1048576,
  2432. datetime.datetime(2020, 9, 1, 14, 30,
  2433. tzinfo=datetime.timezone.utc),
  2434. 'GPL',
  2435. 'https://qubes-os.org',
  2436. 'Qubes template for fedora-31',
  2437. 'Qubes template\n for fedora-31\n'
  2438. )
  2439. ]
  2440. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2441. b'0\x00test-vm class=TemplateVM state=Halted\n' \
  2442. b'test-vm-2 class=TemplateVM state=Halted\n' \
  2443. b'test-vm-3 class=TemplateVM state=Halted\n'
  2444. for key, val in [
  2445. ('name', 'test-vm'),
  2446. ('epoch', '1'),
  2447. ('version', '4.0'),
  2448. ('release', '2019')]:
  2449. self.app.expected_calls[(
  2450. 'test-vm',
  2451. 'admin.vm.feature.Get',
  2452. f'template-{key}',
  2453. None)] = b'0\0' + val.encode()
  2454. for key, val in [
  2455. ('name', 'test-vm-2'),
  2456. ('epoch', '1'),
  2457. ('version', '4.0'),
  2458. ('release', '2019')]:
  2459. self.app.expected_calls[(
  2460. 'test-vm-2',
  2461. 'admin.vm.feature.Get',
  2462. f'template-{key}',
  2463. None)] = b'0\0' + val.encode()
  2464. for key, val in [('name', 'test-vm-3-non-managed')]:
  2465. self.app.expected_calls[(
  2466. 'test-vm-3',
  2467. 'admin.vm.feature.Get',
  2468. f'template-{key}',
  2469. None)] = b'0\0' + val.encode()
  2470. args = argparse.Namespace(
  2471. all=False,
  2472. installed=False,
  2473. available=False,
  2474. extras=False,
  2475. upgrades=True,
  2476. machine_readable=False,
  2477. machine_readable_json=False,
  2478. templates=['test-vm*']
  2479. )
  2480. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out, \
  2481. mock.patch.object(self.app.domains['test-vm-2'],
  2482. 'get_disk_utilization') as mock_disk:
  2483. mock_disk.return_value = 1234321
  2484. qubesadmin.tools.qvm_template.list_templates(
  2485. args, self.app, 'list')
  2486. self.assertEqual(mock_out.getvalue(),
  2487. '''Available Upgrades
  2488. [('test-vm', '2:4.1-2020', 'qubes-templates-itl')]
  2489. ''')
  2490. self.assertEqual(mock_disk.mock_calls, [])
  2491. self.assertEqual(mock_query.mock_calls, [
  2492. mock.call(args, self.app, 'test-vm*')
  2493. ])
  2494. self.assertAllCalled()
  2495. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2496. def __test_list_templates_all_success(self, operation,
  2497. args, expected, mock_query):
  2498. mock_query.return_value = [
  2499. qubesadmin.tools.qvm_template.Template(
  2500. 'test-vm',
  2501. '2',
  2502. '4.1',
  2503. '2020',
  2504. 'qubes-templates-itl',
  2505. 1048576,
  2506. datetime.datetime(2020, 9, 1, 14, 30,
  2507. tzinfo=datetime.timezone.utc),
  2508. 'GPL',
  2509. 'https://qubes-os.org',
  2510. 'Qubes template for fedora-31',
  2511. 'Qubes template\n for fedora-31\n'
  2512. )
  2513. ]
  2514. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2515. b'0\x00test-vm-2 class=TemplateVM state=Halted\n'
  2516. for key, val in [
  2517. ('name', 'test-vm-2'),
  2518. ('epoch', '1'),
  2519. ('version', '4.0'),
  2520. ('release', '2019'),
  2521. ('reponame', '@commandline'),
  2522. ('buildtime', '2020-09-02 14:30:00'),
  2523. ('installtime', '2020-09-02 15:30:00'),
  2524. ('license', 'GPL'),
  2525. ('url', 'https://qubes-os.org'),
  2526. ('summary', 'Summary'),
  2527. ('description', 'Desc|desc')]:
  2528. self.app.expected_calls[(
  2529. 'test-vm-2',
  2530. 'admin.vm.feature.Get',
  2531. f'template-{key}',
  2532. None)] = b'0\0' + val.encode()
  2533. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out, \
  2534. mock.patch.object(self.app.domains['test-vm-2'],
  2535. 'get_disk_utilization') as mock_disk:
  2536. mock_disk.return_value = 1234321
  2537. qubesadmin.tools.qvm_template.list_templates(
  2538. args, self.app, operation)
  2539. self.assertEqual(mock_out.getvalue(), expected)
  2540. self.assertEqual(mock_disk.mock_calls, [mock.call()])
  2541. self.assertEqual(mock_query.mock_calls, [
  2542. mock.call(args, self.app, 'test-vm*')
  2543. ])
  2544. self.assertAllCalled()
  2545. def test_154_list_templates_all_success(self):
  2546. args = argparse.Namespace(
  2547. all=True,
  2548. installed=False,
  2549. available=False,
  2550. extras=False,
  2551. upgrades=False,
  2552. machine_readable=False,
  2553. machine_readable_json=False,
  2554. templates=['test-vm*']
  2555. )
  2556. expected = \
  2557. '''Installed Templates
  2558. [('test-vm-2', '1:4.0-2019', '@commandline')]
  2559. Available Templates
  2560. [('test-vm', '2:4.1-2020', 'qubes-templates-itl')]
  2561. '''
  2562. self.__test_list_templates_all_success('list', args, expected)
  2563. def test_155_list_templates_all_implicit_success(self):
  2564. args = argparse.Namespace(
  2565. all=False,
  2566. installed=False,
  2567. available=False,
  2568. extras=False,
  2569. upgrades=False,
  2570. machine_readable=False,
  2571. machine_readable_json=False,
  2572. templates=['test-vm*']
  2573. )
  2574. expected = \
  2575. '''Installed Templates
  2576. [('test-vm-2', '1:4.0-2019', '@commandline')]
  2577. Available Templates
  2578. [('test-vm', '2:4.1-2020', 'qubes-templates-itl')]
  2579. '''
  2580. self.__test_list_templates_all_success('list', args, expected)
  2581. def test_156_list_templates_info_all_success(self):
  2582. args = argparse.Namespace(
  2583. all=False,
  2584. installed=False,
  2585. available=False,
  2586. extras=False,
  2587. upgrades=False,
  2588. machine_readable=False,
  2589. machine_readable_json=False,
  2590. templates=['test-vm*']
  2591. )
  2592. expected = \
  2593. '''Installed Templates
  2594. [('Name', ':', 'test-vm-2'), ('Epoch', ':', '1'), ('Version', ':', '4.0'), ('Release', ':', '2019'), ('Size', ':', '1.2 MiB'), ('Repository', ':', '@commandline'), ('Buildtime', ':', '2020-09-02 14:30:00'), ('Install time', ':', '2020-09-02 15:30:00'), ('URL', ':', 'https://qubes-os.org'), ('License', ':', 'GPL'), ('Summary', ':', 'Summary'), ('Description', ':', 'Desc'), ('', ':', 'desc'), (' ', ' ', ' ')]
  2595. Available Templates
  2596. [('Name', ':', 'test-vm'), ('Epoch', ':', '2'), ('Version', ':', '4.1'), ('Release', ':', '2020'), ('Size', ':', '1.0 MiB'), ('Repository', ':', 'qubes-templates-itl'), ('Buildtime', ':', '2020-09-01 14:30:00+00:00'), ('URL', ':', 'https://qubes-os.org'), ('License', ':', 'GPL'), ('Summary', ':', 'Qubes template for fedora-31'), ('Description', ':', 'Qubes template'), ('', ':', ' for fedora-31'), (' ', ' ', ' ')]
  2597. '''
  2598. self.__test_list_templates_all_success('info', args, expected)
  2599. def test_157_list_templates_list_all_machinereadable_success(self):
  2600. args = argparse.Namespace(
  2601. all=False,
  2602. installed=False,
  2603. available=False,
  2604. extras=False,
  2605. upgrades=False,
  2606. machine_readable=True,
  2607. machine_readable_json=False,
  2608. templates=['test-vm*']
  2609. )
  2610. expected = \
  2611. '''installed|test-vm-2|1:4.0-2019|@commandline
  2612. available|test-vm|2:4.1-2020|qubes-templates-itl
  2613. '''
  2614. self.__test_list_templates_all_success('list', args, expected)
  2615. def test_158_list_templates_info_all_machinereadable_success(self):
  2616. args = argparse.Namespace(
  2617. all=False,
  2618. installed=False,
  2619. available=False,
  2620. extras=False,
  2621. upgrades=False,
  2622. machine_readable=True,
  2623. machine_readable_json=False,
  2624. templates=['test-vm*']
  2625. )
  2626. expected = \
  2627. '''installed|test-vm-2|1|4.0|2019|@commandline|1234321|2020-09-02 14:30:00|2020-09-02 15:30:00|GPL|https://qubes-os.org|Summary|Desc|desc
  2628. available|test-vm|2|4.1|2020|qubes-templates-itl|1048576|2020-09-01 14:30:00||GPL|https://qubes-os.org|Qubes template for fedora-31|Qubes template| for fedora-31|
  2629. '''
  2630. self.__test_list_templates_all_success('info', args, expected)
  2631. def test_159_list_templates_list_all_machinereadablejson_success(self):
  2632. args = argparse.Namespace(
  2633. all=False,
  2634. installed=False,
  2635. available=False,
  2636. extras=False,
  2637. upgrades=False,
  2638. machine_readable=False,
  2639. machine_readable_json=True,
  2640. templates=['test-vm*']
  2641. )
  2642. expected = \
  2643. '''{"installed": [{"name": "test-vm-2", "evr": "1:4.0-2019", "reponame": "@commandline"}], "available": [{"name": "test-vm", "evr": "2:4.1-2020", "reponame": "qubes-templates-itl"}]}
  2644. '''
  2645. self.__test_list_templates_all_success('list', args, expected)
  2646. def test_160_list_templates_info_all_machinereadablejson_success(self):
  2647. args = argparse.Namespace(
  2648. all=False,
  2649. installed=False,
  2650. available=False,
  2651. extras=False,
  2652. upgrades=False,
  2653. machine_readable=False,
  2654. machine_readable_json=True,
  2655. templates=['test-vm*']
  2656. )
  2657. expected = \
  2658. r'''{"installed": [{"name": "test-vm-2", "epoch": "1", "version": "4.0", "release": "2019", "reponame": "@commandline", "size": "1234321", "buildtime": "2020-09-02 14:30:00", "installtime": "2020-09-02 15:30:00", "license": "GPL", "url": "https://qubes-os.org", "summary": "Summary", "description": "Desc\ndesc"}], "available": [{"name": "test-vm", "epoch": "2", "version": "4.1", "release": "2020", "reponame": "qubes-templates-itl", "size": "1048576", "buildtime": "2020-09-01 14:30:00", "installtime": "", "license": "GPL", "url": "https://qubes-os.org", "summary": "Qubes template for fedora-31", "description": "Qubes template\n for fedora-31\n"}]}
  2659. '''
  2660. self.__test_list_templates_all_success('info', args, expected)
  2661. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2662. def test_161_list_templates_noresults_fail(self, mock_query):
  2663. mock_query.return_value = []
  2664. args = argparse.Namespace(
  2665. all=False,
  2666. installed=False,
  2667. available=True,
  2668. extras=False,
  2669. upgrades=False,
  2670. machine_readable=False,
  2671. machine_readable_json=False,
  2672. templates=[]
  2673. )
  2674. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  2675. with self.assertRaises(SystemExit):
  2676. qubesadmin.tools.qvm_template.list_templates(
  2677. args, self.app, 'list')
  2678. self.assertTrue('No matching templates' in mock_err.getvalue())
  2679. self.assertEqual(mock_query.mock_calls, [
  2680. mock.call(args, self.app)
  2681. ])
  2682. self.assertAllCalled()
  2683. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2684. def test_170_search_success(self, mock_query):
  2685. mock_query.return_value = [
  2686. qubesadmin.tools.qvm_template.Template(
  2687. 'test-vm',
  2688. '2',
  2689. '4.1',
  2690. '2020',
  2691. 'qubes-templates-itl',
  2692. 1048576,
  2693. datetime.datetime(2020, 9, 1, 14, 30,
  2694. tzinfo=datetime.timezone.utc),
  2695. 'GPL',
  2696. 'https://qubes-os.org',
  2697. 'Qubes template for fedora-31',
  2698. 'Qubes template\n for fedora-31\n'
  2699. ),
  2700. qubesadmin.tools.qvm_template.Template(
  2701. 'test-vm',
  2702. '0',
  2703. '4.1',
  2704. '2020',
  2705. 'qubes-templates-itl',
  2706. 1048576,
  2707. datetime.datetime(2020, 9, 1, 14, 30,
  2708. tzinfo=datetime.timezone.utc),
  2709. 'GPL',
  2710. 'https://qubes-os.org',
  2711. 'Older Qubes template for fedora-31',
  2712. 'Older Qubes template\n for fedora-31\n'
  2713. ),
  2714. qubesadmin.tools.qvm_template.Template(
  2715. 'should-not-match-3',
  2716. '0',
  2717. '4.1',
  2718. '2020',
  2719. 'qubes-templates-itl',
  2720. 1048576,
  2721. datetime.datetime(2020, 9, 1, 14, 30,
  2722. tzinfo=datetime.timezone.utc),
  2723. 'GPL',
  2724. 'https://qubes-os.org/test-vm',
  2725. 'Qubes template for fedora-31',
  2726. 'test-vm Qubes template\n for fedora-31\n'
  2727. )
  2728. ]
  2729. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2730. b'0\x00test-vm-2 class=TemplateVM state=Halted\n'
  2731. for key, val in [
  2732. ('name', 'test-vm-2'),
  2733. ('epoch', '1'),
  2734. ('version', '4.0'),
  2735. ('release', '2019'),
  2736. ('reponame', '@commandline'),
  2737. ('buildtime', '2020-09-02 14:30:00'),
  2738. ('license', 'GPL'),
  2739. ('url', 'https://qubes-os.org'),
  2740. ('summary', 'Summary'),
  2741. ('description', 'Desc|desc')]:
  2742. self.app.expected_calls[(
  2743. 'test-vm-2',
  2744. 'admin.vm.feature.Get',
  2745. f'template-{key}',
  2746. None)] = b'0\0' + val.encode()
  2747. args = argparse.Namespace(
  2748. all=False,
  2749. templates=['test-vm']
  2750. )
  2751. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out, \
  2752. mock.patch.object(self.app.domains['test-vm-2'],
  2753. 'get_disk_utilization') as mock_disk:
  2754. mock_disk.return_value = 1234321
  2755. qubesadmin.tools.qvm_template.search(args, self.app)
  2756. self.assertEqual(mock_out.getvalue(),
  2757. '''=== Name Exactly Matched: test-vm ===
  2758. test-vm : Qubes template for fedora-31
  2759. === Name Matched: test-vm ===
  2760. test-vm-2 : Summary
  2761. ''')
  2762. self.assertEqual(mock_disk.mock_calls, [mock.call()])
  2763. self.assertEqual(mock_query.mock_calls, [
  2764. mock.call(args, self.app)
  2765. ])
  2766. self.assertAllCalled()
  2767. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2768. def test_171_search_summary_success(self, mock_query):
  2769. mock_query.return_value = [
  2770. qubesadmin.tools.qvm_template.Template(
  2771. 'test-template',
  2772. '2',
  2773. '4.1',
  2774. '2020',
  2775. 'qubes-templates-itl',
  2776. 1048576,
  2777. datetime.datetime(2020, 9, 1, 14, 30,
  2778. tzinfo=datetime.timezone.utc),
  2779. 'GPL',
  2780. 'https://qubes-os.org',
  2781. 'Qubes template for test-vm :)',
  2782. 'Qubes template\n for fedora-31\n'
  2783. ),
  2784. qubesadmin.tools.qvm_template.Template(
  2785. 'test-template-exact',
  2786. '2',
  2787. '4.1',
  2788. '2020',
  2789. 'qubes-templates-itl',
  2790. 1048576,
  2791. datetime.datetime(2020, 9, 1, 14, 30,
  2792. tzinfo=datetime.timezone.utc),
  2793. 'GPL',
  2794. 'https://qubes-os.org',
  2795. 'test-vm',
  2796. 'Qubes template\n for fedora-31\n'
  2797. ),
  2798. qubesadmin.tools.qvm_template.Template(
  2799. 'test-vm',
  2800. '2',
  2801. '4.1',
  2802. '2020',
  2803. 'qubes-templates-itl',
  2804. 1048576,
  2805. datetime.datetime(2020, 9, 1, 14, 30,
  2806. tzinfo=datetime.timezone.utc),
  2807. 'GPL',
  2808. 'https://qubes-os.org',
  2809. 'Qubes template for test-vm',
  2810. 'Qubes template\n for fedora-31\n'
  2811. ),
  2812. qubesadmin.tools.qvm_template.Template(
  2813. 'test-vm-2',
  2814. '2',
  2815. '4.1',
  2816. '2020',
  2817. 'qubes-templates-itl',
  2818. 1048576,
  2819. datetime.datetime(2020, 9, 1, 14, 30,
  2820. tzinfo=datetime.timezone.utc),
  2821. 'GPL',
  2822. 'https://qubes-os.org',
  2823. 'Qubes template for test-vm-2',
  2824. 'Qubes template\n for fedora-31\n'
  2825. ),
  2826. ]
  2827. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2828. b'0\x00'
  2829. args = argparse.Namespace(
  2830. all=False,
  2831. templates=['test-vm']
  2832. )
  2833. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  2834. qubesadmin.tools.qvm_template.search(args, self.app)
  2835. self.assertEqual(mock_out.getvalue(),
  2836. '''=== Name & Summary Matched: test-vm ===
  2837. test-vm : Qubes template for test-vm
  2838. test-vm-2 : Qubes template for test-vm-2
  2839. === Summary Matched: test-vm ===
  2840. test-template : Qubes template for test-vm :)
  2841. === Summary Exactly Matched: test-vm ===
  2842. test-template-exact : test-vm
  2843. ''')
  2844. self.assertEqual(mock_query.mock_calls, [
  2845. mock.call(args, self.app)
  2846. ])
  2847. self.assertAllCalled()
  2848. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2849. def test_172_search_namesummaryexact_success(self, mock_query):
  2850. mock_query.return_value = [
  2851. qubesadmin.tools.qvm_template.Template(
  2852. 'test-template-exact',
  2853. '2',
  2854. '4.1',
  2855. '2020',
  2856. 'qubes-templates-itl',
  2857. 1048576,
  2858. datetime.datetime(2020, 9, 1, 14, 30,
  2859. tzinfo=datetime.timezone.utc),
  2860. 'GPL',
  2861. 'https://qubes-os.org',
  2862. 'test-vm',
  2863. 'Qubes template\n for fedora-31\n'
  2864. ),
  2865. qubesadmin.tools.qvm_template.Template(
  2866. 'test-vm',
  2867. '2',
  2868. '4.1',
  2869. '2020',
  2870. 'qubes-templates-itl',
  2871. 1048576,
  2872. datetime.datetime(2020, 9, 1, 14, 30,
  2873. tzinfo=datetime.timezone.utc),
  2874. 'GPL',
  2875. 'https://qubes-os.org',
  2876. 'test-vm',
  2877. 'Qubes template\n for fedora-31\n'
  2878. ),
  2879. qubesadmin.tools.qvm_template.Template(
  2880. 'test-vm-2',
  2881. '2',
  2882. '4.1',
  2883. '2020',
  2884. 'qubes-templates-itl',
  2885. 1048576,
  2886. datetime.datetime(2020, 9, 1, 14, 30,
  2887. tzinfo=datetime.timezone.utc),
  2888. 'GPL',
  2889. 'https://qubes-os.org',
  2890. 'test-vm',
  2891. 'Qubes template\n for fedora-31\n'
  2892. )
  2893. ]
  2894. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2895. b'0\x00'
  2896. args = argparse.Namespace(
  2897. all=False,
  2898. templates=['test-vm']
  2899. )
  2900. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  2901. qubesadmin.tools.qvm_template.search(args, self.app)
  2902. self.assertEqual(mock_out.getvalue(),
  2903. '''=== Name & Summary Exactly Matched: test-vm ===
  2904. test-vm : test-vm
  2905. === Name & Summary Matched: test-vm ===
  2906. test-vm-2 : test-vm
  2907. === Summary Exactly Matched: test-vm ===
  2908. test-template-exact : test-vm
  2909. ''')
  2910. self.assertEqual(mock_query.mock_calls, [
  2911. mock.call(args, self.app)
  2912. ])
  2913. self.assertAllCalled()
  2914. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2915. def test_173_search_multiquery_success(self, mock_query):
  2916. mock_query.return_value = [
  2917. qubesadmin.tools.qvm_template.Template(
  2918. 'test-template-exact',
  2919. '2',
  2920. '4.1',
  2921. '2020',
  2922. 'qubes-templates-itl',
  2923. 1048576,
  2924. datetime.datetime(2020, 9, 1, 14, 30,
  2925. tzinfo=datetime.timezone.utc),
  2926. 'GPL',
  2927. 'https://qubes-os.org',
  2928. 'test-vm',
  2929. 'Qubes template\n for fedora-31\n'
  2930. ),
  2931. qubesadmin.tools.qvm_template.Template(
  2932. 'test-vm',
  2933. '2',
  2934. '4.1',
  2935. '2020',
  2936. 'qubes-templates-itl',
  2937. 1048576,
  2938. datetime.datetime(2020, 9, 1, 14, 30,
  2939. tzinfo=datetime.timezone.utc),
  2940. 'GPL',
  2941. 'https://qubes-os.org',
  2942. 'test-vm',
  2943. 'Qubes template\n for fedora-31\n'
  2944. ),
  2945. qubesadmin.tools.qvm_template.Template(
  2946. 'should-not-match',
  2947. '2',
  2948. '4.1',
  2949. '2020',
  2950. 'qubes-templates-itl',
  2951. 1048576,
  2952. datetime.datetime(2020, 9, 1, 14, 30,
  2953. tzinfo=datetime.timezone.utc),
  2954. 'GPL',
  2955. 'https://qubes-os.org',
  2956. 'Summary',
  2957. 'test-vm Qubes template\n for fedora-31\n'
  2958. )
  2959. ]
  2960. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  2961. b'0\x00'
  2962. args = argparse.Namespace(
  2963. all=False,
  2964. templates=['test-vm', 'test-template']
  2965. )
  2966. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  2967. qubesadmin.tools.qvm_template.search(args, self.app)
  2968. self.assertEqual(mock_out.getvalue(),
  2969. '''=== Name & Summary Matched: test-template, test-vm ===
  2970. test-template-exact : test-vm
  2971. ''')
  2972. self.assertEqual(mock_query.mock_calls, [
  2973. mock.call(args, self.app)
  2974. ])
  2975. self.assertAllCalled()
  2976. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  2977. def test_174_search_multiquery_exact_success(self, mock_query):
  2978. mock_query.return_value = [
  2979. qubesadmin.tools.qvm_template.Template(
  2980. 'test-vm',
  2981. '2',
  2982. '4.1',
  2983. '2020',
  2984. 'qubes-templates-itl',
  2985. 1048576,
  2986. datetime.datetime(2020, 9, 1, 14, 30,
  2987. tzinfo=datetime.timezone.utc),
  2988. 'GPL',
  2989. 'https://qubes-os.org',
  2990. 'summary',
  2991. 'Qubes template\n for fedora-31\n'
  2992. ),
  2993. qubesadmin.tools.qvm_template.Template(
  2994. 'summary',
  2995. '2',
  2996. '4.1',
  2997. '2020',
  2998. 'qubes-templates-itl',
  2999. 1048576,
  3000. datetime.datetime(2020, 9, 1, 14, 30,
  3001. tzinfo=datetime.timezone.utc),
  3002. 'GPL',
  3003. 'https://qubes-os.org',
  3004. 'test-vm Summary',
  3005. 'Qubes template\n for fedora-31\n'
  3006. )
  3007. ]
  3008. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  3009. b'0\x00'
  3010. args = argparse.Namespace(
  3011. all=False,
  3012. templates=['test-vm', 'summary']
  3013. )
  3014. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  3015. qubesadmin.tools.qvm_template.search(args, self.app)
  3016. self.assertEqual(mock_out.getvalue(),
  3017. '''=== Name & Summary Matched: summary, test-vm ===
  3018. summary : test-vm Summary
  3019. === Name & Summary Exactly Matched: summary, test-vm ===
  3020. test-vm : summary
  3021. ''')
  3022. self.assertEqual(mock_query.mock_calls, [
  3023. mock.call(args, self.app)
  3024. ])
  3025. self.assertAllCalled()
  3026. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  3027. def test_175_search_all_success(self, mock_query):
  3028. mock_query.return_value = [
  3029. qubesadmin.tools.qvm_template.Template(
  3030. 'test-vm',
  3031. '2',
  3032. '4.1',
  3033. '2020',
  3034. 'qubes-templates-itl',
  3035. 1048576,
  3036. datetime.datetime(2020, 9, 1, 14, 30,
  3037. tzinfo=datetime.timezone.utc),
  3038. 'GPL',
  3039. 'https://qubes-os.org/keyword-url',
  3040. 'summary',
  3041. 'Qubes template\n for fedora-31\n'
  3042. ),
  3043. qubesadmin.tools.qvm_template.Template(
  3044. 'test-vm-exact',
  3045. '2',
  3046. '4.1',
  3047. '2020',
  3048. 'qubes-templates-itl',
  3049. 1048576,
  3050. datetime.datetime(2020, 9, 1, 14, 30,
  3051. tzinfo=datetime.timezone.utc),
  3052. 'GPL',
  3053. 'https://qubes-os.org',
  3054. 'test-vm Summary',
  3055. 'Qubes template\n for fedora-31\n'
  3056. ),
  3057. qubesadmin.tools.qvm_template.Template(
  3058. 'test-vm-exac2',
  3059. '2',
  3060. '4.1',
  3061. '2020',
  3062. 'qubes-templates-itl',
  3063. 1048576,
  3064. datetime.datetime(2020, 9, 1, 14, 30,
  3065. tzinfo=datetime.timezone.utc),
  3066. 'GPL',
  3067. 'test-vm-exac2',
  3068. 'test-vm Summary',
  3069. 'Qubes template\n for fedora-31\n'
  3070. ),
  3071. qubesadmin.tools.qvm_template.Template(
  3072. 'test-vm-2',
  3073. '2',
  3074. '4.1',
  3075. '2020',
  3076. 'qubes-templates-itl',
  3077. 1048576,
  3078. datetime.datetime(2020, 9, 1, 14, 30,
  3079. tzinfo=datetime.timezone.utc),
  3080. 'GPL',
  3081. 'https://qubes-os.org',
  3082. 'test-vm Summary',
  3083. 'keyword-desc'
  3084. ),
  3085. qubesadmin.tools.qvm_template.Template(
  3086. 'should-not-match',
  3087. '2',
  3088. '4.1',
  3089. '2020',
  3090. 'qubes-templates-itl',
  3091. 1048576,
  3092. datetime.datetime(2020, 9, 1, 14, 30,
  3093. tzinfo=datetime.timezone.utc),
  3094. 'GPL',
  3095. 'https://qubes-os.org',
  3096. 'Summary',
  3097. 'Description'
  3098. )
  3099. ]
  3100. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  3101. b'0\x00'
  3102. args = argparse.Namespace(
  3103. all=True,
  3104. templates=['test-vm-exact', 'test-vm-exac2',
  3105. 'keyword-url', 'keyword-desc']
  3106. )
  3107. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  3108. qubesadmin.tools.qvm_template.search(args, self.app)
  3109. self.assertEqual(mock_out.getvalue(),
  3110. '''=== Name & URL Exactly Matched: test-vm-exac2 ===
  3111. test-vm-exac2 : test-vm Summary
  3112. === Name Exactly Matched: test-vm-exact ===
  3113. test-vm-exact : test-vm Summary
  3114. === Description Exactly Matched: keyword-desc ===
  3115. test-vm-2 : test-vm Summary
  3116. === URL Matched: keyword-url ===
  3117. test-vm : summary
  3118. ''')
  3119. self.assertEqual(mock_query.mock_calls, [
  3120. mock.call(args, self.app)
  3121. ])
  3122. self.assertAllCalled()
  3123. @mock.patch('qubesadmin.tools.qvm_template.qrexec_repoquery')
  3124. def test_176_search_wildcard_success(self, mock_query):
  3125. mock_query.return_value = [
  3126. qubesadmin.tools.qvm_template.Template(
  3127. 'test-vm',
  3128. '2',
  3129. '4.1',
  3130. '2020',
  3131. 'qubes-templates-itl',
  3132. 1048576,
  3133. datetime.datetime(2020, 9, 1, 14, 30,
  3134. tzinfo=datetime.timezone.utc),
  3135. 'GPL',
  3136. 'https://qubes-os.org',
  3137. 'Qubes template for fedora-31',
  3138. 'Qubes template\n for fedora-31\n'
  3139. ),
  3140. qubesadmin.tools.qvm_template.Template(
  3141. 'should-not-match-3',
  3142. '0',
  3143. '4.1',
  3144. '2020',
  3145. 'qubes-templates-itl',
  3146. 1048576,
  3147. datetime.datetime(2020, 9, 1, 14, 30,
  3148. tzinfo=datetime.timezone.utc),
  3149. 'GPL',
  3150. 'https://qubes-os.org/test-vm',
  3151. 'Qubes template for fedora-31',
  3152. 'test-vm Qubes template\n for fedora-31\n'
  3153. )
  3154. ]
  3155. self.app.expected_calls[('dom0', 'admin.vm.List', None, None)] = \
  3156. b'0\x00'
  3157. args = argparse.Namespace(
  3158. all=False,
  3159. templates=['t?st-vm']
  3160. )
  3161. with mock.patch('sys.stdout', new=io.StringIO()) as mock_out:
  3162. qubesadmin.tools.qvm_template.search(args, self.app)
  3163. self.assertEqual(mock_out.getvalue(),
  3164. '''=== Name Matched: t?st-vm ===
  3165. test-vm : Qubes template for fedora-31
  3166. ''')
  3167. self.assertEqual(mock_query.mock_calls, [
  3168. mock.call(args, self.app)
  3169. ])
  3170. self.assertAllCalled()
  3171. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3172. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3173. def test_180_download_success(self, mock_qrexec, mock_dllist):
  3174. with tempfile.TemporaryDirectory() as dir:
  3175. args = argparse.Namespace(
  3176. retries=1
  3177. )
  3178. qubesadmin.tools.qvm_template.download(args, self.app, dir, {
  3179. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3180. ('1', '2', '3'), 'qubes-templates-itl', 1048576),
  3181. 'fedora-32': qubesadmin.tools.qvm_template.DlEntry(
  3182. ('0', '1', '2'),
  3183. 'qubes-templates-itl-testing',
  3184. 2048576)
  3185. }, '.unverified')
  3186. self.assertEqual(mock_qrexec.mock_calls, [
  3187. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3188. dir + '/qubes-template-fedora-31-1:2-3.rpm.unverified',
  3189. 1048576),
  3190. mock.call(args, self.app, 'qubes-template-fedora-32-0:1-2',
  3191. dir + '/qubes-template-fedora-32-0:1-2.rpm.unverified',
  3192. 2048576)
  3193. ])
  3194. self.assertEqual(mock_dllist.mock_calls, [])
  3195. self.assertTrue(all(
  3196. [x.endswith('.unverified') for x in os.listdir(dir)]))
  3197. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3198. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3199. def test_181_download_success_nosuffix(self, mock_qrexec, mock_dllist):
  3200. with tempfile.TemporaryDirectory() as dir:
  3201. args = argparse.Namespace(
  3202. retries=1,
  3203. downloaddir=dir
  3204. )
  3205. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  3206. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3207. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3208. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3209. })
  3210. self.assertEqual(mock_qrexec.mock_calls, [
  3211. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3212. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3213. 1048576)
  3214. ])
  3215. self.assertEqual(mock_dllist.mock_calls, [])
  3216. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3217. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3218. def test_182_download_success_getdllist(self, mock_qrexec, mock_dllist):
  3219. mock_dllist.return_value = {
  3220. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3221. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3222. }
  3223. with tempfile.TemporaryDirectory() as dir:
  3224. args = argparse.Namespace(
  3225. retries=1
  3226. )
  3227. qubesadmin.tools.qvm_template.download(args, self.app,
  3228. dir, None, '.unverified',
  3229. qubesadmin.tools.qvm_template.VersionSelector.LATEST_LOWER)
  3230. self.assertEqual(mock_qrexec.mock_calls, [
  3231. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3232. dir + '/qubes-template-fedora-31-1:2-3.rpm.unverified',
  3233. 1048576)
  3234. ])
  3235. self.assertEqual(mock_dllist.mock_calls, [
  3236. mock.call(args, self.app,
  3237. version_selector=\
  3238. qubesadmin.tools.qvm_template.\
  3239. VersionSelector.LATEST_LOWER)
  3240. ])
  3241. self.assertTrue(all(
  3242. [x.endswith('.unverified') for x in os.listdir(dir)]))
  3243. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3244. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3245. def test_183_download_success_downloaddir(self, mock_qrexec, mock_dllist):
  3246. with tempfile.TemporaryDirectory() as dir:
  3247. args = argparse.Namespace(
  3248. retries=1,
  3249. downloaddir=dir
  3250. )
  3251. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3252. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3253. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3254. }, '.unverified')
  3255. self.assertEqual(mock_qrexec.mock_calls, [
  3256. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3257. dir + '/qubes-template-fedora-31-1:2-3.rpm.unverified',
  3258. 1048576)
  3259. ])
  3260. self.assertEqual(mock_dllist.mock_calls, [])
  3261. self.assertTrue(all(
  3262. [x.endswith('.unverified') for x in os.listdir(dir)]))
  3263. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3264. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3265. def test_184_download_success_exists(self, mock_qrexec, mock_dllist):
  3266. with tempfile.TemporaryDirectory() as dir:
  3267. with open(os.path.join(
  3268. dir, 'qubes-template-fedora-31-1:2-3.rpm.unverified'),
  3269. 'w') as _:
  3270. pass
  3271. args = argparse.Namespace(
  3272. retries=1,
  3273. downloaddir=dir
  3274. )
  3275. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  3276. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3277. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3278. ('1', '2', '3'), 'qubes-templates-itl', 1048576),
  3279. 'fedora-32': qubesadmin.tools.qvm_template.DlEntry(
  3280. ('0', '1', '2'),
  3281. 'qubes-templates-itl-testing',
  3282. 2048576)
  3283. }, '.unverified')
  3284. self.assertTrue('already exists, skipping'
  3285. in mock_err.getvalue())
  3286. self.assertEqual(mock_qrexec.mock_calls, [
  3287. mock.call(args, self.app, 'qubes-template-fedora-32-0:1-2',
  3288. dir + '/qubes-template-fedora-32-0:1-2.rpm.unverified',
  3289. 2048576)
  3290. ])
  3291. self.assertEqual(mock_dllist.mock_calls, [])
  3292. self.assertTrue(all(
  3293. [x.endswith('.unverified') for x in os.listdir(dir)]))
  3294. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3295. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3296. def test_185_download_success_existsmove(self, mock_qrexec, mock_dllist):
  3297. with tempfile.TemporaryDirectory() as dir:
  3298. with open(os.path.join(
  3299. dir, 'qubes-template-fedora-31-1:2-3.rpm'),
  3300. 'w') as _:
  3301. pass
  3302. args = argparse.Namespace(
  3303. retries=1,
  3304. downloaddir=dir
  3305. )
  3306. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  3307. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3308. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3309. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3310. }, '.unverified')
  3311. self.assertTrue('already exists, skipping'
  3312. in mock_err.getvalue())
  3313. self.assertEqual(mock_qrexec.mock_calls, [])
  3314. self.assertEqual(mock_dllist.mock_calls, [])
  3315. self.assertTrue(os.path.exists(
  3316. dir + '/qubes-template-fedora-31-1:2-3.rpm.unverified'))
  3317. self.assertTrue(all(
  3318. [x.endswith('.unverified') for x in os.listdir(dir)]))
  3319. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3320. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3321. def test_186_download_success_existsnosuffix(self, mock_qrexec, mock_dllist):
  3322. with tempfile.TemporaryDirectory() as dir:
  3323. with open(os.path.join(
  3324. dir, 'qubes-template-fedora-31-1:2-3.rpm'),
  3325. 'w') as _:
  3326. pass
  3327. args = argparse.Namespace(
  3328. retries=1,
  3329. downloaddir=dir
  3330. )
  3331. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err:
  3332. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3333. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3334. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3335. })
  3336. self.assertTrue('already exists, skipping'
  3337. in mock_err.getvalue())
  3338. self.assertEqual(mock_qrexec.mock_calls, [])
  3339. self.assertEqual(mock_dllist.mock_calls, [])
  3340. self.assertTrue(os.path.exists(
  3341. dir + '/qubes-template-fedora-31-1:2-3.rpm'))
  3342. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3343. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3344. def test_187_download_success_retry(self, mock_qrexec, mock_dllist):
  3345. counter = 0
  3346. def f(*args):
  3347. nonlocal counter
  3348. counter += 1
  3349. if counter == 1:
  3350. raise ConnectionError
  3351. mock_qrexec.side_effect = f
  3352. with tempfile.TemporaryDirectory() as dir:
  3353. args = argparse.Namespace(
  3354. retries=2,
  3355. downloaddir=dir
  3356. )
  3357. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  3358. mock.patch('os.remove') as mock_rm:
  3359. qubesadmin.tools.qvm_template.download(args, self.app, None, {
  3360. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3361. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3362. })
  3363. self.assertTrue('retrying...' in mock_err.getvalue())
  3364. self.assertEqual(mock_rm.mock_calls, [
  3365. mock.call(dir + '/qubes-template-fedora-31-1:2-3.rpm')
  3366. ])
  3367. self.assertEqual(mock_qrexec.mock_calls, [
  3368. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3369. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3370. 1048576),
  3371. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3372. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3373. 1048576)
  3374. ])
  3375. self.assertEqual(mock_dllist.mock_calls, [])
  3376. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3377. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3378. def test_188_download_fail_retry(self, mock_qrexec, mock_dllist):
  3379. counter = 0
  3380. def f(*args):
  3381. nonlocal counter
  3382. counter += 1
  3383. if counter <= 3:
  3384. raise ConnectionError
  3385. mock_qrexec.side_effect = f
  3386. with tempfile.TemporaryDirectory() as dir:
  3387. args = argparse.Namespace(
  3388. retries=3,
  3389. downloaddir=dir
  3390. )
  3391. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  3392. mock.patch('os.remove') as mock_rm:
  3393. with self.assertRaises(SystemExit):
  3394. qubesadmin.tools.qvm_template.download(
  3395. args, self.app, None, {
  3396. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3397. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3398. })
  3399. self.assertEqual(mock_err.getvalue().count('retrying...'), 2)
  3400. self.assertTrue('download failed' in mock_err.getvalue())
  3401. self.assertEqual(mock_rm.mock_calls, [
  3402. mock.call(dir + '/qubes-template-fedora-31-1:2-3.rpm'),
  3403. mock.call(dir + '/qubes-template-fedora-31-1:2-3.rpm'),
  3404. mock.call(dir + '/qubes-template-fedora-31-1:2-3.rpm')
  3405. ])
  3406. self.assertEqual(mock_qrexec.mock_calls, [
  3407. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3408. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3409. 1048576),
  3410. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3411. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3412. 1048576),
  3413. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3414. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3415. 1048576)
  3416. ])
  3417. self.assertEqual(mock_dllist.mock_calls, [])
  3418. @mock.patch('qubesadmin.tools.qvm_template.get_dl_list')
  3419. @mock.patch('qubesadmin.tools.qvm_template.qrexec_download')
  3420. def test_189_download_fail_interrupt(self, mock_qrexec, mock_dllist):
  3421. def f(*args):
  3422. raise RuntimeError
  3423. mock_qrexec.side_effect = f
  3424. with tempfile.TemporaryDirectory() as dir:
  3425. args = argparse.Namespace(
  3426. retries=3,
  3427. downloaddir=dir
  3428. )
  3429. with mock.patch('sys.stderr', new=io.StringIO()) as mock_err, \
  3430. mock.patch('os.remove') as mock_rm:
  3431. with self.assertRaises(RuntimeError):
  3432. qubesadmin.tools.qvm_template.download(
  3433. args, self.app, None, {
  3434. 'fedora-31': qubesadmin.tools.qvm_template.DlEntry(
  3435. ('1', '2', '3'), 'qubes-templates-itl', 1048576)
  3436. })
  3437. self.assertEqual(mock_rm.mock_calls, [
  3438. mock.call(dir + '/qubes-template-fedora-31-1:2-3.rpm')
  3439. ])
  3440. self.assertEqual(mock_qrexec.mock_calls, [
  3441. mock.call(args, self.app, 'qubes-template-fedora-31-1:2-3',
  3442. dir + '/qubes-template-fedora-31-1:2-3.rpm',
  3443. 1048576)
  3444. ])
  3445. self.assertEqual(mock_dllist.mock_calls, [])