import logging import os import subprocess import warnings logger = logging.getLogger() class RADVDSvc(object): svc_name = 'radvd' def __init__(self): self.name = self.svc_name self.is_systemd = False self.cmd_tpl = None self.has_pkill = False self._get_manager() def _get_manager(self): chkpaths = ('/run/systemd/system', '/dev/.run/systemd', '/dev/.systemd') for _ in chkpaths: if os.path.exists(_): self.is_systemd = True break if self.is_systemd: self.cmd_tpl = 'systemctl {op} {name}' else: # Systemd haters, if you don't understand the benefits of unified service management across all linux # distros, you've obviously never done wide-platform management or scripting. # Let this else block be a learning experience for you. self.cmd_tpl = None self.has_pkill = False for p in os.environ.get('PATH', '/usr/bin').split(':'): fpath = os.path.abspath(os.path.expanduser(p)) bins = os.listdir(fpath) if 'pkill' in bins: self.has_pkill = True if 'service' in bins: # CentOS/RHEL pre-7.x self.cmd_tpl = 'service {name} {op}' break elif 'initctl' in bins: # older Ubuntu and other Upstart distros cmd = ['initctl', 'restart', self.name] break elif 'rc-service' in bins: # OpenRC cmd = ['rc-service', self.name, 'restart'] break # That wasn't even all of them. # This doesn't make sense since we template the command now. # if not self.cmd_tpl and self.has_pkill: # last-ditch effort. # cmd = ['pkill', '-HUP', self.name] if not self.cmd_tpl: logger.error('Could not find which service manager this system is using.') raise RuntimeError('Could not determine service manager') return(None) def restart(self): self.stop() self.start() return(None) def start(self): cmd = self.cmd_tpl.format(op = 'start', name = self.name).split() cmd_exec = subprocess.run(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE) if cmd_exec.returncode != 0: logger.warning('Could not successfully start {0}; returned status {1}'.format(self.name, cmd_exec.returncode)) for i in ('stdout', 'stderr'): s = getattr(cmd_exec, i).decode('utf-8') if s.strip() != '': logger.warning('{0}: {1}'.format(i.upper(), s)) warnings.warn('Service did not start successfully') return(None) def stop(self): cmd = self.cmd_tpl.format(op = 'stop', name = self.name).split() cmd_exec = subprocess.run(cmd, stdin = subprocess.PIPE, stdout = subprocess.PIPE) if cmd_exec.returncode != 0: logger.warning('Could not successfully stop {0}; returned status {1}'.format(self.name, cmd_exec.returncode)) for i in ('stdout', 'stderr'): s = getattr(cmd_exec, i).decode('utf-8') if s.strip() != '': logger.warning('{0}: {1}'.format(i.upper(), s)) warnings.warn('Service did not stop successfully') return(None) class RADVDConf(object): path = '/etc/radvd.conf' tpl = ('interface {iface} {{\n' '\tAdvSendAdvert on;\n' # '\tAdvLinkMTU 1280;\n' # Is it 1480 or 1280? Arch wiki says 1480, but everything else (older) says 1280. '\tAdvLinkMTU 1480;\n' '\tMinRtrAdvInterval 60;\n' '\tMaxRtrAdvInterval 600;\n' '\tAdvDefaultLifetime 9000;\n' '{prefix}' '\troute ::/0 {{\n' '\t\tAdvRouteLifetime infinity;\n' '\t}};\n' '{rdnss}' '}};\n\n') tpl_prefix = ('\tprefix {subnet} {{\n' '\t\tAdvOnLink on;\n' '\t\tAdvAutonomous on;\n' '\t\tAdvRouterAddr off;\n' '\t}};\n') tpl_rdnss = ('\tRDNSS {client_ip} {{\n' '\t\tAdvRDNSSOpen on;\n' '\t\tAdvRDNSSPreference 2;\n' '\t}};\n') def __init__(self, cfg = None): if not cfg: self.cfg = self.path else: self.cfg = os.path.abspath(os.path.expanduser(cfg)) self.cfgStr = None def generate(self, assign_objs): self.cfgStr = '' for assign_obj in assign_objs: if not assign_obj.do_radvd: continue for b in assign_obj.iface_blocks: prefix = self.tpl_prefix.format(subnet = str(b)) if assign_obj.radvd_dns: dns = self.tpl_rdnss.format(client_ip = str(next(b.iter_hosts()))) else: dns = '' self.cfgStr += self.tpl.format(prefix = prefix, rdnss = dns, iface = assign_obj.iface) return(None) def write(self): with open(self.cfg, 'w') as fh: fh.write(self.cfgStr) return(None) class RADVD(object): def __init__(self): self.svc = RADVDSvc() self.conf = RADVDConf(cfg = '/etc/radvd.conf')