From: Leandro Lucarella Date: Mon, 22 Oct 2007 16:11:43 +0000 (-0300) Subject: Merge commit 'nico-baryon/master' X-Git-Url: https://git.llucax.com/software/pymin.git/commitdiff_plain/71a35239852c629c02006219427db45b75bc24da?hp=60de22ab6dba022c4a6ed243c15f1e941f8154cd Merge commit 'nico-baryon/master' Conflicts: pymin/services/ppp/__init__.py pymin/services/vrrp/__init__.py --- diff --git a/TODO b/TODO index 3438f7e..0bdd48e 100644 --- a/TODO +++ b/TODO @@ -20,31 +20,19 @@ Ideas / TODO: * Paths. * SubHandlers: - * ComposeDictSubHandler con soporte de dirty/del/add (para ip y DNS). - * Agregar SimpleDictSubHandler? (que no use una clase, que use un dict - de strings directamente, para Proxy Users por ej.). Ídem List. * Agregar SetSubHandler? (para Proxy Hosts) * Agregar logging. * Agregar validación con formencode. -* Ver como manejar la información sobre si un servicio está andando o no. Si se - agrega una acción 'status' para ver el estado y si ese estado se saca de posta - de /proc o si es un estado interno y se asume que los servicios no se caen (no - creo que sea una buena idea esto último). Además habría que ver cuando arranca - el pymin, si se inician servicios automáticamente o no y si la info de qué - servicios iniciar o no es persistente y si puede configurarla el usuario. +* Hacer que el estado sobre si un servicio está andando o no sea más confiable + que un simple flag interno (en caso de ver que realmente esté corriendo, + probablemente sea una buena idea que haya un flag que indique si hay que + levantarlo en el inicio). * No usar comandos con templates, porque después si no hay que ejecutarlos con un shell (porque el template devuelve un string todo grande) y hay que andar teniendo cuidado de escapar las cosas (y hay riesgos de seguridad de shell injection). -Estas cosas quedan sujetas a necesitada y a definición del protocolo. -Para mí lo ideal es que el protocolo de red sea igual que la consola del -usuario, porque después de todo no va a ser más que eso, mandar comanditos. - -Por otro lado, el cliente de consola, por que no es el cliente web pero -accedido via ssh usando un navegador de texto como w3m??? - diff --git a/pymin/services/dhcp/__init__.py b/pymin/services/dhcp/__init__.py index 251553b..de1515b 100644 --- a/pymin/services/dhcp/__init__.py +++ b/pymin/services/dhcp/__init__.py @@ -87,7 +87,7 @@ class DhcpHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler, self._persistent_dir = pickle_dir self._config_writer_cfg_dir = config_dir self._config_build_templates() - self._restore() + InitdHandler.__init__(self) self.host = HostHandler(self) def _get_config_vars(self, config_file): diff --git a/pymin/services/dns/__init__.py b/pymin/services/dns/__init__.py index d34291e..7d09c2f 100644 --- a/pymin/services/dns/__init__.py +++ b/pymin/services/dns/__init__.py @@ -109,13 +109,7 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, self._config_writer_cfg_dir = config_dir self._update = False self._config_build_templates() - self._restore() - # FIXME self._update = True - #if not self._restore(): - #r = self._restore() - #print r - #if not r: - # self._update = True + InitdHandler.__init__(self) self.host = HostHandler(self) self.zone = ZoneHandler(self) self.mx = MailExchangeHandler(self) @@ -132,7 +126,7 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, delete_zones = list() for a_zone in self.zones.values(): if a_zone._update or a_zone._add: - if not a_zone._add: + if not a_zone._add and self._service_running: call(('rndc', 'freeze', a_zone.name)) vars = dict( zone = a_zone, @@ -143,7 +137,7 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, self._write_single_config('zoneX.zone', self._zone_filename(a_zone), vars) a_zone._update = False - if not a_zone._add: + if not a_zone._add and self._service_running: call(('rndc', 'thaw', a_zone.name)) else : self._update = True @@ -165,7 +159,8 @@ class DnsHandler(Restorable, ConfigWriter, InitdHandler, TransactionalHandler, if self._update: self._write_single_config('named.conf') self._update = False - self.reload() + return False # Do reload + return True # we don't need to reload if __name__ == '__main__': diff --git a/pymin/services/firewall/__init__.py b/pymin/services/firewall/__init__.py index 71a9548..06b4191 100644 --- a/pymin/services/firewall/__init__.py +++ b/pymin/services/firewall/__init__.py @@ -84,7 +84,7 @@ class FirewallHandler(Restorable, ConfigWriter, ServiceHandler, handler_help = u"Manage firewall service" - _persistent_attrs = 'rules' + _persistent_attrs = ['rules'] _restorable_defaults = dict(rules=list()) @@ -101,7 +101,7 @@ class FirewallHandler(Restorable, ConfigWriter, ServiceHandler, self._service_restart = self._service_start self._service_reload = self._service_start self._config_build_templates() - self._restore() + ServiceHandler.__init__(self) self.rule = RuleHandler(self) def _get_config_vars(self, config_file): diff --git a/pymin/services/ip/__init__.py b/pymin/services/ip/__init__.py index e626ab7..9d256a2 100644 --- a/pymin/services/ip/__init__.py +++ b/pymin/services/ip/__init__.py @@ -187,6 +187,7 @@ class IpHandler(Restorable, ConfigWriter, TransactionalHandler): self._config_writer_cfg_dir = config_dir self._config_build_templates() self._restore() + self._write_config() self.addr = AddressHandler(self) self.route = RouteHandler(self) self.dev = DeviceHandler(self) diff --git a/pymin/services/nat/__init__.py b/pymin/services/nat/__init__.py index 4724fba..a30f4c7 100644 --- a/pymin/services/nat/__init__.py +++ b/pymin/services/nat/__init__.py @@ -6,7 +6,7 @@ from pymin.seqtools import Sequence from pymin.dispatcher import Handler, handler, HandlerError from pymin.services.util import Restorable, ConfigWriter, RestartHandler, \ ReloadHandler, TransactionalHandler, \ - ListSubHandler, call + ServiceHandler, ListSubHandler, call __ALL__ = ('NatHandler',) @@ -180,8 +180,8 @@ class MasqHandler(ListSubHandler): _cont_subhandler_attr = 'masqs' _cont_subhandler_class = Masq -class NatHandler(Restorable, ConfigWriter, RestartHandler, ReloadHandler, - TransactionalHandler): +class NatHandler(Restorable, ConfigWriter, ReloadHandler, ServiceHandler, + TransactionalHandler): r"""NatHandler([pickle_dir[, config_dir]]) -> NatHandler instance. Handles NAT commands using iptables. @@ -203,8 +203,8 @@ class NatHandler(Restorable, ConfigWriter, RestartHandler, ReloadHandler, masqs=list(), ) - @handler(u'Start the service.') - def start(self): + def _service_start(self): + call(('iptables', '-t', 'nat', '-F')) for (index, port) in enumerate(self.ports): call(['iptables'] + port.as_call_list(index+1)) for (index, snat) in enumerate(self.snats): @@ -212,14 +212,15 @@ class NatHandler(Restorable, ConfigWriter, RestartHandler, ReloadHandler, for (index, masq) in enumerate(self.masqs): call(['iptables'] + masq.as_call_list(index+1)) - @handler(u'Stop the service.') - def stop(self): + def _service_stop(self): call(('iptables', '-t', 'nat', '-F')) + _service_restart = _service_start + def __init__(self, pickle_dir='.'): r"Initialize the object, see class documentation for details." self._persistent_dir = pickle_dir - self._restore() + ServiceHandler.__init__(self) self.forward = PortForwardHandler(self) self.snat = SNatHandler(self) self.masq = MasqHandler(self) diff --git a/pymin/services/ppp/__init__.py b/pymin/services/ppp/__init__.py index 868b6d5..c251e50 100644 --- a/pymin/services/ppp/__init__.py +++ b/pymin/services/ppp/__init__.py @@ -1,11 +1,13 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : +import os from os import path +from signal import SIGTERM from pymin.seqtools import Sequence from pymin.dispatcher import Handler, handler, HandlerError -from pymin.services.util import Restorable, ConfigWriter \ - ,TransactionalHandler, DictSubHandler, call +from pymin.services.util import Restorable, ConfigWriter, ReloadHandler, \ + TransactionalHandler, DictSubHandler, call __ALL__ = ('PppHandler',) @@ -32,6 +34,7 @@ class Connection(Sequence): self.username = username self.password = password self.type = type + self._running = False if type == 'OE': if not 'device' in kw: raise ConnectionError('Bad arguments for type=OE') @@ -70,11 +73,11 @@ class ConnectionHandler(DictSubHandler): _cont_subhandler_attr = 'conns' _cont_subhandler_class = Connection -class PppHandler(Restorable, ConfigWriter, TransactionalHandler): +class PppHandler(Restorable, ConfigWriter, ReloadHandler, TransactionalHandler): handler_help = u"Manage ppp service" - _persistent_attrs = ('conns') + _persistent_attrs = ['conns'] _restorable_defaults = dict( conns = dict(), @@ -89,32 +92,76 @@ class PppHandler(Restorable, ConfigWriter, TransactionalHandler): self._config_writer_cfg_dir = config_dir self._config_build_templates() self._restore() + for conn in self.conns.values(): + if conn._running: + conn._running = False + self.start(conn.name) self.conn = ConnectionHandler(self) - @handler('Starts the service') - def start(self, name): - if name in self.conns: - call(['pppd','call', name],stdout=None, stderr=None) - #print ('pon', name) - else: - raise ConnectionNotFoundError(name) - - @handler('Stops the service') - def stop(self, name): + @handler(u'Start one or all the connections.') + def start(self, name=None): + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if name in self.conns: + if not self.conns[name]._running: + call(('pppd', 'call', name)) + self.conns[name]._running = True + self._dump_attr('conns') + else: + raise ConnectionNotFoundError(name) + + @handler(u'Stop one or all the connections.') + def stop(self, name=None): + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if name in self.conns: + if self.conns[name]._running: + call(('poff', name)) + if path.exists('/var/run/ppp-' + name + '.pid'): + pid = file('/var/run/ppp-' + name + '.pid').readline() + try: + os.kill(int(pid.strip()), SIGTERM) + except OSError: + pass # XXX report error? + self.conns[name]._running = False + self._dump_attr('conns') + else: + raise ConnectionNotFoundError(name) + + @handler(u'Restart one or all the connections (even disconnected ones).') + def restart(self, name=None): + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + self.stop(name) + self.start(name) + + @handler(u'Restart only one or all the already running connections.') + def reload(self, name=None): + r"reload() -> None :: Reload the configuration of the service." + names = [name] + if name is None: + names = self.conns.keys() + for name in names: + if self.conns[name]._running: + self.stop(name) + self.start(name) + + @handler(u'Tell if the service is running.') + def running(self, name=None): + r"reload() -> None :: Reload the configuration of the service." + if name is None: + return [c.name for c in self.conns.values() if c._running] if name in self.conns: - if path.exists('/var/run/ppp-' + name + '.pid'): - pid = file('/var/run/ppp-' + name + '.pid').readline().strip() - call(['kill',pid],stdout=None, stderr=None) - #print ('poff', name) + return int(self.conns[name]._running) else: raise ConnectionNotFoundError(name) - @handler('Reloads the service') - def reload(self): - for conn in self.conns.values(): - self.stop(conn.name) - self.start(conn.name) - def _write_config(self): r"_write_config() -> None :: Generate all the configuration files." #guardo los pass que van el pap-secrets @@ -146,3 +193,4 @@ if __name__ == '__main__': p.commit() print p.conn.list() print p.conn.show() + diff --git a/pymin/services/proxy/__init__.py b/pymin/services/proxy/__init__.py index 715b0ab..a02e6c5 100644 --- a/pymin/services/proxy/__init__.py +++ b/pymin/services/proxy/__init__.py @@ -70,7 +70,7 @@ class ProxyHandler(Restorable, ConfigWriter, InitdHandler, self._persistent_dir = pickle_dir self._config_writer_cfg_dir = config_dir self._config_build_templates() - self._restore() + InitdHandler.__init__(self) self.host = HostHandler(self) self.user = UserHandler(self) diff --git a/pymin/services/util.py b/pymin/services/util.py index 418616b..a41882b 100644 --- a/pymin/services/util.py +++ b/pymin/services/util.py @@ -293,9 +293,6 @@ class Restorable(Persistent): r"_restore() -> bool :: Restore persistent data or create a default." try: self._load() - # TODO tener en cuenta servicios que hay que levantar y los que no - if hasattr(self, 'commit'): # TODO deberia ser reload y/o algo para comandos - self.commit() return True except IOError: for (k, v) in self._restorable_defaults.items(): @@ -307,8 +304,6 @@ class Restorable(Persistent): self._dump() if hasattr(self, '_write_config'): self._write_config() - if hasattr(self, 'reload'): - self.reload() return False class ConfigWriter: @@ -436,7 +431,7 @@ class ConfigWriter: self._write_single_config(t) -class ServiceHandler(Handler): +class ServiceHandler(Handler, Restorable): r"""ServiceHandler([start[, stop[, restart[, reload]]]]) -> ServiceHandler. This is a helper class to inherit from to automatically handle services @@ -467,26 +462,63 @@ class ServiceHandler(Handler): reload=reload).items(): if action is not None: setattr(self, '_service_%s' % name, action) + self._persistent_attrs = list(self._persistent_attrs) + self._persistent_attrs.append('_service_running') + if '_service_running' not in self._restorable_defaults: + self._restorable_defaults['_service_running'] = False + self._restore() + if self._service_running: + self._service_running = False + self.start() @handler(u'Start the service.') def start(self): r"start() -> None :: Start the service." - call(self._service_start) + if not self._service_running: + if callable(self._service_start): + self._service_start() + else: + call(self._service_start) + self._service_running = True + self._dump_attr('_service_running') @handler(u'Stop the service.') def stop(self): r"stop() -> None :: Stop the service." - call(self._service_stop) + if self._service_running: + if callable(self._service_stop): + self._service_stop() + else: + call(self._service_stop) + self._service_running = False + self._dump_attr('_service_running') @handler(u'Restart the service.') def restart(self): r"restart() -> None :: Restart the service." - call(self._service_restart) + if callable(self._service_restart): + self._service_restart() + else: + call(self._service_restart) + self._service_running = True + self._dump_attr('_service_running') @handler(u'Reload the service config (without restarting, if possible).') def reload(self): r"reload() -> None :: Reload the configuration of the service." - call(self._service_reload) + if self._service_running: + if callable(self._service_reload): + self._service_reload() + else: + call(self._service_reload) + + @handler(u'Tell if the service is running.') + def running(self): + r"reload() -> None :: Reload the configuration of the service." + if self._service_running: + return 1 + else: + return 0 class RestartHandler(Handler): r"""RestartHandler() -> RestartHandler :: Provides generic restart command. @@ -512,9 +544,11 @@ class ReloadHandler(Handler): @handler(u'Reload the service config (alias to restart).') def reload(self): r"reload() -> None :: Reload the configuration of the service." - self.restart() + if hasattr(self, '_service_running') and self._service_running: + self.restart() -class InitdHandler(Handler): +class InitdHandler(ServiceHandler): + # TODO update docs, declarative style is depracated r"""InitdHandler([initd_name[, initd_dir]]) -> InitdHandler. This is a helper class to inherit from to automatically handle services @@ -543,26 +577,11 @@ class InitdHandler(Handler): self._initd_name = initd_name if initd_dir is not None: self._initd_dir = initd_dir - - @handler(u'Start the service.') - def start(self): - r"start() -> None :: Start the service." - call((path.join(self._initd_dir, self._initd_name), 'start')) - - @handler(u'Stop the service.') - def stop(self): - r"stop() -> None :: Stop the service." - call((path.join(self._initd_dir, self._initd_name), 'stop')) - - @handler(u'Restart the service.') - def restart(self): - r"restart() -> None :: Restart the service." - call((path.join(self._initd_dir, self._initd_name), 'restart')) - - @handler(u'Reload the service config (without restarting, if possible).') - def reload(self): - r"reload() -> None :: Reload the configuration of the service." - call((path.join(self._initd_dir, self._initd_name), 'reload')) + actions = dict() + for action in ('start', 'stop', 'restart', 'reload'): + actions[action] = (path.join(self._initd_dir, self._initd_name), + action) + ServiceHandler.__init__(self, **actions) class TransactionalHandler(Handler): r"""Handle command transactions providing a commit and rollback commands. @@ -585,9 +604,10 @@ class TransactionalHandler(Handler): r"commit() -> None :: Commit the changes and reload the service." if hasattr(self, '_dump'): self._dump() + unchanged = False if hasattr(self, '_write_config'): - self._write_config() - if hasattr(self, 'reload'): + unchanged = self._write_config() + if not unchanged and hasattr(self, 'reload'): self.reload() @handler(u'Discard all the uncommited changes.') @@ -626,6 +646,8 @@ class ParametersHandler(Handler): if not param in self.params: raise ParameterNotFoundError(param) self.params[param] = value + if hasattr(self, '_update'): + self._update = True @handler(u'Get a service parameter.') def get(self, param): diff --git a/pymin/services/vrrp/__init__.py b/pymin/services/vrrp/__init__.py index 580d4e6..403cd36 100644 --- a/pymin/services/vrrp/__init__.py +++ b/pymin/services/vrrp/__init__.py @@ -1,18 +1,24 @@ # vim: set encoding=utf-8 et sw=4 sts=4 : +import os from os import path +from signal import SIGTERM from subprocess import Popen, PIPE from pymin.seqtools import Sequence from pymin.dispatcher import Handler, handler, HandlerError -from pymin.services.util import Restorable, TransactionalHandler, ParametersHandler, call +from pymin.services.util import Restorable, TransactionalHandler, \ + ReloadHandler, RestartHandler, \ + ServiceHandler, ParametersHandler, call __ALL__ = ('VrrpHandler',) -class VrrpHandler(Restorable, ParametersHandler, TransactionalHandler): +class VrrpHandler(Restorable, ParametersHandler, ReloadHandler, RestartHandler, + ServiceHandler, TransactionalHandler): + handler_help = u"Manage VRRP service" - _persistent_attrs = 'params' + _persistent_attrs = ['params'] _restorable_defaults = dict( params = dict( ipaddress='192.168.0.1', @@ -22,37 +28,31 @@ class VrrpHandler(Restorable, ParametersHandler, TransactionalHandler): ), ) - def __init__(self, pickle_dir='.', config_dir='.', pid_dir='.'): - self._persistent_dir = pickle_dir - self._pid_dir = pid_dir - self._restore() - - @handler('Starts the service') - def start(self): + def _service_start(self): if self.params['prio'] != '': - call(('/usr/local/bin/vrrpd','-i',self.params['dev'],'-v',self.params['id'],'-p',self.params['prio'],self.params['ipaddress'])) - #print ('vrrpd','-i',self.params['dev'],'-v',self.params['id'],'-p',self.params['prio'],self.params['ipaddress']) + call(('vrrp', '-i', self.params['dev'], '-v', self.params['id'], + '-p', self.params['prio'], self.params['ipaddress'])) else: - call(('/usr/local/bin/vrrpd','-i',self.params['dev'],'-v',self.params['id'],self.params['ipaddress'])) - #print ('vrrpd','-i',self.params['dev'],'-v',self.params['id'],self.params['ipaddress']) - - @handler('Stop the service') - def stop(self): - try : - pid = 'vrrpd' + '_' + self.params['dev'] + '_' + self.params['id'] + '.pid' - f = file(path.join(self._pid_dir, pid ), 'r') - call(('kill',f.read().strip('\n'))) - #print('kill','<',f.read()) - except IOError: + call(('vrrp', '-i', self.params['dev'], '-v', self.params['id'], \ + self.params['ipaddress'])) + + def _service_stop(self): + try: + pid_filename = 'vrrpd_%(dev)s_%(id)s.pid' % self.params + pid = file(path.join(self._pid_dir, pid_filename )).read().strip() + os.kill(int(pid), SIGTERM) + except (IOError, OSError): + # TODO log pass - @handler('Reloads the service') - def reload(self): - self.stop() - self.start() + def __init__(self, pickle_dir='.', config_dir='.', pid_dir='.'): + self._persistent_dir = pickle_dir + self._pid_dir = pid_dir + ServiceHandler.__init__(self) if __name__ == '__main__': v = VrrpHandler() v.set('prio','10') v.commit() +