137 lines
4.9 KiB
Python
137 lines
4.9 KiB
Python
import logging
|
|
import random
|
|
import re
|
|
import time
|
|
|
|
import sau
|
|
import sau.errors
|
|
import sau.helpers
|
|
|
|
PKG_PATH='/usr/sbin/pkg'
|
|
SERVICE_PATH='/usr/sbin/service'
|
|
FREEBSD_UPDATE_PATH='/usr/sbin/freebsd-update'
|
|
|
|
# regex to identify rc.d-files
|
|
rc_script_re = re.compile(r'^(?:/usr/local)?/etc/rc\.d/(.*)$')
|
|
# regex to parse packages
|
|
pkg_upgrade_re = re.compile(r'^\s([^\s]*): ([^\s]*) -> ([^\s]*).*$')
|
|
|
|
|
|
def identify_service_from_bin(exe):
|
|
log = logging.getLogger(sau.LOGNAME)
|
|
cmd = [ PKG_PATH, 'which', '-q', exe ]
|
|
ret, pkg, err = sau.helpers.exec_cmd(cmd)
|
|
if not pkg:
|
|
raise sau.errors.UnknownServiceError("'{}' does not belong to any package".format(exe))
|
|
pkg = pkg.strip()
|
|
|
|
log.debug('{} belongs to package {}'.format(exe, pkg))
|
|
cmd = [ PKG_PATH, 'info', '-l', pkg ]
|
|
ret, out, err = sau.helpers.exec_cmd(cmd)
|
|
rc_scripts = set()
|
|
for line in out.splitlines():
|
|
match = re.match(rc_script_re, line.strip())
|
|
if match:
|
|
log.debug('found potential rc-script for {} at {}'.format(exe, match.group(1)))
|
|
rc_scripts.add(match.group(1))
|
|
if len(rc_scripts) < 1:
|
|
raise sau.errors.UnknownServiceError("'{}' belongs to package '{}', but no rc-script was found".format(exe, pkg))
|
|
if len(rc_scripts) > 1:
|
|
raise sau.errors.UnkownServiceError("'{}' belongs to package '{}, but it contains multiple rc-scripts: {}".format(exe, pkg, ', '.join(rc_scripts)))
|
|
return rc_scripts.pop()
|
|
|
|
|
|
def restart_service(service):
|
|
log = logging.getLogger(sau.LOGNAME)
|
|
cmd = [ SERVICE_PATH, service, 'restart' ]
|
|
ret, out, err = sau.helpers.exec_cmd(cmd)
|
|
|
|
if ret != 0:
|
|
log.warning("Restart of {} failed:".format(service))
|
|
for line in out.splitlines():
|
|
log.warning("stdout: {}".format(line))
|
|
for line in err.splitlines():
|
|
log.warning("stderr: {}".format(line))
|
|
else:
|
|
log.info("restarted service {}".format(service))
|
|
|
|
def system_upgrade():
|
|
log = logging.getLogger(sau.LOGNAME)
|
|
# this is how freebsd wants it...
|
|
time.sleep(random.randint(0, 3600))
|
|
# now we can lie without soiling our conscience too much
|
|
cmd = [ FREEBSD_UPDATE_PATH, '--not-running-from-cron', 'fetch', 'install' ]
|
|
ret, out, err = sau.helpers.exec_cmd(cmd, timeout=7200, env = { 'PAGER': '/bin/cat' })
|
|
|
|
if ret == 0:
|
|
if out.endswith('No updates are available to install.\n'):
|
|
log.info('No system updates are available')
|
|
else:
|
|
log.info('System updates installed:')
|
|
for line in out.splitlines():
|
|
log.info(line)
|
|
return True
|
|
else:
|
|
log.warning('System upgrade failed (return code {}):'.format(ret))
|
|
for line in out.splitlines():
|
|
log.warning('stdout: {}'.format(line))
|
|
for line in err.splitlines():
|
|
log.warning('stderr: {}'.format(line))
|
|
return False
|
|
|
|
def pkg_upgrade():
|
|
log = logging.getLogger(sau.LOGNAME)
|
|
conf = sau.config
|
|
cmd = [ PKG_PATH, 'upgrade', '-nq' ]
|
|
ret, out, err = sau.helpers.exec_cmd(cmd)
|
|
|
|
if ret == 0 and not out and not err:
|
|
log.info('No package upgrades available')
|
|
return False
|
|
|
|
upgrades = []
|
|
for line in out.splitlines():
|
|
match = re.match(pkg_upgrade_re, line)
|
|
if match:
|
|
upgrades.append({
|
|
'pkg': match.group(1),
|
|
'version_old': match.group(2),
|
|
'version_new': match.group(3)
|
|
})
|
|
|
|
if not upgrades or err:
|
|
log.warning('Could not parse pkg output:')
|
|
for line in out.splitlines():
|
|
log.warning('stdout: {}'.format(line))
|
|
for line in err.splitlines():
|
|
log.warning('stderr: {}'.format(line))
|
|
return False
|
|
|
|
default_version_sens = conf.getint('default', 'version_sensitivity', fallback=1)
|
|
for pkg in upgrades:
|
|
pkg['upgrade_level'] = sau.helpers.version_diff(pkg['version_new'], pkg['version_old'])
|
|
log.info('pkg upgrade available {}'.format(pkg))
|
|
sens = conf.getint('packages', pkg['pkg'], fallback=default_version_sens)
|
|
if sens <= pkg['upgrade_level']:
|
|
log.debug('configured level {} <= pkg level {}'.format(sens, pkg['upgrade_level']))
|
|
pkg['upgrade'] = True
|
|
else:
|
|
log.debug('configured level {} > pkg level {}'.format(sens, pkg['upgrade_level']))
|
|
pkg['upgrade'] = False
|
|
|
|
for pkg in [x for x in upgrades if not x['upgrade']]:
|
|
log.warning('Package require manual upgrade, no upgrades done: {} {} -> {}'.format(pkg['pkg'], pkg['version_old'], pkg['version_new']))
|
|
return False
|
|
|
|
cmd = [ PKG_PATH, 'upgrade', '-yq' ]
|
|
ret, out, err = sau.helpers.exec_cmd(cmd, timeout=3600)
|
|
if ret != 0 or err:
|
|
log.warning('{} failed:'.format(' '.join(cmd)))
|
|
for line in out.splitlines():
|
|
log.warning('stdout: {}'.format(line))
|
|
for line in err.splitlines():
|
|
log.warning('stderr: {}'.format(line))
|
|
|
|
return True
|
|
|