Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/pr/150'
Browse files Browse the repository at this point in the history
* origin/pr/150:
  updater: fix status checking
  updater: qapp as argument
  updater: fix QubesOS/qubes-issues/issues/9031
  • Loading branch information
marmarek committed Apr 23, 2024
2 parents 5aafcef + 298a230 commit a954e65
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 112 deletions.
60 changes: 32 additions & 28 deletions vmupdate/agent/source/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,33 @@
class AgentArgs:
# To avoid code repeating when we want to retrieve arguments
OPTIONS = {
"log": {"action": 'store',
"default": "INFO",
"help": 'Provide logging level. Values: DEBUG, INFO (default) '
'WARNING, ERROR, CRITICAL'},
"no-refresh": {"action": 'store_true',
"help": 'Do not refresh available packages before '
'upgrading'},
"force-upgrade": {"action": 'store_true',
"help": 'Try upgrade even if errors are '
'encountered (like a refresh error)'},
"leave-obsolete": {"action": 'store_true',
"help": 'Do not remove obsolete packages during '
'upgrading'},
("--log",): {"action": 'store',
"default": "INFO",
"help": 'Provide logging level. Values: DEBUG, '
'INFO (default), WARNING, ERROR, CRITICAL'},
("--no-refresh",): {"action": 'store_true',
"help": 'Do not refresh available packages before '
'upgrading'},
("--force-upgrade", "-f"):
{"action": 'store_true',
"help": 'Try upgrade even if errors are '
'encountered (like a refresh error)'},
("--leave-obsolete",): {
"action": 'store_true',
"help": 'Do not remove obsolete packages during upgrading'},
}
EXCLUSIVE_OPTIONS_1 = {
"show-output": {"action": 'store_true',
"help": 'Show output of management commands'},
"quiet": {"action": 'store_true',
"help": 'Do not print anything to stdout'}
("--show-output", "--verbose", "-v"):
{"action": 'store_true',
"help": 'Show output of management commands'},
("--quiet", "-q"): {"action": 'store_true',
"help": 'Do not print anything to stdout'}
}
EXCLUSIVE_OPTIONS_2 = {
"no-progress": {"action": "store_true",
"help": "Do not show upgrading progress."},
"just-print-progress": {"action": "store_true",
"help": argparse.SUPPRESS}
("--no-progress",): {"action": "store_true",
"help": "Do not show upgrading progress."},
("--just-print-progress",): {"action": "store_true",
"help": argparse.SUPPRESS}
}
ALL_OPTIONS = {**OPTIONS, **EXCLUSIVE_OPTIONS_1, **EXCLUSIVE_OPTIONS_2}

Expand All @@ -58,13 +60,13 @@ def add_arguments(parser):
Add common arguments to the parser.
"""
for arg, properties in AgentArgs.OPTIONS.items():
parser.add_argument('--' + arg, **properties)
parser.add_argument(*arg, **properties)
verbosity = parser.add_mutually_exclusive_group()
for arg, properties in AgentArgs.EXCLUSIVE_OPTIONS_1.items():
verbosity.add_argument('--' + arg, **properties)
verbosity.add_argument(*arg, **properties)
progress_reporting = parser.add_mutually_exclusive_group()
for arg, properties in AgentArgs.EXCLUSIVE_OPTIONS_2.items():
progress_reporting.add_argument('--' + arg, **properties)
progress_reporting.add_argument(*arg, **properties)

@staticmethod
def to_cli_args(args):
Expand All @@ -75,10 +77,12 @@ def to_cli_args(args):
args_dict = vars(args)

cli_args = []
for key, value in AgentArgs.ALL_OPTIONS.items():
for keys, value in AgentArgs.ALL_OPTIONS.items():
# keys[0] since first value is used as attribute name in parser
param_name = keys[0][2:].replace("-", "_")
if value["action"] == "store_true":
if args_dict[key.replace("-", "_")]:
cli_args.append("--" + key)
if args_dict[param_name]:
cli_args.append(keys[0])
else:
cli_args.extend(("--" + key, args_dict[key.replace("-", "_")]))
cli_args.extend((keys[0], args_dict[param_name]))
return cli_args
8 changes: 8 additions & 0 deletions vmupdate/agent/source/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ class FinalStatus(Enum):
ERROR = "error"
CANCELLED = "cancelled"
NO_UPDATES = "no updates"
UNKNOWN = "unknown"

@classmethod
def __missing__(cls, key):
return cls.UNKNOWN

def __bool__(self):
return self == FinalStatus.SUCCESS


class StatusInfo:
Expand Down
41 changes: 24 additions & 17 deletions vmupdate/update_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class UpdateManager:
Update multiple qubes simultaneously.
"""

def __init__(self, qubes, args):
def __init__(self, qubes, args, log):
self.qubes = qubes
self.max_concurrency = args.max_concurrency
self.show_output = args.show_output
Expand All @@ -56,13 +56,16 @@ def __init__(self, qubes, args):
self.buffer = ""
self.cleanup = not args.no_cleanup
self.ret_code = 0
self.log = log

def run(self, agent_args):
"""
Run simultaneously `update_qube` for all qubes as separate processes.
"""
self.log.info("Update Manager: New batch of qubes to update")
if not self.qubes:
return 0
self.log.info("Update Manager: No qubes to update, quiting.")
return 0, {q.name: FinalStatus.SUCCESS for q in self.qubes}

show_progress = not self.quiet and not self.no_progress
SimpleTerminalBar.reinit_class()
Expand All @@ -79,7 +82,7 @@ def run(self, agent_args):
progress_bar.add_bar(qube.name)
progress_bar.pool.apply_async(
update_qube,
(qube.name, agent_args, show_progress,
(qube, agent_args, show_progress,
progress_bar.status_notifier, progress_bar.termination),
callback=self.collect_result, error_callback=print
)
Expand All @@ -88,10 +91,18 @@ def run(self, agent_args):
progress_bar.feeding()
progress_bar.pool.join()
progress_bar.close()
self.log.info("Update Manager: Finished, collecting success info")

stats = list(progress_bar.statuses.values())
if FinalStatus.ERROR in stats:
self.ret_code = max(self.ret_code, 5)
if FinalStatus.UNKNOWN in stats:
self.ret_code = max(self.ret_code, 6)

if self.buffer:
print(self.buffer)

return self.ret_code
return self.ret_code, progress_bar.statuses

def collect_result(self, result_tuple: Tuple[str, ProcessResult]):
"""
Expand Down Expand Up @@ -182,11 +193,12 @@ def __init__(self, dummy, output, max_concurrency, printer: Optional):
# in pool
signal.signal(signal.SIGINT, signal.SIG_IGN)
self.pool = multiprocessing.Pool(max_concurrency)
# set SIGINT handler to gracefully termination
# set SIGINT handler to graceful termination
signal.signal(signal.SIGINT, self.signal_handler_during_feeding)

self.progresses = {}
self.progress_bars = {}
self.statuses = {}
self.output_class = output
self.print = printer

Expand Down Expand Up @@ -224,6 +236,7 @@ def feeding(self):
if feed.status == Status.DONE:
left_to_finish -= 1
status_name = feed.info.value
self.statuses[feed.qname] = FinalStatus(status_name)
self.progress_bars[feed.qname].set_description(
f"{feed.qname} ({status_name})")
if feed.status == Status.UPDATING:
Expand Down Expand Up @@ -256,32 +269,26 @@ def close(self):


def update_qube(
qname, agent_args, show_progress, status_notifier, termination
qube, agent_args, show_progress, status_notifier, termination
) -> Tuple[str, ProcessResult]:
"""
Create and run `UpdateAgentManager` for qube.
:param qname: name of qube
:param qube: vm to update
:param agent_args: args for agent entrypoint
:param show_progress: if progress should be printed in real time
:param status_notifier: object to be fed with the progress data
:param status_notifier: an object to be fed with the progress data
:param termination: signal to gracefully terminate subprocess
:return:
"""
app = qubesadmin.Qubes()
try:
qube = app.domains[qname]
except KeyError:
return qname, ProcessResult(2, "ERROR (qube not found)")

if termination.value:
status_notifier.put(StatusInfo.done(qube, FinalStatus.CANCELLED))
return qname, ProcessResult(130, "Canceled")
return qube.name, ProcessResult(130, "Canceled")
status_notifier.put(StatusInfo.updating(qube, 0))

try:
runner = UpdateAgentManager(
app,
qube.app,
qube,
agent_args=agent_args,
show_progress=show_progress
Expand All @@ -292,7 +299,7 @@ def update_qube(
termination=termination
)
except Exception as exc: # pylint: disable=broad-except
return qname, ProcessResult(1, f"ERROR (exception {str(exc)})")
return qube.name, ProcessResult(1, f"ERROR (exception {str(exc)})")
return qube.name, result


Expand Down
Loading

0 comments on commit a954e65

Please sign in to comment.