Skip to content

Commit 19d64ca

Browse files
committed
Use admin utils wrappers for vm actions
1 parent 497d467 commit 19d64ca

5 files changed

Lines changed: 53 additions & 56 deletions

File tree

vmupdate/qube_connection.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
# along with this program; if not, write to the Free Software
1919
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
2020
# USA.
21+
22+
import asyncio
2123
import os
2224
import shutil
2325
import signal
@@ -30,11 +32,11 @@
3032

3133
import qubesadmin
3234
import qubesadmin.exc
35+
from qubesadmin.utils import shutdown
3336
from vmupdate.agent.source.args import AgentArgs
3437
from vmupdate.agent.source.log_config import LOGPATH, LOG_FILE
3538
from vmupdate.agent.source.status import StatusInfo, FinalStatus, FormatedLine
3639
from vmupdate.agent.source.common.process_result import ProcessResult
37-
from vmupdate.utils import shutdown_domains
3840

3941

4042
class QubeConnection:
@@ -93,15 +95,20 @@ def __exit__(self, exc_type, exc_val, exc_tb):
9395
)
9496

9597
if self.qube.is_running() and not self._initially_running:
98+
wait = False
9699
if self._has_assigned_pci_devices(self.qube):
97100
self.logger.info(
98101
"Waiting for full shutdown %s (PCI devices assigned)",
99102
self.qube.name,
100103
)
101-
shutdown_domains([self.qube], self.logger)
102-
else:
103-
self.logger.info("Shutdown %s", self.qube.name)
104-
self.qube.shutdown()
104+
wait = True
105+
failed = asyncio.run(
106+
shutdown(domains=[self.qube], force=False, wait=wait)
107+
)
108+
if failed:
109+
exc = list(failed.values())[0]
110+
self.logger.error(str(exc))
111+
raise exc
105112

106113
self.__connected = False
107114

vmupdate/tests/test_qube_connection.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
from vmupdate.qube_connection import QubeConnection
2424

2525

26-
@patch("vmupdate.qube_connection.shutdown_domains")
27-
def test_wait_for_shutdown_when_vm_started_by_update(shutdown_domains):
26+
def test_wait_for_shutdown_when_vm_started_by_update():
2827
vm = Mock()
2928
vm.name = "hvm1"
3029
vm.is_running.side_effect = [False, True]
@@ -43,12 +42,10 @@ def test_wait_for_shutdown_when_vm_started_by_update(shutdown_domains):
4342
):
4443
pass
4544

46-
shutdown_domains.assert_called_once_with([vm], logger)
47-
vm.shutdown.assert_not_called()
45+
vm.shutdown.assert_called_once_with(force=False, wait=True)
4846

4947

50-
@patch("vmupdate.qube_connection.shutdown_domains")
51-
def test_do_not_wait_for_shutdown_without_assigned_pci(shutdown_domains):
48+
def test_do_not_wait_for_shutdown_without_assigned_pci():
5249
vm = Mock()
5350
vm.name = "hvm2"
5451
vm.is_running.side_effect = [False, True]
@@ -67,12 +64,10 @@ def test_do_not_wait_for_shutdown_without_assigned_pci(shutdown_domains):
6764
):
6865
pass
6966

70-
vm.shutdown.assert_called_once_with()
71-
shutdown_domains.assert_not_called()
67+
vm.shutdown.assert_called_once_with(force=False, wait=False)
7268

7369

74-
@patch("vmupdate.qube_connection.shutdown_domains")
75-
def test_do_not_shutdown_if_vm_was_already_running(shutdown_domains):
70+
def test_do_not_shutdown_if_vm_was_already_running():
7671
vm = Mock()
7772
vm.name = "hvm3"
7873
vm.is_running.return_value = True
@@ -92,4 +87,3 @@ def test_do_not_shutdown_if_vm_was_already_running(shutdown_domains):
9287
pass
9388

9489
vm.shutdown.assert_not_called()
95-
shutdown_domains.assert_not_called()

vmupdate/tests/test_vmupdate.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -383,11 +383,9 @@ def test_selection(
383383
@patch("vmupdate.update_manager.UpdateAgentManager")
384384
@patch("multiprocessing.Pool")
385385
@patch("multiprocessing.Manager")
386-
@patch("asyncio.run")
387386
@patch("subprocess.Popen")
388387
def test_restarting(
389388
dummy_subprocess,
390-
arun,
391389
mp_manager,
392390
mp_pool,
393391
agent_mng,
@@ -506,7 +504,6 @@ def test_restarting(
506504

507505
fails = {args: failed[args] for args in failed if failed[args]}
508506
assert not fails
509-
arun.asseert_called()
510507

511508

512509
stat = FinalStatus
@@ -719,7 +716,6 @@ def test_error(
719716
@patch("os.chown")
720717
@patch("logging.FileHandler")
721718
@patch("logging.getLogger")
722-
@patch("asyncio.run")
723719
@pytest.mark.parametrize(
724720
"action, code",
725721
(
@@ -729,7 +725,6 @@ def test_error(
729725
),
730726
)
731727
def test_error_apply(
732-
_arun,
733728
_logger,
734729
_log_file,
735730
_chmod,

vmupdate/utils.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,27 +22,40 @@
2222
from datetime import datetime
2323

2424
import qubesadmin.exc
25-
from qubesadmin.events.utils import wait_for_domain_shutdown
25+
from qubesadmin.utils import shutdown, start
2626
from vmupdate.agent.source.common.exit_codes import EXIT
2727

2828

29-
def shutdown_domains(to_shutdown, log):
29+
async def shutdown_domains(
30+
to_shutdown,
31+
log,
32+
wait: bool = False,
33+
force: bool = False,
34+
):
3035
"""
3136
Try to shut down vms and wait to finish.
3237
"""
3338
ret_code = EXIT.OK
34-
wait_for = []
35-
for vm in to_shutdown:
36-
try:
37-
vm.shutdown(force=True)
38-
wait_for.append(vm)
39-
except qubesadmin.exc.QubesVMError as exc:
40-
log.error(str(exc))
41-
ret_code = EXIT.ERR_SHUTDOWN_APP
39+
all_failed = []
40+
failed = await shutdown(domains=to_shutdown, wait=wait, force=force)
41+
for qube, exc in failed.items():
42+
log.error(str(exc))
43+
all_failed.append(qube)
44+
ret_code = EXIT.ERR_SHUTDOWN_APP
45+
done = [qube for qube in to_shutdown if qube not in all_failed]
46+
return ret_code, done
4247

43-
asyncio.run(wait_for_domain_shutdown(wait_for))
4448

45-
return ret_code, wait_for
49+
async def restart_vms(to_restart, log):
50+
"""
51+
Try to restart vms.
52+
"""
53+
ret_code, shutdowns = await shutdown_domains(to_restart, log)
54+
failed = await start(domains=shutdowns)
55+
for exc in failed.values():
56+
log.error(str(exc))
57+
ret_code = EXIT.ERR_START_APP
58+
return ret_code
4659

4760

4861
def get_feature(vm, feature_name, default_value=None):

vmupdate/vmupdate.py

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@
44
"""
55

66
import argparse
7+
import asyncio
78
import logging
89
import sys
910
import os
1011
import grp
1112
from typing import Set, Iterable, Dict, Tuple
1213

1314
import qubesadmin
15+
import qubesadmin.utils
1416
import qubesadmin.exc
1517
from vmupdate.agent.source.status import FinalStatus
1618
from vmupdate.agent.source.common.exit_codes import EXIT
1719
from vmupdate.utils import (
1820
shutdown_domains,
21+
restart_vms,
1922
get_feature,
2023
get_boolean_feature,
2124
is_stale,
@@ -119,8 +122,10 @@ def main(args=None, app=qubesadmin.Qubes()):
119122
if ret_code_appvm == EXIT.SIGINT:
120123
return EXIT.SIGINT
121124

122-
ret_code_restart = apply_updates_to_appvm(
123-
args, independent, templ_statuses, app_statuses, log
125+
ret_code_restart = asyncio.run(
126+
apply_updates_to_appvm(
127+
args, independent, templ_statuses, app_statuses, log
128+
)
124129
)
125130

126131
ret_code = max(
@@ -396,7 +401,7 @@ def run_update(
396401
return ret_code, statuses
397402

398403

399-
def apply_updates_to_appvm(
404+
async def apply_updates_to_appvm(
400405
args,
401406
vm_updated: Iterable,
402407
template_statuses: Dict[str, FinalStatus],
@@ -445,7 +450,7 @@ def apply_updates_to_appvm(
445450

446451
# first shutdown templates to apply changes to the root volume
447452
# they are no need to start templates automatically
448-
ret_code, _ = shutdown_domains(templates_to_shutdown, log)
453+
ret_code, _ = await shutdown_domains(templates_to_shutdown, log)
449454

450455
if ret_code != EXIT.OK:
451456
log.error("Shutdown of some templates fails with code %d", ret_code)
@@ -464,11 +469,11 @@ def apply_updates_to_appvm(
464469
)
465470

466471
# both flags `restart` and `apply-to-all` include service vms
467-
ret_code_ = restart_vms(to_restart, log)
472+
ret_code_ = await restart_vms(to_restart, log)
468473
ret_code = max(ret_code, ret_code_)
469474
if args.apply_to_all:
470475
# there is no need to start plain AppVMs automatically
471-
ret_code_, _ = shutdown_domains(to_shutdown, log)
476+
ret_code_, _ = await shutdown_domains(to_shutdown, log)
472477
ret_code = max(ret_code, ret_code_)
473478

474479
return ret_code
@@ -496,22 +501,5 @@ def get_derived_vm_to_apply(templates, derived_statuses):
496501
return to_restart, to_shutdown
497502

498503

499-
def restart_vms(to_restart, log):
500-
"""
501-
Try to restart vms.
502-
"""
503-
ret_code, shutdowns = shutdown_domains(to_restart, log)
504-
505-
# restart shutdown qubes
506-
for vm in shutdowns:
507-
try:
508-
vm.start()
509-
except qubesadmin.exc.QubesVMError as exc:
510-
log.error(str(exc))
511-
ret_code = EXIT.ERR_START_APP
512-
513-
return ret_code
514-
515-
516504
if __name__ == "__main__":
517505
sys.exit(main())

0 commit comments

Comments
 (0)