Initial implementation of backend arbitrary action execution
This commit is contained in:
parent
a58ebbd40a
commit
b93be150c2
|
@ -64,8 +64,24 @@ class ServiceBackend(plugins.Plugin):
|
|||
return None
|
||||
|
||||
@classmethod
|
||||
def get_backends(cls):
|
||||
return cls.get_plugins()
|
||||
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
|
||||
|
||||
@classmethod
|
||||
def get_backend(cls, name):
|
||||
|
@ -126,6 +142,7 @@ class ServiceController(ServiceBackend):
|
|||
@classmethod
|
||||
def get_backends(cls):
|
||||
""" filter controller classes """
|
||||
backends = super(ServiceController, cls).get_backends()
|
||||
return [
|
||||
plugin for plugin in cls.plugins if ServiceController in plugin.__mro__
|
||||
backend for backend in backends if ServiceController in backend.__mro__
|
||||
]
|
||||
|
|
|
@ -19,8 +19,6 @@ transports = {}
|
|||
|
||||
|
||||
def BashSSH(backend, log, server, cmds):
|
||||
from .models import BackendLog
|
||||
|
||||
script = '\n'.join(['set -e', 'set -o pipefail'] + cmds + ['exit 0'])
|
||||
script = script.replace('\r', '')
|
||||
digest = hashlib.md5(script).hexdigest()
|
||||
|
@ -48,7 +46,7 @@ def BashSSH(backend, log, server, cmds):
|
|||
ssh.connect(addr, username='root', key_filename=settings.ORCHESTRATION_SSH_KEY_PATH, timeout=10)
|
||||
except socket.error:
|
||||
logger.error('%s timed out on %s' % (backend, server))
|
||||
log.state = BackendLog.TIMEOUT
|
||||
log.state = log.TIMEOUT
|
||||
log.save(update_fields=['state'])
|
||||
return
|
||||
transport = ssh.get_transport()
|
||||
|
@ -92,11 +90,11 @@ def BashSSH(backend, log, server, cmds):
|
|||
if channel.exit_status_ready():
|
||||
break
|
||||
log.exit_code = exit_code = channel.recv_exit_status()
|
||||
log.state = BackendLog.SUCCESS if exit_code == 0 else BackendLog.FAILURE
|
||||
log.state = log.SUCCESS if exit_code == 0 else log.FAILURE
|
||||
logger.debug('%s execution state on %s is %s' % (backend, server, log.state))
|
||||
log.save()
|
||||
except:
|
||||
log.state = BackendLog.ERROR
|
||||
log.state = log.ERROR
|
||||
log.traceback = ExceptionInfo(sys.exc_info()).traceback
|
||||
logger.error('Exception while executing %s on %s' % (backend, server))
|
||||
logger.debug(log.traceback)
|
||||
|
@ -109,7 +107,6 @@ def BashSSH(backend, log, server, cmds):
|
|||
|
||||
|
||||
def Python(backend, log, server, cmds):
|
||||
from .models import BackendLog
|
||||
script = [ str(cmd.func.func_name) + str(cmd.args) for cmd in cmds ]
|
||||
script = json.dumps(script, indent=4).replace('"', '')
|
||||
log.script = '\n'.join([log.script, script])
|
||||
|
@ -121,10 +118,10 @@ def Python(backend, log, server, cmds):
|
|||
stdout += str(result)
|
||||
except:
|
||||
log.exit_code = 1
|
||||
log.state = BackendLog.FAILURE
|
||||
log.state = log.FAILURE
|
||||
log.traceback = ExceptionInfo(sys.exc_info()).traceback
|
||||
else:
|
||||
log.exit_code = 0
|
||||
log.state = BackendLog.SUCCESS
|
||||
log.state = log.SUCCESS
|
||||
log.stdout += stdout
|
||||
log.save()
|
||||
|
|
|
@ -50,6 +50,7 @@ class OperationsMiddleware(object):
|
|||
return
|
||||
pending_operations = cls.get_pending_operations()
|
||||
for backend in ServiceBackend.get_backends():
|
||||
# Check if there exists a related instance to be executed for this backend
|
||||
instance = None
|
||||
if backend.is_main(kwargs['instance']):
|
||||
instance = kwargs['instance']
|
||||
|
@ -61,6 +62,7 @@ class OperationsMiddleware(object):
|
|||
instance = candidate
|
||||
# related objects with backend.model trigger save()
|
||||
action = Operation.SAVE
|
||||
# Maintain consistent state of pending_operations based on save/delete behaviour
|
||||
if instance is not None:
|
||||
# Prevent creating a deleted instance by deleting existing saves
|
||||
if action == Operation.DELETE:
|
||||
|
|
|
@ -138,6 +138,12 @@ class BackendOperation(models.Model):
|
|||
def execute(cls, operations):
|
||||
return manager.execute(operations)
|
||||
|
||||
@classmethod
|
||||
def execute_action(cls, instance, action):
|
||||
backends = ServiceBackend.get_backends(instance=instance, action=action)
|
||||
operations = [cls.create(backend, instance, action) for backend in backends]
|
||||
return cls.execute(operations)
|
||||
|
||||
def backend_class(self):
|
||||
return ServiceBackend.get_backend(self.backend)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from django.utils.translation import ungettext, ugettext_lazy as _
|
|||
|
||||
from orchestra.admin.decorators import action_with_confirmation
|
||||
from orchestra.admin.utils import change_url
|
||||
from orchestra.apps.orchestration.models import BackendOperation as Operation
|
||||
|
||||
|
||||
class GrantPermissionForm(forms.Form):
|
||||
|
@ -22,7 +23,8 @@ class GrantPermissionForm(forms.Form):
|
|||
|
||||
@action_with_confirmation(extra_context=dict(form=GrantPermissionForm()))
|
||||
def grant_permission(modeladmin, request, queryset):
|
||||
user = queryset.get()
|
||||
log = Operation.execute_action(user, 'grant_permission')
|
||||
# TODO
|
||||
pass
|
||||
grant_permission.url_name = 'grant-permission'
|
||||
grant_permission.verbose_name = _("Grant permission")
|
||||
|
|
|
@ -12,6 +12,7 @@ from . import settings
|
|||
class SystemUserBackend(ServiceController):
|
||||
verbose_name = _("System user")
|
||||
model = 'systemusers.SystemUser'
|
||||
actions = ('save', 'delete', 'grant_permission')
|
||||
|
||||
def save(self, user):
|
||||
context = self.get_context(user)
|
||||
|
@ -40,6 +41,10 @@ class SystemUserBackend(ServiceController):
|
|||
self.append("groupdel %(username)s || true" % context)
|
||||
self.delete_home(context, user)
|
||||
|
||||
def grant_permission(self, user):
|
||||
context = self.get_context(user)
|
||||
# TODO setacl
|
||||
|
||||
def delete_home(self, context, user):
|
||||
if user.is_main:
|
||||
# TODO delete instead of this shit
|
||||
|
|
Loading…
Reference in a new issue