2024-02-12 15:17:18 +00:00
|
|
|
"""
|
|
|
|
A task to start the system upgrade or rebuild by starting a systemd unit.
|
|
|
|
After starting, track the status of the systemd unit and update the Job
|
|
|
|
status accordingly.
|
|
|
|
"""
|
2024-07-26 19:59:44 +00:00
|
|
|
|
2024-02-12 15:17:18 +00:00
|
|
|
import subprocess
|
|
|
|
from selfprivacy_api.utils.huey import huey
|
|
|
|
from selfprivacy_api.jobs import JobStatus, Jobs, Job
|
2024-03-05 08:55:52 +00:00
|
|
|
from selfprivacy_api.utils.waitloop import wait_until_true
|
|
|
|
from selfprivacy_api.utils.systemd import (
|
|
|
|
get_service_status,
|
|
|
|
get_last_log_lines,
|
|
|
|
ServiceStatus,
|
|
|
|
)
|
2024-02-26 19:49:32 +00:00
|
|
|
|
|
|
|
START_TIMEOUT = 60 * 5
|
|
|
|
START_INTERVAL = 1
|
|
|
|
RUN_TIMEOUT = 60 * 60
|
|
|
|
RUN_INTERVAL = 5
|
2024-02-12 15:17:18 +00:00
|
|
|
|
|
|
|
|
2024-03-05 08:55:52 +00:00
|
|
|
def check_if_started(unit_name: str):
|
|
|
|
"""Check if the systemd unit has started"""
|
|
|
|
try:
|
|
|
|
status = get_service_status(unit_name)
|
|
|
|
if status == ServiceStatus.ACTIVE:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def check_running_status(job: Job, unit_name: str):
|
|
|
|
"""Check if the systemd unit is running"""
|
|
|
|
try:
|
|
|
|
status = get_service_status(unit_name)
|
|
|
|
if status == ServiceStatus.INACTIVE:
|
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.FINISHED,
|
|
|
|
result="System rebuilt.",
|
|
|
|
progress=100,
|
|
|
|
)
|
|
|
|
return True
|
|
|
|
if status == ServiceStatus.FAILED:
|
2024-03-06 15:33:55 +00:00
|
|
|
log_lines = get_last_log_lines(unit_name, 10)
|
2024-03-05 08:55:52 +00:00
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.ERROR,
|
2024-03-06 15:33:55 +00:00
|
|
|
error="System rebuild failed. Last log lines:\n" + "\n".join(log_lines),
|
2024-03-05 08:55:52 +00:00
|
|
|
)
|
|
|
|
return True
|
|
|
|
if status == ServiceStatus.ACTIVE:
|
|
|
|
log_lines = get_last_log_lines(unit_name, 1)
|
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.RUNNING,
|
|
|
|
status_text=log_lines[0] if len(log_lines) > 0 else "",
|
|
|
|
)
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2024-03-08 09:04:54 +00:00
|
|
|
def rebuild_system(job: Job, upgrade: bool = False):
|
|
|
|
"""
|
|
|
|
Broken out to allow calling it synchronously.
|
|
|
|
We cannot just block until task is done because it will require a second worker
|
|
|
|
Which we do not have
|
|
|
|
"""
|
|
|
|
|
2024-02-12 15:41:24 +00:00
|
|
|
unit_name = "sp-nixos-upgrade.service" if upgrade else "sp-nixos-rebuild.service"
|
2024-02-12 15:17:18 +00:00
|
|
|
try:
|
2024-02-12 15:41:24 +00:00
|
|
|
command = ["systemctl", "start", unit_name]
|
2024-02-12 15:17:18 +00:00
|
|
|
subprocess.run(
|
|
|
|
command,
|
|
|
|
check=True,
|
|
|
|
start_new_session=True,
|
|
|
|
shell=False,
|
|
|
|
)
|
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.RUNNING,
|
2024-02-12 15:34:55 +00:00
|
|
|
status_text="Starting the system rebuild...",
|
2024-02-12 15:17:18 +00:00
|
|
|
)
|
|
|
|
# Wait for the systemd unit to start
|
2024-03-05 08:55:52 +00:00
|
|
|
try:
|
|
|
|
wait_until_true(
|
|
|
|
lambda: check_if_started(unit_name),
|
|
|
|
timeout_sec=START_TIMEOUT,
|
|
|
|
interval=START_INTERVAL,
|
|
|
|
)
|
|
|
|
except TimeoutError:
|
2024-03-06 15:33:55 +00:00
|
|
|
log_lines = get_last_log_lines(unit_name, 10)
|
2024-03-05 08:55:52 +00:00
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.ERROR,
|
2024-03-06 15:33:55 +00:00
|
|
|
error="System rebuild timed out. Last log lines:\n"
|
|
|
|
+ "\n".join(log_lines),
|
2024-03-05 08:55:52 +00:00
|
|
|
)
|
|
|
|
return
|
2024-02-12 15:17:18 +00:00
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.RUNNING,
|
|
|
|
status_text="Rebuilding the system...",
|
|
|
|
)
|
|
|
|
# Wait for the systemd unit to finish
|
2024-03-05 08:55:52 +00:00
|
|
|
try:
|
|
|
|
wait_until_true(
|
|
|
|
lambda: check_running_status(job, unit_name),
|
|
|
|
timeout_sec=RUN_TIMEOUT,
|
|
|
|
interval=RUN_INTERVAL,
|
|
|
|
)
|
|
|
|
except TimeoutError:
|
2024-03-06 15:33:55 +00:00
|
|
|
log_lines = get_last_log_lines(unit_name, 10)
|
2024-03-05 08:55:52 +00:00
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.ERROR,
|
2024-03-06 15:33:55 +00:00
|
|
|
error="System rebuild timed out. Last log lines:\n"
|
|
|
|
+ "\n".join(log_lines),
|
2024-03-05 08:55:52 +00:00
|
|
|
)
|
|
|
|
return
|
2024-02-12 15:17:18 +00:00
|
|
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
Jobs.update(
|
|
|
|
job=job,
|
|
|
|
status=JobStatus.ERROR,
|
|
|
|
status_text=str(e),
|
|
|
|
)
|
2024-03-08 09:04:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
@huey.task()
|
|
|
|
def rebuild_system_task(job: Job, upgrade: bool = False):
|
|
|
|
"""Rebuild the system"""
|
|
|
|
rebuild_system(job, upgrade)
|