mirror of
https://github.com/isjerryxiao/pacroller.git
synced 2024-11-25 17:20:41 +08:00
add needrestart support
This commit is contained in:
parent
fc23239e30
commit
58b8818218
5 changed files with 82 additions and 12 deletions
|
@ -145,7 +145,7 @@ def _stdout_parser(stdout: List[str], report: checkReport) -> None:
|
|||
if _m := REGEX['s_upgrade_pkg'].match(line):
|
||||
logger.debug(f's_upgrade_pkg {line=}')
|
||||
else:
|
||||
report.crit(f'{line=} is unknown')
|
||||
logger.debug(f'stdout {line=} is unknown')
|
||||
else:
|
||||
logger.debug(f'skip {line=}')
|
||||
ln += 1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"timeout": 300,
|
||||
"upgrade_timeout": 3600,
|
||||
"network_retry": 5,
|
||||
"custom_sync": false,
|
||||
|
@ -12,5 +13,8 @@
|
|||
"ignored_pacnew": [
|
||||
"/etc/locale.gen",
|
||||
"/etc/pacman.d/mirrorlist"
|
||||
]
|
||||
],
|
||||
"need_restart": false,
|
||||
"need_restart_cmd": ["needrestart", "-r", "a", "-m", "a", "-l"],
|
||||
"systemd-check": true
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@ if (cfg := (CONFIG_DIR / CONFIG_FILE)).exists():
|
|||
else:
|
||||
_config = dict()
|
||||
|
||||
TIMEOUT = int(_config.get('timeout', 300))
|
||||
UPGRADE_TIMEOUT = int(_config.get('upgrade_timeout', 3600))
|
||||
NETWORK_RETRY = int(_config.get('network_retry', 5))
|
||||
assert UPGRADE_TIMEOUT > 0 and NETWORK_RETRY > 0
|
||||
assert TIMEOUT > 0 and UPGRADE_TIMEOUT > 0 and NETWORK_RETRY > 0
|
||||
|
||||
CUSTOM_SYNC = bool(_config.get('custom_sync', False))
|
||||
SYNC_SH = CONFIG_DIR / str(_config.get('sync_shell', "sync.sh"))
|
||||
|
@ -31,4 +32,12 @@ for (k, v) in HOLD.items():
|
|||
assert isinstance(k, str) and isinstance(v, str)
|
||||
|
||||
IGNORED_PACNEW = _config.get('ignored_pacnew', list())
|
||||
assert isinstance(IGNORED_PACNEW, list)
|
||||
for i in IGNORED_PACNEW:
|
||||
assert isinstance(i, str)
|
||||
|
||||
NEEDRESTART = bool(_config.get('need_restart', False))
|
||||
NEEDRESTART_CMD = _config.get('need_restart_cmd', False)
|
||||
for i in NEEDRESTART_CMD:
|
||||
assert isinstance(i, str)
|
||||
|
||||
SYSTEMD = bool(_config.get('systemd-check', True))
|
||||
|
|
|
@ -13,7 +13,8 @@ from typing import List, Iterator
|
|||
from pacroller.utils import execute_with_io, UnknownQuestionError, back_readline
|
||||
from pacroller.checker import log_checker, sync_err_is_net, upgrade_err_is_net, checkReport
|
||||
from pacroller.config import (CONFIG_DIR, CONFIG_FILE, LIB_DIR, DB_FILE, PACMAN_LOG, PACMAN_CONFIG,
|
||||
UPGRADE_TIMEOUT, NETWORK_RETRY, CUSTOM_SYNC, SYNC_SH, EXTRA_SAFE, SHELL, HOLD)
|
||||
TIMEOUT, UPGRADE_TIMEOUT, NETWORK_RETRY, CUSTOM_SYNC, SYNC_SH,
|
||||
EXTRA_SAFE, SHELL, HOLD, NEEDRESTART, NEEDRESTART_CMD, SYSTEMD)
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
@ -29,6 +30,10 @@ class PackageHold(Exception):
|
|||
pass
|
||||
class CheckFailed(Exception):
|
||||
pass
|
||||
class NeedrestartFailed(Exception):
|
||||
pass
|
||||
class SystemdNotRunning(Exception):
|
||||
pass
|
||||
|
||||
def sync() -> None:
|
||||
logger.info('sync start')
|
||||
|
@ -43,7 +48,7 @@ def sync() -> None:
|
|||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
encoding='utf-8',
|
||||
timeout=300
|
||||
timeout=TIMEOUT
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
if sync_err_is_net(e.output):
|
||||
|
@ -99,7 +104,7 @@ def upgrade() -> List[str]:
|
|||
examine_upgrade(t.to_add, t.to_remove)
|
||||
finally:
|
||||
t.release()
|
||||
pacman_output = execute_with_io(['pacman', '-Su', '--noprogressbar', '--color', 'never'])
|
||||
pacman_output = execute_with_io(['pacman', '-Su', '--noprogressbar', '--color', 'never'], UPGRADE_TIMEOUT)
|
||||
logger.info('upgrade end')
|
||||
return pacman_output
|
||||
|
||||
|
@ -132,7 +137,13 @@ def do_system_upgrade(debug=False) -> checkReport:
|
|||
with open(PACMAN_LOG, 'r') as pacman_log:
|
||||
pacman_log.seek(log_anchor)
|
||||
log = pacman_log.read().split('\n')
|
||||
report = log_checker(stdout, log, debug=debug)
|
||||
try:
|
||||
report = log_checker(stdout, log, debug=debug)
|
||||
except Exception:
|
||||
logger.exception('checker has crashed, here is the debug info')
|
||||
_report = log_checker(stdout, log, debug=True)
|
||||
raise
|
||||
|
||||
logger.info(report.summary(verbose=True, show_package=False))
|
||||
return report
|
||||
|
||||
|
@ -156,6 +167,21 @@ def has_previous_error() -> Exception:
|
|||
else:
|
||||
return None
|
||||
|
||||
def is_system_failed() -> str:
|
||||
try:
|
||||
p = subprocess.run(["systemctl", "is-system-running"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
|
||||
stdin=subprocess.DEVNULL,
|
||||
timeout=20, encoding='utf-8')
|
||||
except Exception:
|
||||
ret = "exec fail"
|
||||
else:
|
||||
ret = p.stdout.strip()
|
||||
if ret == 'running':
|
||||
return None
|
||||
else:
|
||||
return ret
|
||||
|
||||
def main() -> None:
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Pacman Automatic Rolling Helper')
|
||||
|
@ -171,7 +197,12 @@ def main() -> None:
|
|||
|
||||
if args.action == 'run':
|
||||
if getuid() != 0:
|
||||
parser.error('you need to be root')
|
||||
logger.error('you need to be root')
|
||||
exit(1)
|
||||
if SYSTEMD:
|
||||
if _s := is_system_failed():
|
||||
logger.error(f'systemd is not in {_s} state, refused')
|
||||
exit(11)
|
||||
if prev_err := has_previous_error():
|
||||
logger.error(f'Cannot continue, a previous error {prev_err} is still present. Please resolve this issue and run fail-reset.')
|
||||
else:
|
||||
|
@ -190,7 +221,28 @@ def main() -> None:
|
|||
else:
|
||||
if report._warn or report._crit:
|
||||
exc = CheckFailed('manual inspection required')
|
||||
if exc:
|
||||
write_db(report, exc)
|
||||
exit(2)
|
||||
if NEEDRESTART:
|
||||
try:
|
||||
p = subprocess.run(
|
||||
NEEDRESTART_CMD,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
encoding='utf-8',
|
||||
timeout=TIMEOUT
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f'needrestart failed with {e.returncode=} {e.output=}')
|
||||
exc = NeedrestartFailed(f'{e.returncode=}')
|
||||
else:
|
||||
logger.debug(f'needrestart {p.stdout=}')
|
||||
write_db(report, exc)
|
||||
if exc:
|
||||
exit(2)
|
||||
|
||||
elif args.action == 'status':
|
||||
count = 0
|
||||
for entry in read_db():
|
||||
|
@ -202,8 +254,13 @@ def main() -> None:
|
|||
break
|
||||
print()
|
||||
elif args.action == 'fail-reset':
|
||||
if SYSTEMD:
|
||||
if _s := is_system_failed():
|
||||
logger.error(f'systemd is not in {_s} state, refused')
|
||||
exit(11)
|
||||
if getuid() != 0:
|
||||
parser.error('you need to be root')
|
||||
logger.error('you need to be root')
|
||||
exit(1)
|
||||
if prev_err := has_previous_error():
|
||||
write_db(None)
|
||||
logger.info(f'reset previous error {prev_err}')
|
||||
|
|
|
@ -14,7 +14,7 @@ class UnknownQuestionError(subprocess.SubprocessError):
|
|||
def __str__(self):
|
||||
return f"Pacman returned an unknown question {self.question}"
|
||||
|
||||
def execute_with_io(command: List[str]) -> List[str]:
|
||||
def execute_with_io(command: List[str], timeout: int = 3600) -> List[str]:
|
||||
'''
|
||||
captures stdout and stderr and
|
||||
automatically handles [y/n] questions of pacman
|
||||
|
@ -41,7 +41,7 @@ def execute_with_io(command: List[str]) -> List[str]:
|
|||
stderr=subprocess.STDOUT,
|
||||
encoding='utf-8'
|
||||
)
|
||||
Thread(target=set_timeout, args=(p, 3600), daemon=True).start() # should be configurable
|
||||
Thread(target=set_timeout, args=(p, timeout), daemon=True).start() # should be configurable
|
||||
line = ''
|
||||
output = ''
|
||||
while (r := p.stdout.read(1)) != '':
|
||||
|
|
Loading…
Reference in a new issue