Commit d1bee728 by Czémán Arnold

common, vm: make operations help() friendly

parent 5ea451a2
...@@ -38,15 +38,23 @@ class SubOperationMixin(object): ...@@ -38,15 +38,23 @@ class SubOperationMixin(object):
parent, user, kwargs) parent, user, kwargs)
class WritableDocMeta(type):
pass
class Operation(object): class Operation(object):
"""Base class for VM operations. """Base class for VM operations.
""" """
__metaclass__ = WritableDocMeta
async_queue = 'localhost.man' async_queue = 'localhost.man'
required_perms = None required_perms = None
superuser_required = False superuser_required = False
do_not_call_in_templates = True do_not_call_in_templates = True
abortable = False abortable = False
has_percentage = False has_percentage = False
description = None
@classmethod @classmethod
def get_activity_code_suffix(cls): def get_activity_code_suffix(cls):
...@@ -219,10 +227,12 @@ operation_registry_name = '_ops' ...@@ -219,10 +227,12 @@ operation_registry_name = '_ops'
class OperatedMixin(object): class OperatedMixin(object):
def __getattr__(self, name):
# NOTE: __getattr__ is only called if the attribute doesn't already def __getattribute__(self, name):
# exist in your __dict__ ops = getattr(type(self), operation_registry_name, {})
return self.get_operation_class(name)(self) if name in ops:
return type(self).get_operation_class(name)(self)
return object.__getattribute__(self, name)
@classmethod @classmethod
def get_operation_class(cls, name): def get_operation_class(cls, name):
...@@ -261,6 +271,19 @@ class OperatedMixin(object): ...@@ -261,6 +271,19 @@ class OperatedMixin(object):
return None return None
def generate_op_doc(op, op_id):
doc = """
Usage: %s(<args>) | %s.async(<args>)
Args: %s
%s
""" % (op_id, op_id,
", ".join(getargspec(op._operation)[0]),
op.__doc__)
op.__doc__ = doc
def register_operation(op_cls, op_id=None, target_cls=None): def register_operation(op_cls, op_id=None, target_cls=None):
"""Register the specified operation with the target class. """Register the specified operation with the target class.
...@@ -297,5 +320,9 @@ def register_operation(op_cls, op_id=None, target_cls=None): ...@@ -297,5 +320,9 @@ def register_operation(op_cls, op_id=None, target_cls=None):
if not hasattr(target_cls, operation_registry_name): if not hasattr(target_cls, operation_registry_name):
setattr(target_cls, operation_registry_name, dict()) setattr(target_cls, operation_registry_name, dict())
op_cls.__doc__ = op_cls.description or "It has no documentation. :("
op = op_cls(None)
generate_op_doc(op, op_id)
setattr(target_cls, op_id, op)
getattr(target_cls, operation_registry_name)[op_id] = op_cls getattr(target_cls, operation_registry_name)[op_id] = op_cls
return op_cls return op_cls
...@@ -24,6 +24,7 @@ from django.contrib.auth.models import User, Group, Permission ...@@ -24,6 +24,7 @@ from django.contrib.auth.models import User, Group, Permission
from django.contrib.auth import authenticate from django.contrib.auth import authenticate
from common.tests.celery_mock import MockCeleryMixin from common.tests.celery_mock import MockCeleryMixin
from common.operations import operation_registry_name
from dashboard.views import VmAddInterfaceView from dashboard.views import VmAddInterfaceView
from vm.models import Instance, InstanceTemplate, Lease, Node, Trait from vm.models import Instance, InstanceTemplate, Lease, Node, Trait
from vm.operations import (WakeUpOperation, AddInterfaceOperation, from vm.operations import (WakeUpOperation, AddInterfaceOperation,
...@@ -482,7 +483,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase): ...@@ -482,7 +483,7 @@ class VmDetailTest(LoginMixin, MockCeleryMixin, TestCase):
patch.object(Instance.WrongStateError, 'send_message') as wro: patch.object(Instance.WrongStateError, 'send_message') as wro:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
new_wake_up.side_effect = inst.wake_up new_wake_up.side_effect = inst.wake_up
inst._wake_up_vm = Mock() getattr(inst, operation_registry_name)["_wake_up_vm"] = Mock()
inst.get_remote_queue_name = Mock(return_value='test') inst.get_remote_queue_name = Mock(return_value='test')
inst.status = 'SUSPENDED' inst.status = 'SUSPENDED'
inst.set_level(self.u2, 'owner') inst.set_level(self.u2, 'owner')
......
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