Commit 2c621f0f by Őry Máté

Merge branch 'feature-operations-hre' into 'master'

Handle and use HumanReadableExceptions
parents 8f0c13b8 0385e921
...@@ -23,6 +23,7 @@ from logging import getLogger ...@@ -23,6 +23,7 @@ from logging import getLogger
from time import time from time import time
from warnings import warn from warnings import warn
from django.contrib import messages
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.cache import cache from django.core.cache import cache
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
...@@ -46,17 +47,24 @@ class WorkerNotFound(Exception): ...@@ -46,17 +47,24 @@ class WorkerNotFound(Exception):
def activitycontextimpl(act, on_abort=None, on_commit=None): def activitycontextimpl(act, on_abort=None, on_commit=None):
try: try:
try:
yield act yield act
except HumanReadableException as e:
result = e
raise
except BaseException as e: except BaseException as e:
# BaseException is the common parent of Exception and # BaseException is the common parent of Exception and
# system-exiting exceptions, e.g. KeyboardInterrupt # system-exiting exceptions, e.g. KeyboardInterrupt
handler = None if on_abort is None else lambda a: on_abort(a, e) result = create_readable(
result = create_readable(ugettext_noop("Failure."), ugettext_noop("Failure."),
ugettext_noop("Unhandled exception: " ugettext_noop("Unhandled exception: %(error)s"),
"%(error)s"),
error=unicode(e)) error=unicode(e))
raise
except:
logger.exception("Failed activity %s" % unicode(act))
handler = None if on_abort is None else lambda a: on_abort(a, e)
act.finish(succeeded=False, result=result, event_handler=handler) act.finish(succeeded=False, result=result, event_handler=handler)
raise e raise
else: else:
act.finish(succeeded=True, event_handler=on_commit) act.finish(succeeded=True, event_handler=on_commit)
...@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel): ...@@ -196,6 +204,10 @@ class ActivityModel(TimeStampedModel):
DeprecationWarning, stacklevel=2) DeprecationWarning, stacklevel=2)
value = create_readable(user_text_template="", value = create_readable(user_text_template="",
admin_text_template=value) admin_text_template=value)
elif not hasattr(value, "to_dict"):
warn("Use HumanReadableObject.", DeprecationWarning, stacklevel=2)
value = create_readable(user_text_template="",
admin_text_template=unicode(value))
self.result_data = None if value is None else value.to_dict() self.result_data = None if value is None else value.to_dict()
...@@ -361,8 +373,9 @@ class HumanReadableObject(object): ...@@ -361,8 +373,9 @@ class HumanReadableObject(object):
@classmethod @classmethod
def create(cls, user_text_template, admin_text_template=None, **params): def create(cls, user_text_template, admin_text_template=None, **params):
return cls(user_text_template, return cls(user_text_template=user_text_template,
admin_text_template or user_text_template, params) admin_text_template=(admin_text_template
or user_text_template), params=params)
def set(self, user_text_template, admin_text_template=None, **params): def set(self, user_text_template, admin_text_template=None, **params):
self._set_values(user_text_template, self._set_values(user_text_template,
...@@ -407,10 +420,28 @@ create_readable = HumanReadableObject.create ...@@ -407,10 +420,28 @@ create_readable = HumanReadableObject.create
class HumanReadableException(HumanReadableObject, Exception): class HumanReadableException(HumanReadableObject, Exception):
"""HumanReadableObject that is an Exception so can used in except clause. """HumanReadableObject that is an Exception so can used in except clause.
""" """
pass def __init__(self, level=None, *args, **kwargs):
super(HumanReadableException, self).__init__(*args, **kwargs)
if level is not None:
if hasattr(messages, level):
self.level = level
else:
raise ValueError(
"Level should be the name of an attribute of django."
"contrib.messages (and it should be callable with "
"(request, message)). Like 'error', 'warning'.")
else:
self.level = "error"
def send_message(self, request, level=None):
if request.user and request.user.is_superuser:
msg = self.get_admin_text()
else:
msg = self.get_user_text()
getattr(messages, level or self.level)(request, msg)
def humanize_exception(message, exception=None, **params):
def humanize_exception(message, exception=None, level=None, **params):
"""Return new dynamic-class exception which is based on """Return new dynamic-class exception which is based on
HumanReadableException and the original class with the dict of exception. HumanReadableException and the original class with the dict of exception.
...@@ -419,8 +450,10 @@ def humanize_exception(message, exception=None, **params): ...@@ -419,8 +450,10 @@ def humanize_exception(message, exception=None, **params):
... ...
Welcome! Welcome!
""" """
Ex = type("HumanReadable" + type(exception).__name__, Ex = type("HumanReadable" + type(exception).__name__,
(HumanReadableException, type(exception)), (HumanReadableException, type(exception)),
exception.__dict__) exception.__dict__)
return Ex.create(message, **params) ex = Ex.create(message, **params)
if level:
ex.level = level
return ex
...@@ -529,24 +529,25 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -529,24 +529,25 @@ class VmDetailTest(LoginMixin, TestCase):
def test_permitted_wake_up_wrong_state(self): def test_permitted_wake_up_wrong_state(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(WakeUpOperation, 'async') as mock_method: with patch.object(WakeUpOperation, 'async') as mock_method, \
patch.object(Instance.WrongStateError, 'send_message') as wro:
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.wake_up mock_method.side_effect = inst.wake_up
inst.status = 'RUNNING' inst.status = 'RUNNING'
inst.set_level(self.u2, 'owner') inst.set_level(self.u2, 'owner')
with patch('dashboard.views.messages') as msg:
c.post("/dashboard/vm/1/op/wake_up/") c.post("/dashboard/vm/1/op/wake_up/")
assert msg.error.called
inst = Instance.objects.get(pk=1) inst = Instance.objects.get(pk=1)
self.assertEqual(inst.status, 'RUNNING') # mocked anyway self.assertEqual(inst.status, 'RUNNING') # mocked anyway
assert mock_method.called assert mock_method.called
assert wro.called
def test_permitted_wake_up(self): def test_permitted_wake_up(self):
c = Client() c = Client()
self.login(c, "user2") self.login(c, "user2")
with patch.object(Instance, 'select_node', return_value=None): with patch.object(Instance, 'select_node', return_value=None), \
with patch.object(WakeUpOperation, 'async') as new_wake_up: patch.object(WakeUpOperation, 'async') as new_wake_up, \
with patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa: patch('vm.tasks.vm_tasks.wake_up.apply_async') as wuaa, \
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.get_remote_queue_name = Mock(return_value='test') inst.get_remote_queue_name = Mock(return_value='test')
...@@ -559,6 +560,7 @@ class VmDetailTest(LoginMixin, TestCase): ...@@ -559,6 +560,7 @@ class VmDetailTest(LoginMixin, TestCase):
self.assertEqual(inst.status, 'RUNNING') self.assertEqual(inst.status, 'RUNNING')
assert new_wake_up.called assert new_wake_up.called
assert wuaa.called assert wuaa.called
assert not wro.called
def test_unpermitted_wake_up(self): def test_unpermitted_wake_up(self):
c = Client() c = Client()
......
...@@ -71,7 +71,7 @@ from .tables import ( ...@@ -71,7 +71,7 @@ from .tables import (
NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable, NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable,
GroupListTable, UserKeyListTable GroupListTable, UserKeyListTable
) )
from common.models import HumanReadableObject from common.models import HumanReadableObject, HumanReadableException
from vm.models import ( from vm.models import (
Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface, Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity, Trait, InterfaceTemplate, Lease, Node, NodeActivity, Trait,
...@@ -562,9 +562,13 @@ class OperationView(RedirectToLoginMixin, DetailView): ...@@ -562,9 +562,13 @@ class OperationView(RedirectToLoginMixin, DetailView):
done = False done = False
try: try:
task = self.get_op().async(user=request.user, **extra) task = self.get_op().async(user=request.user, **extra)
except HumanReadableException as e:
e.send_message(request)
logger.exception("Could not start operation")
result = e
except Exception as e: except Exception as e:
messages.error(request, _('Could not start operation.')) messages.error(request, _('Could not start operation.'))
logger.exception(e) logger.exception("Could not start operation")
result = e result = e
else: else:
wait = self.wait_for_result wait = self.wait_for_result
...@@ -575,6 +579,10 @@ class OperationView(RedirectToLoginMixin, DetailView): ...@@ -575,6 +579,10 @@ class OperationView(RedirectToLoginMixin, DetailView):
except TimeoutError: except TimeoutError:
logger.debug("Result didn't arrive in %ss", logger.debug("Result didn't arrive in %ss",
self.wait_for_result, exc_info=True) self.wait_for_result, exc_info=True)
except HumanReadableException as e:
e.send_message(request)
logger.exception(e)
result = e
except Exception as e: except Exception as e:
messages.error(request, _('Operation failed.')) messages.error(request, _('Operation failed.'))
logger.debug("Operation failed.", exc_info=True) logger.debug("Operation failed.", exc_info=True)
......
...@@ -41,7 +41,7 @@ from model_utils.models import TimeStampedModel, StatusModel ...@@ -41,7 +41,7 @@ from model_utils.models import TimeStampedModel, StatusModel
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
from acl.models import AclBase from acl.models import AclBase
from common.models import create_readable from common.models import create_readable, HumanReadableException
from common.operations import OperatedMixin from common.operations import OperatedMixin
from ..tasks import vm_tasks, agent_tasks from ..tasks import vm_tasks, agent_tasks
from .activity import (ActivityInProgressError, instance_activity, from .activity import (ActivityInProgressError, instance_activity,
...@@ -276,28 +276,26 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -276,28 +276,26 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
verbose_name = _('instance') verbose_name = _('instance')
verbose_name_plural = _('instances') verbose_name_plural = _('instances')
class InstanceDestroyedError(Exception): class InstanceError(HumanReadableException):
def __init__(self, instance, message=None): def __init__(self, instance, params=None, level=None, **kwargs):
if message is None: kwargs.update(params or {})
message = ("The instance (%s) has already been destroyed." self.instance = kwargs["instance"] = instance
% instance) super(Instance.InstanceError, self).__init__(
level, self.message, self.message, kwargs)
Exception.__init__(self, message) class InstanceDestroyedError(InstanceError):
message = ugettext_noop(
"Instance %(instance)s has already been destroyed.")
self.instance = instance class WrongStateError(InstanceError):
message = ugettext_noop(
"Current state (%(state)s) of instance %(instance)s is "
"inappropriate for the invoked operation.")
class WrongStateError(Exception): def __init__(self, instance, params=None, **kwargs):
super(Instance.WrongStateError, self).__init__(
def __init__(self, instance, message=None): instance, params, state=instance.status)
if message is None:
message = ("The instance's current state (%s) is "
"inappropriate for the invoked operation."
% instance.status)
Exception.__init__(self, message)
self.instance = instance
def __unicode__(self): def __unicode__(self):
parts = (self.name, "(" + str(self.id) + ")") parts = (self.name, "(" + str(self.id) + ")")
......
...@@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop ...@@ -26,7 +26,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from celery.exceptions import TimeLimitExceeded from celery.exceptions import TimeLimitExceeded
from common.models import create_readable from common.models import create_readable, humanize_exception
from common.operations import Operation, register_operation from common.operations import Operation, register_operation
from .tasks.local_tasks import ( from .tasks.local_tasks import (
abortable_async_instance_operation, abortable_async_node_operation, abortable_async_instance_operation, abortable_async_node_operation,
...@@ -45,6 +45,8 @@ class InstanceOperation(Operation): ...@@ -45,6 +45,8 @@ class InstanceOperation(Operation):
async_operation = abortable_async_instance_operation async_operation = abortable_async_instance_operation
host_cls = Instance host_cls = Instance
concurrency_check = True concurrency_check = True
accept_states = None
deny_states = None
def __init__(self, instance): def __init__(self, instance):
super(InstanceOperation, self).__init__(subject=instance) super(InstanceOperation, self).__init__(subject=instance)
...@@ -53,11 +55,26 @@ class InstanceOperation(Operation): ...@@ -53,11 +55,26 @@ class InstanceOperation(Operation):
def check_precond(self): def check_precond(self):
if self.instance.destroyed_at: if self.instance.destroyed_at:
raise self.instance.InstanceDestroyedError(self.instance) raise self.instance.InstanceDestroyedError(self.instance)
if self.accept_states:
if self.instance.status not in self.accept_states:
logger.debug("precond failed for %s: %s not in %s",
unicode(self.__class__),
unicode(self.instance.status),
unicode(self.accept_states))
raise self.instance.WrongStateError(self.instance)
if self.deny_states:
if self.instance.status in self.deny_states:
logger.debug("precond failed for %s: %s in %s",
unicode(self.__class__),
unicode(self.instance.status),
unicode(self.accept_states))
raise self.instance.WrongStateError(self.instance)
def check_auth(self, user): def check_auth(self, user):
if not self.instance.has_level(user, self.acl_level): if not self.instance.has_level(user, self.acl_level):
raise PermissionDenied("%s doesn't have the required ACL level." % raise humanize_exception(ugettext_noop(
user) "%(acl_level)s level is required for this operation."),
PermissionDenied(), acl_level=self.acl_level)
super(InstanceOperation, self).check_auth(user=user) super(InstanceOperation, self).check_auth(user=user)
...@@ -94,6 +111,7 @@ class AddInterfaceOperation(InstanceOperation): ...@@ -94,6 +111,7 @@ class AddInterfaceOperation(InstanceOperation):
description = _("Add a new network interface for the specified VLAN to " description = _("Add a new network interface for the specified VLAN to "
"the VM.") "the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def rollback(self, net, activity): def rollback(self, net, activity):
with activity.sub_activity( with activity.sub_activity(
...@@ -102,14 +120,11 @@ class AddInterfaceOperation(InstanceOperation): ...@@ -102,14 +120,11 @@ class AddInterfaceOperation(InstanceOperation):
net.destroy() net.destroy()
net.delete() net.delete()
def check_precond(self):
super(AddInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, vlan, managed=None): def _operation(self, activity, user, system, vlan, managed=None):
if not vlan.has_level(user, 'user'): if not vlan.has_level(user, 'user'):
raise PermissionDenied() raise humanize_exception(ugettext_noop(
"User acces to vlan %(vlan)s is required."),
PermissionDenied(), vlan=vlan)
if managed is None: if managed is None:
managed = vlan.managed managed = vlan.managed
...@@ -141,11 +156,7 @@ class CreateDiskOperation(InstanceOperation): ...@@ -141,11 +156,7 @@ class CreateDiskOperation(InstanceOperation):
name = _("create disk") name = _("create disk")
description = _("Create empty disk for the VM.") description = _("Create empty disk for the VM.")
required_perms = ('storage.create_empty_disk', ) required_perms = ('storage.create_empty_disk', )
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(CreateDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, size, activity, name=None): def _operation(self, user, size, activity, name=None):
from storage.models import Disk from storage.models import Disk
...@@ -183,11 +194,7 @@ class DownloadDiskOperation(InstanceOperation): ...@@ -183,11 +194,7 @@ class DownloadDiskOperation(InstanceOperation):
abortable = True abortable = True
has_percentage = True has_percentage = True
required_perms = ('storage.download_disk', ) required_perms = ('storage.download_disk', )
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(DownloadDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, url, task, activity, name=None): def _operation(self, user, url, task, activity, name=None):
activity.result = url activity.result = url
...@@ -218,11 +225,7 @@ class DeployOperation(InstanceOperation): ...@@ -218,11 +225,7 @@ class DeployOperation(InstanceOperation):
name = _("deploy") name = _("deploy")
description = _("Deploy new virtual machine with network.") description = _("Deploy new virtual machine with network.")
required_perms = () required_perms = ()
deny_states = ('SUSPENDED', 'RUNNING')
def check_precond(self):
super(DeployOperation, self).check_precond()
if self.instance.status in ['RUNNING', 'SUSPENDED']:
raise self.instance.WrongStateError(self.instance)
def is_preferred(self): def is_preferred(self):
return self.instance.status in (self.instance.STATUS.STOPPED, return self.instance.status in (self.instance.STATUS.STOPPED,
...@@ -323,6 +326,7 @@ class MigrateOperation(InstanceOperation): ...@@ -323,6 +326,7 @@ class MigrateOperation(InstanceOperation):
name = _("migrate") name = _("migrate")
description = _("Live migrate running VM to another node.") description = _("Live migrate running VM to another node.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def rollback(self, activity): def rollback(self, activity):
with activity.sub_activity( with activity.sub_activity(
...@@ -330,11 +334,6 @@ class MigrateOperation(InstanceOperation): ...@@ -330,11 +334,6 @@ class MigrateOperation(InstanceOperation):
"redeploy network (rollback)")): "redeploy network (rollback)")):
self.instance.deploy_net() self.instance.deploy_net()
def check_precond(self):
super(MigrateOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def check_auth(self, user): def check_auth(self, user):
if not user.is_superuser: if not user.is_superuser:
raise PermissionDenied() raise PermissionDenied()
...@@ -384,11 +383,7 @@ class RebootOperation(InstanceOperation): ...@@ -384,11 +383,7 @@ class RebootOperation(InstanceOperation):
name = _("reboot") name = _("reboot")
description = _("Reboot virtual machine with Ctrl+Alt+Del signal.") description = _("Reboot virtual machine with Ctrl+Alt+Del signal.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(RebootOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, timeout=5): def _operation(self, timeout=5):
self.instance.reboot_vm(timeout=timeout) self.instance.reboot_vm(timeout=timeout)
...@@ -403,11 +398,7 @@ class RemoveInterfaceOperation(InstanceOperation): ...@@ -403,11 +398,7 @@ class RemoveInterfaceOperation(InstanceOperation):
name = _("remove interface") name = _("remove interface")
description = _("Remove the specified network interface from the VM.") description = _("Remove the specified network interface from the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(RemoveInterfaceOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, interface): def _operation(self, activity, user, system, interface):
if self.instance.is_running: if self.instance.is_running:
...@@ -428,11 +419,7 @@ class RemoveDiskOperation(InstanceOperation): ...@@ -428,11 +419,7 @@ class RemoveDiskOperation(InstanceOperation):
name = _("remove disk") name = _("remove disk")
description = _("Remove the specified disk from the VM.") description = _("Remove the specified disk from the VM.")
required_perms = () required_perms = ()
accept_states = ('STOPPED', 'PENDING', 'RUNNING')
def check_precond(self):
super(RemoveDiskOperation, self).check_precond()
if self.instance.status not in ['STOPPED', 'PENDING', 'RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, disk): def _operation(self, activity, user, system, disk):
if self.instance.is_running and disk.type not in ["iso"]: if self.instance.is_running and disk.type not in ["iso"]:
...@@ -450,11 +437,7 @@ class ResetOperation(InstanceOperation): ...@@ -450,11 +437,7 @@ class ResetOperation(InstanceOperation):
name = _("reset") name = _("reset")
description = _("Reset virtual machine (reset button).") description = _("Reset virtual machine (reset button).")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ResetOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, timeout=5): def _operation(self, timeout=5):
self.instance.reset_vm(timeout=timeout) self.instance.reset_vm(timeout=timeout)
...@@ -473,6 +456,7 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -473,6 +456,7 @@ class SaveAsTemplateOperation(InstanceOperation):
""") """)
abortable = True abortable = True
required_perms = ('vm.create_template', ) required_perms = ('vm.create_template', )
accept_states = ('RUNNING', 'PENDING', 'STOPPED')
def is_preferred(self): def is_preferred(self):
return (self.instance.is_base and return (self.instance.is_base and
...@@ -493,11 +477,6 @@ class SaveAsTemplateOperation(InstanceOperation): ...@@ -493,11 +477,6 @@ class SaveAsTemplateOperation(InstanceOperation):
for disk in self.disks: for disk in self.disks:
disk.destroy() disk.destroy()
def check_precond(self):
super(SaveAsTemplateOperation, self).check_precond()
if self.instance.status not in ['RUNNING', 'PENDING', 'STOPPED']:
raise self.instance.WrongStateError(self.instance)
def _operation(self, activity, user, system, timeout=300, name=None, def _operation(self, activity, user, system, timeout=300, name=None,
with_shutdown=True, task=None, **kwargs): with_shutdown=True, task=None, **kwargs):
if with_shutdown: if with_shutdown:
...@@ -567,11 +546,7 @@ class ShutdownOperation(InstanceOperation): ...@@ -567,11 +546,7 @@ class ShutdownOperation(InstanceOperation):
description = _("Shutdown virtual machine with ACPI signal.") description = _("Shutdown virtual machine with ACPI signal.")
abortable = True abortable = True
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ShutdownOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'STOPPED' activity.resultant_state = 'STOPPED'
...@@ -591,11 +566,7 @@ class ShutOffOperation(InstanceOperation): ...@@ -591,11 +566,7 @@ class ShutOffOperation(InstanceOperation):
name = _("shut off") name = _("shut off")
description = _("Shut off VM (plug-out).") description = _("Shut off VM (plug-out).")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ShutOffOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'STOPPED' activity.resultant_state = 'STOPPED'
...@@ -623,16 +594,12 @@ class SleepOperation(InstanceOperation): ...@@ -623,16 +594,12 @@ class SleepOperation(InstanceOperation):
name = _("sleep") name = _("sleep")
description = _("Suspend virtual machine with memory dump.") description = _("Suspend virtual machine with memory dump.")
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def is_preferred(self): def is_preferred(self):
return (not self.instance.is_base and return (not self.instance.is_base and
self.instance.status == self.instance.STATUS.RUNNING) self.instance.status == self.instance.STATUS.RUNNING)
def check_precond(self):
super(SleepOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error): def on_abort(self, activity, error):
if isinstance(error, TimeLimitExceeded): if isinstance(error, TimeLimitExceeded):
activity.resultant_state = None activity.resultant_state = None
...@@ -670,15 +637,11 @@ class WakeUpOperation(InstanceOperation): ...@@ -670,15 +637,11 @@ class WakeUpOperation(InstanceOperation):
Power on Virtual Machine and load its memory from dump. Power on Virtual Machine and load its memory from dump.
""") """)
required_perms = () required_perms = ()
accept_states = ('SUSPENDED', )
def is_preferred(self): def is_preferred(self):
return self.instance.status == self.instance.STATUS.SUSPENDED return self.instance.status == self.instance.STATUS.SUSPENDED
def check_precond(self):
super(WakeUpOperation, self).check_precond()
if self.instance.status not in ['SUSPENDED']:
raise self.instance.WrongStateError(self.instance)
def on_abort(self, activity, error): def on_abort(self, activity, error):
activity.resultant_state = 'ERROR' activity.resultant_state = 'ERROR'
...@@ -718,11 +681,6 @@ class RenewOperation(InstanceOperation): ...@@ -718,11 +681,6 @@ class RenewOperation(InstanceOperation):
required_perms = () required_perms = ()
concurrency_check = False concurrency_check = False
def check_precond(self):
super(RenewOperation, self).check_precond()
if self.instance.status == 'DESTROYED':
raise self.instance.WrongStateError(self.instance)
def _operation(self, lease=None): def _operation(self, lease=None):
(self.instance.time_of_suspend, (self.instance.time_of_suspend,
self.instance.time_of_delete) = self.instance.get_renew_times(lease) self.instance.time_of_delete) = self.instance.get_renew_times(lease)
...@@ -790,7 +748,8 @@ class FlushOperation(NodeOperation): ...@@ -790,7 +748,8 @@ class FlushOperation(NodeOperation):
def check_auth(self, user): def check_auth(self, user):
if not user.is_superuser: if not user.is_superuser:
raise PermissionDenied() raise humanize_exception(ugettext_noop(
"Superuser privileges are required."), PermissionDenied())
super(FlushOperation, self).check_auth(user=user) super(FlushOperation, self).check_auth(user=user)
...@@ -815,11 +774,7 @@ class ScreenshotOperation(InstanceOperation): ...@@ -815,11 +774,7 @@ class ScreenshotOperation(InstanceOperation):
description = _("Get screenshot") description = _("Get screenshot")
acl_level = "owner" acl_level = "owner"
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(ScreenshotOperation, self).check_precond()
if self.instance.status not in ['RUNNING']:
raise self.instance.WrongStateError(self.instance)
def _operation(self): def _operation(self):
return self.instance.get_screenshot(timeout=20) return self.instance.get_screenshot(timeout=20)
...@@ -835,10 +790,13 @@ class RecoverOperation(InstanceOperation): ...@@ -835,10 +790,13 @@ class RecoverOperation(InstanceOperation):
description = _("Recover virtual machine from destroyed state.") description = _("Recover virtual machine from destroyed state.")
acl_level = "owner" acl_level = "owner"
required_perms = ('vm.recover', ) required_perms = ('vm.recover', )
accept_states = ('DESTROYED', )
def check_precond(self): def check_precond(self):
if not self.instance.destroyed_at: try:
raise self.instance.WrongStateError(self.instance) super(RecoverOperation, self).check_precond()
except Instance.InstanceDestroyedError:
pass
def on_commit(self, activity): def on_commit(self, activity):
activity.resultant_state = 'PENDING' activity.resultant_state = 'PENDING'
...@@ -862,11 +820,7 @@ class ResourcesOperation(InstanceOperation): ...@@ -862,11 +820,7 @@ class ResourcesOperation(InstanceOperation):
description = _("Change resources") description = _("Change resources")
acl_level = "owner" acl_level = "owner"
required_perms = ('vm.change_resources', ) required_perms = ('vm.change_resources', )
accept_states = ('STOPPED', 'PENDING', )
def check_precond(self):
super(ResourcesOperation, self).check_precond()
if self.instance.status not in ["STOPPED", "PENDING"]:
raise self.instance.WrongStateError(self.instance)
def _operation(self, user, num_cores, ram_size, max_ram_size, priority): def _operation(self, user, num_cores, ram_size, max_ram_size, priority):
...@@ -889,11 +843,7 @@ class PasswordResetOperation(InstanceOperation): ...@@ -889,11 +843,7 @@ class PasswordResetOperation(InstanceOperation):
description = _("Password reset") description = _("Password reset")
acl_level = "owner" acl_level = "owner"
required_perms = () required_perms = ()
accept_states = ('RUNNING', )
def check_precond(self):
super(PasswordResetOperation, self).check_precond()
if self.instance.status not in ["RUNNING"]:
raise self.instance.WrongStateError(self.instance)
def _operation(self): def _operation(self):
self.instance.pw = pwgen() self.instance.pw = pwgen()
......
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