X-Git-Url: https://git.llucax.com/software/pymin.git/blobdiff_plain/d6dfd46ed31d985e364b9ea9a404c394f15c2348..29b9cec06cf295e44693db06422ac986c9214ac4:/services/util.py diff --git a/services/util.py b/services/util.py index cbfc091..7239867 100644 --- a/services/util.py +++ b/services/util.py @@ -72,6 +72,29 @@ class ExecutionError(Error): command = ' '.join(command) return "Can't execute command %s: %s" % (command, self.error) +class ParameterError(Error, KeyError): + r""" + ParameterError(paramname) -> ParameterError instance + + This is the base exception for all DhcpHandler parameters related errors. + """ + + def __init__(self, paramname): + r"Initialize the object. See class documentation for more info." + self.message = 'Parameter error: "%s"' % paramname + +class ParameterNotFoundError(ParameterError): + r""" + ParameterNotFoundError(hostname) -> ParameterNotFoundError instance + + This exception is raised when trying to operate on a parameter that doesn't + exists. + """ + + def __init__(self, paramname): + r"Initialize the object. See class documentation for more info." + self.message = 'Parameter not found: "%s"' % paramname + def call(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, universal_newlines=True, **kw): @@ -89,120 +112,18 @@ def call(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, if r is not 0: raise ExecutionError(command, ReturnNot0Error(r)) -class ServiceHandler(Handler): - r"""ServiceHandler([start[, stop[, restart[, reload]]]]) -> ServiceHandler. - - This is a helper class to inherit from to automatically handle services - with start, stop, restart, reload actions. - - The actions can be defined by calling the constructor with all the - parameters or in a more declarative way as class attributes, like: - - class TestHandler(ServiceHandler): - _service_start = ('command', 'start') - _service_stop = ('command', 'stop') - _service_restart = ('command', 'restart') - _service_reload = 'reload-command' - - Commands are executed without using the shell, that's why they are specified - as tuples (where the first element is the command and the others are the - command arguments). If only a command is needed (without arguments) a single - string can be specified. - - All commands must be specified. - """ - # TODO implement it using metaclasses to add the handlers method by demand - # (only for specifieds commands). - - def __init__(self, start=None, stop=None, restart=None, reload=None): - r"Initialize the object, see the class documentation for details." - for (name, action) in dict(start=start, stop=stop, restart=restart, - reload=reload).items(): - if action is not None: - setattr(self, '_service_%s' % name, action) - - @handler(u'Start the service.') - def start(self): - r"start() -> None :: Start the service." - call(self._service_start) - - @handler(u'Stop the service.') - def stop(self): - r"stop() -> None :: Stop the service." - call(self._service_stop) - - @handler(u'Restart the service.') - def restart(self): - r"restart() -> None :: Restart the service." - call(self._service_restart) - - @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) - -class InitdHandler(Handler): - r"""InitdHandler([initd_name[, initd_dir]]) -> InitdHandler. - - This is a helper class to inherit from to automatically handle services - with start, stop, restart, reload actions using a /etc/init.d like script. - - The name and directory of the script can be defined by calling the - constructor or in a more declarative way as class attributes, like: - - class TestHandler(ServiceHandler): - _initd_name = 'some-service' - _initd_dir = '/usr/local/etc/init.d' - - The default _initd_dir is '/etc/init.d', _initd_name has no default and - must be specified in either way. - - Commands are executed without using the shell. - """ - # TODO implement it using metaclasses to add the handlers method by demand - # (only for specifieds commands). - - _initd_dir = '/etc/init.d' - - def __init__(self, initd_name=None, initd_dir=None): - r"Initialize the object, see the class documentation for details." - if initd_name is not None: - 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')) - class Persistent: - r"""Persistent([vars[, dir[, ext]]]) -> Persistent. + r"""Persistent([attrs[, dir[, ext]]]) -> Persistent. This is a helper class to inherit from to automatically handle data persistence using pickle. - The variables attributes to persist (vars), and the pickle directory (dir) + The variables attributes to persist (attrs), and the pickle directory (dir) and file extension (ext) can be defined by calling the constructor or in a more declarative way as class attributes, like: class TestHandler(Persistent): - _persistent_vars = ('some_var', 'other_var') + _persistent_attrs = ('some_attr', 'other_attr') _persistent_dir = 'persistent-data' _persistent_ext = '.pickle' @@ -217,14 +138,14 @@ class Persistent: # TODO implement it using metaclasses to add the handlers method by demand # (only for specifieds commands). - _persistent_vars = () + _persistent_attrs = () _persistent_dir = '.' _persistent_ext = '.pkl' - def __init__(self, vars=None, dir=None, ext=None): + def __init__(self, attrs=None, dir=None, ext=None): r"Initialize the object, see the class documentation for details." - if vars is not None: - self._persistent_vars = vars + if attrs is not None: + self._persistent_attrs = attrs if dir is not None: self._persistent_dir = dir if ext is not None: @@ -232,28 +153,28 @@ class Persistent: def _dump(self): r"_dump() -> None :: Dump all persistent data to pickle files." - if isinstance(self._persistent_vars, basestring): - self._persistent_vars = (self._persistent_vars,) - for varname in self._persistent_vars: - self._dump_var(varname) + if isinstance(self._persistent_attrs, basestring): + self._persistent_attrs = (self._persistent_attrs,) + for attrname in self._persistent_attrs: + self._dump_attr(attrname) def _load(self): r"_load() -> None :: Load all persistent data from pickle files." - if isinstance(self._persistent_vars, basestring): - self._persistent_vars = (self._persistent_vars,) - for varname in self._persistent_vars: - self._load_var(varname) - - def _dump_var(self, varname): - r"_dump_var() -> None :: Dump a especific variable to a pickle file." - f = file(self._pickle_filename(varname), 'wb') - pickle.dump(getattr(self, varname), f, 2) + if isinstance(self._persistent_attrs, basestring): + self._persistent_attrs = (self._persistent_attrs,) + for attrname in self._persistent_attrs: + self._load_attr(attrname) + + def _dump_attr(self, attrname): + r"_dump_attr() -> None :: Dump a specific variable to a pickle file." + f = file(self._pickle_filename(attrname), 'wb') + pickle.dump(getattr(self, attrname), f, 2) f.close() - def _load_var(self, varname): - r"_load_var() -> object :: Load a especific pickle file." - f = file(self._pickle_filename(varname)) - setattr(self, varname, pickle.load(f)) + def _load_attr(self, attrname): + r"_load_attr() -> object :: Load a specific pickle file." + f = file(self._pickle_filename(attrname)) + setattr(self, attrname, pickle.load(f)) f.close() def _pickle_filename(self, name): @@ -271,14 +192,14 @@ class Restorable(Persistent): declarative way as class attributes, like: class TestHandler(Restorable): - _persistent_vars = ('some_var', 'other_var') + _persistent_attrs = ('some_attr', 'other_attr') _restorable_defaults = dict( - some_var = 'some_default', - other_var = 'other_default') + some_attr = 'some_default', + other_attr = 'other_default') - The defaults is a dictionary, very coupled with the _persistent_vars + The defaults is a dictionary, very coupled with the _persistent_attrs attribute inherited from Persistent. The defaults keys should be the - values from _persistent_vars, and the values the default values. + values from _persistent_attrs, and the values the default values. The _restore() method returns True if the data was restored successfully or False if the defaults were loaded (in case you want to take further @@ -419,8 +340,110 @@ class ConfigWriter: for t in self._config_writer_files: self._write_single_config(t) +class ServiceHandler(Handler): + r"""ServiceHandler([start[, stop[, restart[, reload]]]]) -> ServiceHandler. + + This is a helper class to inherit from to automatically handle services + with start, stop, restart, reload actions. + + The actions can be defined by calling the constructor with all the + parameters or in a more declarative way as class attributes, like: + + class TestHandler(ServiceHandler): + _service_start = ('command', 'start') + _service_stop = ('command', 'stop') + _service_restart = ('command', 'restart') + _service_reload = 'reload-command' + + Commands are executed without using the shell, that's why they are specified + as tuples (where the first element is the command and the others are the + command arguments). If only a command is needed (without arguments) a single + string can be specified. + + All commands must be specified. + """ + # TODO implement it using metaclasses to add the handlers method by demand + # (only for specifieds commands). + + def __init__(self, start=None, stop=None, restart=None, reload=None): + r"Initialize the object, see the class documentation for details." + for (name, action) in dict(start=start, stop=stop, restart=restart, + reload=reload).items(): + if action is not None: + setattr(self, '_service_%s' % name, action) + + @handler(u'Start the service.') + def start(self): + r"start() -> None :: Start the service." + call(self._service_start) + + @handler(u'Stop the service.') + def stop(self): + r"stop() -> None :: Stop the service." + call(self._service_stop) + + @handler(u'Restart the service.') + def restart(self): + r"restart() -> None :: Restart the service." + call(self._service_restart) + + @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) + +class InitdHandler(Handler): + r"""InitdHandler([initd_name[, initd_dir]]) -> InitdHandler. + + This is a helper class to inherit from to automatically handle services + with start, stop, restart, reload actions using a /etc/init.d like script. + + The name and directory of the script can be defined by calling the + constructor or in a more declarative way as class attributes, like: + + class TestHandler(ServiceHandler): + _initd_name = 'some-service' + _initd_dir = '/usr/local/etc/init.d' + + The default _initd_dir is '/etc/init.d', _initd_name has no default and + must be specified in either way. + + Commands are executed without using the shell. + """ + # TODO implement it using metaclasses to add the handlers method by demand + # (only for specifieds commands). + + _initd_dir = '/etc/init.d' + + def __init__(self, initd_name=None, initd_dir=None): + r"Initialize the object, see the class documentation for details." + if initd_name is not None: + 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')) + class TransactionalHandler(Handler): - r"""TransactionalHandler([initd_name[, initd_dir]]) -> TransactionalHandler. + r"""Handle command transactions providing a commit and rollback commands. This is a helper class to inherit from to automatically handle transactional handlers, which have commit and rollback commands. @@ -451,6 +474,54 @@ class TransactionalHandler(Handler): if hasattr(self, '_load'): self._load() +class ParametersHandler(Handler): + r"""ParametersHandler([attr]) -> ParametersHandler. + + This is a helper class to inherit from to automatically handle + service parameters, providing set, get, list and show commands. + + The attribute that holds the parameters can be defined by calling the + constructor or in a more declarative way as class attributes, like: + + class TestHandler(ServiceHandler): + _parameters_attr = 'some_attr' + + The default is 'params' and it should be a dictionary. + """ + # TODO implement it using metaclasses to add the handlers method by demand + # (only for specifieds commands). + + _parameters_attr = 'params' + + def __init__(self, attr=None): + r"Initialize the object, see the class documentation for details." + if attr is not None: + self._parameters_attr = attr + + @handler(u'Set a service parameter.') + def set(self, param, value): + r"set(param, value) -> None :: Set a service parameter." + if not param in self.params: + raise ParameterNotFoundError(param) + self.params[param] = value + + @handler(u'Get a service parameter.') + def get(self, param): + r"get(param) -> None :: Get a service parameter." + if not param in self.params: + raise ParameterNotFoundError(param) + return self.params[param] + + @handler(u'List all available service parameters.') + def list(self): + r"list() -> tuple :: List all the parameter names." + return self.params.keys() + + @handler(u'Get all service parameters, with their values.') + def show(self): + r"show() -> (key, value) tuples :: List all the parameters." + return self.params.items() + if __name__ == '__main__': @@ -498,7 +569,7 @@ if __name__ == '__main__': # Persistent test print 'PTestHandler' class PTestHandler(Persistent): - _persistent_vars = 'vars' + _persistent_attrs = 'vars' def __init__(self): self.vars = dict(a=1, b=2) h = PTestHandler() @@ -520,7 +591,7 @@ if __name__ == '__main__': # Restorable test print 'RTestHandler' class RTestHandler(Restorable): - _persistent_vars = 'vars' + _persistent_attrs = 'vars' _restorable_defaults = dict(vars=dict(a=1, b=2)) def __init__(self): self._restore()