Commit 83756cde by Dudás Ádám Committed by Őry Máté

operations: smarter handling of call and generated arguments

_operation methods now only need to accept those generated arguments that are
actually used.
parent c7b205a4
from inspect import getargspec
from logging import getLogger from logging import getLogger
from .models import activity_context from .models import activity_context
...@@ -29,23 +30,51 @@ class Operation(object): ...@@ -29,23 +30,51 @@ class Operation(object):
def __prelude(self, kwargs): def __prelude(self, kwargs):
"""This method contains the shared prelude of call and async. """This method contains the shared prelude of call and async.
""" """
skip_auth_check = kwargs.setdefault('system', False) defaults = {'parent_activity': None, 'system': False, 'user': None}
user = kwargs.setdefault('user', None)
parent_activity = kwargs.pop('parent_activity', None) allargs = dict(defaults, **kwargs) # all arguments
auxargs = allargs.copy() # auxiliary (i.e. only for _operation) args
# NOTE: consumed items should be removed from auxargs, and no new items
# should be added to it
skip_auth_check = auxargs.pop('system')
user = auxargs.pop('user')
parent_activity = auxargs.pop('parent_activity')
# check for unexpected keyword arguments
argspec = getargspec(self._operation)
if argspec.keywords is None: # _operation doesn't take ** args
unexpected_kwargs = set(arg for arg in auxargs
if arg not in argspec.args)
if unexpected_kwargs:
raise TypeError("Operation got unexpected keyword arguments: "
"%s" % ", ".join(unexpected_kwargs))
if not skip_auth_check: if not skip_auth_check:
self.check_auth(user) self.check_auth(user)
self.check_precond() self.check_precond()
return self.create_activity(parent=parent_activity, user=user)
def _exec_op(self, activity, user, **kwargs): activity = self.create_activity(parent=parent_activity, user=user)
return activity, allargs, auxargs
def _exec_op(self, allargs, auxargs):
"""Execute the operation inside the specified activity's context. """Execute the operation inside the specified activity's context.
""" """
with activity_context(activity, on_abort=self.on_abort, # compile arguments for _operation
argspec = getargspec(self._operation)
if argspec.keywords is not None: # _operation takes ** args
arguments = allargs.copy()
else: # _operation doesn't take ** args
arguments = {k: v for (k, v) in allargs.iteritems()
if k in argspec.args}
arguments.update(auxargs)
with activity_context(allargs['activity'], on_abort=self.on_abort,
on_commit=self.on_commit): on_commit=self.on_commit):
return self._operation(activity=activity, user=user, **kwargs) return self._operation(**arguments)
def _operation(self, activity, user, system, **kwargs): def _operation(self, **kwargs):
"""This method is the operation's particular implementation. """This method is the operation's particular implementation.
Deriving classes should implement this method. Deriving classes should implement this method.
...@@ -65,12 +94,10 @@ class Operation(object): ...@@ -65,12 +94,10 @@ class Operation(object):
logger.info("%s called asynchronously on %s with the following " logger.info("%s called asynchronously on %s with the following "
"parameters: %r", self.__class__.__name__, self.subject, "parameters: %r", self.__class__.__name__, self.subject,
kwargs) kwargs)
activity = self.__prelude(kwargs) activity, allargs, auxargs = self.__prelude(kwargs)
return self.async_operation.apply_async(args=(self.id, return self.async_operation.apply_async(
self.subject.pk, args=(self.id, self.subject.pk, activity.pk, allargs, auxargs, ),
activity.pk), queue=self.async_queue)
kwargs=kwargs,
queue=self.async_queue)
def call(self, **kwargs): def call(self, **kwargs):
"""Execute the operation (synchronously). """Execute the operation (synchronously).
...@@ -88,8 +115,9 @@ class Operation(object): ...@@ -88,8 +115,9 @@ class Operation(object):
logger.info("%s called (synchronously) on %s with the following " logger.info("%s called (synchronously) on %s with the following "
"parameters: %r", self.__class__.__name__, self.subject, "parameters: %r", self.__class__.__name__, self.subject,
kwargs) kwargs)
activity = self.__prelude(kwargs) activity, allargs, auxargs = self.__prelude(kwargs)
return self._exec_op(activity=activity, **kwargs) allargs['activity'] = activity
return self._exec_op(allargs, auxargs)
def check_precond(self): def check_precond(self):
pass pass
......
...@@ -2,7 +2,8 @@ from manager.mancelery import celery ...@@ -2,7 +2,8 @@ from manager.mancelery import celery
@celery.task @celery.task
def async_instance_operation(operation_id, instance_pk, activity_pk, **kwargs): def async_instance_operation(operation_id, instance_pk, activity_pk, allargs,
auxargs):
from vm.models import Instance, InstanceActivity from vm.models import Instance, InstanceActivity
instance = Instance.objects.get(pk=instance_pk) instance = Instance.objects.get(pk=instance_pk)
operation = getattr(instance, operation_id) operation = getattr(instance, operation_id)
...@@ -12,11 +13,13 @@ def async_instance_operation(operation_id, instance_pk, activity_pk, **kwargs): ...@@ -12,11 +13,13 @@ def async_instance_operation(operation_id, instance_pk, activity_pk, **kwargs):
activity.task_uuid = async_instance_operation.request.id activity.task_uuid = async_instance_operation.request.id
activity.save() activity.save()
return operation._exec_op(activity=activity, **kwargs) allargs['activity'] = activity
return operation._exec_op(allargs, auxargs)
@celery.task @celery.task
def async_node_operation(operation_id, node_pk, activity_pk, **kwargs): def async_node_operation(operation_id, node_pk, activity_pk, allargs, auxargs):
from vm.models import Node, NodeActivity from vm.models import Node, NodeActivity
node = Node.objects.get(pk=node_pk) node = Node.objects.get(pk=node_pk)
operation = getattr(node, operation_id) operation = getattr(node, operation_id)
...@@ -26,4 +29,6 @@ def async_node_operation(operation_id, node_pk, activity_pk, **kwargs): ...@@ -26,4 +29,6 @@ def async_node_operation(operation_id, node_pk, activity_pk, **kwargs):
activity.task_uuid = async_node_operation.request.id activity.task_uuid = async_node_operation.request.id
activity.save() activity.save()
return operation._exec_op(activity=activity, **kwargs) allargs['activity'] = activity
return operation._exec_op(allargs, auxargs)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment