2014-05-08 16:59:35 +00:00
|
|
|
from functools import partial
|
|
|
|
|
2014-11-14 16:12:56 +00:00
|
|
|
from django.db.models.loading import get_model
|
2014-07-11 14:48:46 +00:00
|
|
|
from django.utils import timezone
|
2014-10-11 16:21:51 +00:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
2014-05-08 16:59:35 +00:00
|
|
|
|
2014-11-12 16:33:40 +00:00
|
|
|
from orchestra.apps import plugins
|
2014-05-08 16:59:35 +00:00
|
|
|
|
|
|
|
from . import methods
|
|
|
|
|
|
|
|
|
2014-07-21 12:20:04 +00:00
|
|
|
class ServiceBackend(plugins.Plugin):
|
2014-05-08 16:59:35 +00:00
|
|
|
"""
|
|
|
|
Service management backend base class
|
|
|
|
|
|
|
|
It uses the _unit of work_ design principle, which allows bulk operations to
|
|
|
|
be conviniently supported. Each backend generates the configuration for all
|
|
|
|
the changes of all modified objects, reloading the daemon just once.
|
|
|
|
"""
|
|
|
|
model = None
|
|
|
|
related_models = () # ((model, accessor__attribute),)
|
|
|
|
script_method = methods.BashSSH
|
|
|
|
function_method = methods.Python
|
|
|
|
type = 'task' # 'sync'
|
|
|
|
ignore_fields = []
|
2014-07-09 16:17:43 +00:00
|
|
|
actions = []
|
2014-05-08 16:59:35 +00:00
|
|
|
|
|
|
|
# TODO type: 'script', execution:'task'
|
|
|
|
|
|
|
|
__metaclass__ = plugins.PluginMount
|
|
|
|
|
|
|
|
def __unicode__(self):
|
|
|
|
return type(self).__name__
|
2014-07-14 14:56:48 +00:00
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
def __str__(self):
|
|
|
|
return unicode(self)
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.cmds = []
|
|
|
|
|
2014-07-09 16:17:43 +00:00
|
|
|
@classmethod
|
|
|
|
def get_actions(cls):
|
|
|
|
return [ action for action in cls.actions if action in dir(cls) ]
|
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
@classmethod
|
|
|
|
def get_name(cls):
|
|
|
|
return cls.__name__
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def is_main(cls, obj):
|
|
|
|
opts = obj._meta
|
|
|
|
return cls.model == '%s.%s' % (opts.app_label, opts.object_name)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_related(cls, obj):
|
|
|
|
opts = obj._meta
|
|
|
|
model = '%s.%s' % (opts.app_label, opts.object_name)
|
|
|
|
for rel_model, field in cls.related_models:
|
|
|
|
if rel_model == model:
|
|
|
|
related = obj
|
|
|
|
for attribute in field.split('__'):
|
|
|
|
related = getattr(related, attribute)
|
|
|
|
return related
|
|
|
|
return None
|
|
|
|
|
|
|
|
@classmethod
|
2014-11-14 16:52:54 +00:00
|
|
|
def get_backends(cls, instance=None, action=None):
|
|
|
|
backends = cls.get_plugins()
|
|
|
|
included = []
|
|
|
|
# Filter for instance or action
|
|
|
|
if instance or action:
|
|
|
|
for backend in backends:
|
|
|
|
include = True
|
|
|
|
if instance:
|
|
|
|
opts = instance._meta
|
|
|
|
if backend.model != '.'.join((opts.app_label, opts.object_name)):
|
|
|
|
include = False
|
|
|
|
if include and action:
|
|
|
|
if action not in backend.get_actions():
|
|
|
|
include = False
|
|
|
|
if include:
|
|
|
|
included.append(backend)
|
|
|
|
backends = included
|
|
|
|
return backends
|
2014-05-08 16:59:35 +00:00
|
|
|
|
2014-07-10 10:03:22 +00:00
|
|
|
@classmethod
|
|
|
|
def get_backend(cls, name):
|
2014-07-21 12:20:04 +00:00
|
|
|
return cls.get_plugin(name)
|
2014-05-08 16:59:35 +00:00
|
|
|
|
2014-11-14 16:12:56 +00:00
|
|
|
@classmethod
|
|
|
|
def model_class(cls):
|
|
|
|
return get_model(cls.model)
|
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
def get_banner(self):
|
2014-07-11 14:48:46 +00:00
|
|
|
time = timezone.now().strftime("%h %d, %Y %I:%M:%S")
|
2014-10-04 09:29:18 +00:00
|
|
|
return "Generated by Orchestra at %s" % time
|
2014-05-08 16:59:35 +00:00
|
|
|
|
2014-11-18 17:47:26 +00:00
|
|
|
def execute(self, server, async=False):
|
2014-05-08 16:59:35 +00:00
|
|
|
from .models import BackendLog
|
|
|
|
state = BackendLog.STARTED if self.cmds else BackendLog.SUCCESS
|
|
|
|
log = BackendLog.objects.create(backend=self.get_name(), state=state, server=server)
|
|
|
|
for method, cmds in self.cmds:
|
2014-11-18 17:47:26 +00:00
|
|
|
method(log, server, cmds, async)
|
2014-05-08 16:59:35 +00:00
|
|
|
if log.state != BackendLog.SUCCESS:
|
|
|
|
break
|
|
|
|
return log
|
|
|
|
|
|
|
|
def append(self, *cmd):
|
|
|
|
# aggregate commands acording to its execution method
|
|
|
|
if isinstance(cmd[0], basestring):
|
|
|
|
method = self.script_method
|
|
|
|
cmd = cmd[0]
|
|
|
|
else:
|
|
|
|
method = self.function_method
|
|
|
|
cmd = partial(*cmd)
|
|
|
|
if not self.cmds or self.cmds[-1][0] != method:
|
|
|
|
self.cmds.append((method, [cmd]))
|
|
|
|
else:
|
|
|
|
self.cmds[-1][1].append(cmd)
|
|
|
|
|
2014-07-25 15:17:50 +00:00
|
|
|
def prepare(self):
|
|
|
|
""" hook for executing something at the beging """
|
|
|
|
pass
|
|
|
|
|
2014-05-08 16:59:35 +00:00
|
|
|
def commit(self):
|
|
|
|
"""
|
|
|
|
apply the configuration, usually reloading a service
|
|
|
|
reloading a service is done in a separated method in order to reload
|
|
|
|
the service once in bulk operations
|
|
|
|
"""
|
|
|
|
pass
|
2014-07-09 16:17:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
class ServiceController(ServiceBackend):
|
|
|
|
actions = ('save', 'delete')
|
2014-10-11 16:21:51 +00:00
|
|
|
abstract = True
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_verbose_name(cls):
|
|
|
|
return _("[S] %s") % super(ServiceController, cls).get_verbose_name()
|
2014-07-09 16:17:43 +00:00
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_backends(cls):
|
|
|
|
""" filter controller classes """
|
2014-11-14 16:52:54 +00:00
|
|
|
backends = super(ServiceController, cls).get_backends()
|
2014-07-16 15:20:16 +00:00
|
|
|
return [
|
2014-11-14 16:52:54 +00:00
|
|
|
backend for backend in backends if ServiceController in backend.__mro__
|
2014-07-16 15:20:16 +00:00
|
|
|
]
|