mirror of
https://github.com/isjerryxiao/pacroller.git
synced 2024-11-23 00:00:44 +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):
|
if _m := REGEX['s_upgrade_pkg'].match(line):
|
||||||
logger.debug(f's_upgrade_pkg {line=}')
|
logger.debug(f's_upgrade_pkg {line=}')
|
||||||
else:
|
else:
|
||||||
report.crit(f'{line=} is unknown')
|
logger.debug(f'stdout {line=} is unknown')
|
||||||
else:
|
else:
|
||||||
logger.debug(f'skip {line=}')
|
logger.debug(f'skip {line=}')
|
||||||
ln += 1
|
ln += 1
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"timeout": 300,
|
||||||
"upgrade_timeout": 3600,
|
"upgrade_timeout": 3600,
|
||||||
"network_retry": 5,
|
"network_retry": 5,
|
||||||
"custom_sync": false,
|
"custom_sync": false,
|
||||||
|
@ -12,5 +13,8 @@
|
||||||
"ignored_pacnew": [
|
"ignored_pacnew": [
|
||||||
"/etc/locale.gen",
|
"/etc/locale.gen",
|
||||||
"/etc/pacman.d/mirrorlist"
|
"/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:
|
else:
|
||||||
_config = dict()
|
_config = dict()
|
||||||
|
|
||||||
|
TIMEOUT = int(_config.get('timeout', 300))
|
||||||
UPGRADE_TIMEOUT = int(_config.get('upgrade_timeout', 3600))
|
UPGRADE_TIMEOUT = int(_config.get('upgrade_timeout', 3600))
|
||||||
NETWORK_RETRY = int(_config.get('network_retry', 5))
|
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))
|
CUSTOM_SYNC = bool(_config.get('custom_sync', False))
|
||||||
SYNC_SH = CONFIG_DIR / str(_config.get('sync_shell', "sync.sh"))
|
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)
|
assert isinstance(k, str) and isinstance(v, str)
|
||||||
|
|
||||||
IGNORED_PACNEW = _config.get('ignored_pacnew', list())
|
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.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.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,
|
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()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
@ -29,6 +30,10 @@ class PackageHold(Exception):
|
||||||
pass
|
pass
|
||||||
class CheckFailed(Exception):
|
class CheckFailed(Exception):
|
||||||
pass
|
pass
|
||||||
|
class NeedrestartFailed(Exception):
|
||||||
|
pass
|
||||||
|
class SystemdNotRunning(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
def sync() -> None:
|
def sync() -> None:
|
||||||
logger.info('sync start')
|
logger.info('sync start')
|
||||||
|
@ -43,7 +48,7 @@ def sync() -> None:
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
encoding='utf-8',
|
encoding='utf-8',
|
||||||
timeout=300
|
timeout=TIMEOUT
|
||||||
)
|
)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
if sync_err_is_net(e.output):
|
if sync_err_is_net(e.output):
|
||||||
|
@ -99,7 +104,7 @@ def upgrade() -> List[str]:
|
||||||
examine_upgrade(t.to_add, t.to_remove)
|
examine_upgrade(t.to_add, t.to_remove)
|
||||||
finally:
|
finally:
|
||||||
t.release()
|
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')
|
logger.info('upgrade end')
|
||||||
return pacman_output
|
return pacman_output
|
||||||
|
|
||||||
|
@ -132,7 +137,13 @@ def do_system_upgrade(debug=False) -> checkReport:
|
||||||
with open(PACMAN_LOG, 'r') as pacman_log:
|
with open(PACMAN_LOG, 'r') as pacman_log:
|
||||||
pacman_log.seek(log_anchor)
|
pacman_log.seek(log_anchor)
|
||||||
log = pacman_log.read().split('\n')
|
log = pacman_log.read().split('\n')
|
||||||
|
try:
|
||||||
report = log_checker(stdout, log, debug=debug)
|
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))
|
logger.info(report.summary(verbose=True, show_package=False))
|
||||||
return report
|
return report
|
||||||
|
|
||||||
|
@ -156,6 +167,21 @@ def has_previous_error() -> Exception:
|
||||||
else:
|
else:
|
||||||
return None
|
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:
|
def main() -> None:
|
||||||
import argparse
|
import argparse
|
||||||
parser = argparse.ArgumentParser(description='Pacman Automatic Rolling Helper')
|
parser = argparse.ArgumentParser(description='Pacman Automatic Rolling Helper')
|
||||||
|
@ -171,7 +197,12 @@ def main() -> None:
|
||||||
|
|
||||||
if args.action == 'run':
|
if args.action == 'run':
|
||||||
if getuid() != 0:
|
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():
|
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.')
|
logger.error(f'Cannot continue, a previous error {prev_err} is still present. Please resolve this issue and run fail-reset.')
|
||||||
else:
|
else:
|
||||||
|
@ -190,7 +221,28 @@ def main() -> None:
|
||||||
else:
|
else:
|
||||||
if report._warn or report._crit:
|
if report._warn or report._crit:
|
||||||
exc = CheckFailed('manual inspection required')
|
exc = CheckFailed('manual inspection required')
|
||||||
|
if exc:
|
||||||
write_db(report, 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':
|
elif args.action == 'status':
|
||||||
count = 0
|
count = 0
|
||||||
for entry in read_db():
|
for entry in read_db():
|
||||||
|
@ -202,8 +254,13 @@ def main() -> None:
|
||||||
break
|
break
|
||||||
print()
|
print()
|
||||||
elif args.action == 'fail-reset':
|
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:
|
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():
|
if prev_err := has_previous_error():
|
||||||
write_db(None)
|
write_db(None)
|
||||||
logger.info(f'reset previous error {prev_err}')
|
logger.info(f'reset previous error {prev_err}')
|
||||||
|
|
|
@ -14,7 +14,7 @@ class UnknownQuestionError(subprocess.SubprocessError):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Pacman returned an unknown question {self.question}"
|
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
|
captures stdout and stderr and
|
||||||
automatically handles [y/n] questions of pacman
|
automatically handles [y/n] questions of pacman
|
||||||
|
@ -41,7 +41,7 @@ def execute_with_io(command: List[str]) -> List[str]:
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
encoding='utf-8'
|
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 = ''
|
line = ''
|
||||||
output = ''
|
output = ''
|
||||||
while (r := p.stdout.read(1)) != '':
|
while (r := p.stdout.read(1)) != '':
|
||||||
|
|
Loading…
Reference in a new issue