Commit 5c0672bc by Őry Máté

Merge branch 'nostate-operation' into 'master'

Nostate Operation
parents 46a12f1e 865a8ba5
......@@ -536,7 +536,7 @@ class VmDetailTest(LoginMixin, TestCase):
with patch.object(WakeUpOperation, 'async') as mock_method:
inst = Instance.objects.get(pk=1)
mock_method.side_effect = inst.wake_up
inst.manual_state_change('RUNNING')
inst.status = 'RUNNING'
inst.set_level(self.u2, 'owner')
with patch('dashboard.views.messages') as msg:
c.post("/dashboard/vm/1/op/wake_up/")
......@@ -554,7 +554,7 @@ class VmDetailTest(LoginMixin, TestCase):
inst = Instance.objects.get(pk=1)
new_wake_up.side_effect = inst.wake_up
inst.get_remote_queue_name = Mock(return_value='test')
inst.manual_state_change('SUSPENDED')
inst.status = 'SUSPENDED'
inst.set_level(self.u2, 'owner')
with patch('dashboard.views.messages') as msg:
response = c.post("/dashboard/vm/1/op/wake_up/")
......@@ -568,12 +568,10 @@ class VmDetailTest(LoginMixin, TestCase):
c = Client()
self.login(c, "user2")
inst = Instance.objects.get(pk=1)
inst.manual_state_change('SUSPENDED')
inst.status = 'SUSPENDED'
inst.set_level(self.u2, 'user')
response = c.post("/dashboard/vm/1/op/wake_up/")
self.assertEqual(response.status_code, 403)
inst = Instance.objects.get(pk=1)
self.assertEqual(inst.status, 'SUSPENDED')
def test_non_existing_template_get(self):
c = Client()
......
......@@ -335,6 +335,7 @@ class VmDetailView(CheckedDetailView):
for k, v in options.iteritems():
if request.POST.get(k) is not None:
return v(request)
raise Http404()
raise Http404()
......@@ -866,6 +867,8 @@ vm_ops = OrderedDict([
op='shut_off', icon='ban', effect='warning')),
('recover', VmOperationView.factory(
op='recover', icon='medkit', effect='warning')),
('nostate', VmOperationView.factory(
op='emergency_change_state', icon='legal', effect='danger')),
('destroy', VmOperationView.factory(
extra_bases=[TokenOperationView],
op='destroy', icon='times', effect='danger')),
......
......@@ -30,7 +30,7 @@ from django.utils.translation import ugettext_lazy as _, ugettext_noop
from common.models import (
ActivityModel, activitycontextimpl, create_readable, join_activity_code,
HumanReadableObject,
HumanReadableObject, HumanReadableException,
)
from manager.mancelery import celery
......@@ -39,16 +39,17 @@ from manager.mancelery import celery
logger = getLogger(__name__)
class ActivityInProgressError(Exception):
class ActivityInProgressError(HumanReadableException):
def __init__(self, activity, message=None):
if message is None:
message = ("Another activity is currently in progress: '%s'."
% activity.activity_code)
Exception.__init__(self, message)
self.activity = activity
@classmethod
def create(cls, activity):
obj = super(ActivityInProgressError, cls).create(
ugettext_noop("%(activity)s activity is currently in progress."),
ugettext_noop("%(activity)s (%(pk)s) activity is currently "
"in progress."),
activity=activity.readable_name, pk=activity.pk)
obj.activity = activity
return obj
def _normalize_readable_name(name, default=None):
......@@ -95,7 +96,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
active_activities = instance.activity_log.filter(finished__isnull=True)
if concurrency_check and active_activities.exists():
raise ActivityInProgressError(active_activities[0])
raise ActivityInProgressError.create(active_activities[0])
activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
act = cls(activity_code=activity_code, instance=instance, parent=None,
......@@ -112,7 +113,7 @@ class InstanceActivity(ActivityModel):
# Check for concurrent activities
active_children = self.children.filter(finished__isnull=True)
if concurrency_check and active_children.exists():
raise ActivityInProgressError(active_children[0])
raise ActivityInProgressError.create(active_children[0])
act = InstanceActivity(
activity_code=join_activity_code(self.activity_code, code_suffix),
......
......@@ -271,6 +271,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
('create_vm', _('Can create a new VM.')),
('config_ports', _('Can configure port forwards.')),
('recover', _('Can recover a destroyed VM.')),
('emergency_change_state', _('Can change VM state to NOSTATE.')),
)
verbose_name = _('instance')
verbose_name_plural = _('instances')
......@@ -444,27 +445,12 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
self.time_of_suspend, self.time_of_delete = self.get_renew_times()
super(Instance, self).clean(*args, **kwargs)
def manual_state_change(self, new_state="NOSTATE", reason=None, user=None):
""" Manually change state of an Instance.
Can be used to recover VM after administrator fixed problems.
"""
# TODO cancel concurrent activity (if exists)
act = InstanceActivity.create(
code_suffix='manual_state_change', instance=self, user=user,
readable_name=create_readable(ugettext_noop(
"force %(state)s state"), state=new_state))
act.finished = act.started
act.result = reason
act.resultant_state = new_state
act.succeeded = True
act.save()
def vm_state_changed(self, new_state):
# log state change
try:
act = InstanceActivity.create(code_suffix='vm_state_changed',
instance=self)
instance=self,
readable_name="vm state changed")
except ActivityInProgressError:
pass # discard state change if another activity is in progress.
else:
......
......@@ -722,6 +722,21 @@ class RenewOperation(InstanceOperation):
register_operation(RenewOperation)
class ChangeStateOperation(InstanceOperation):
activity_code_suffix = 'emergency_change_state'
id = 'emergency_change_state'
name = _("emergency change state")
description = _("Change the virtual machine state to NOSTATE")
acl_level = "owner"
required_perms = ('vm.emergency_change_state', )
def _operation(self, user, activity, new_state="NOSTATE"):
activity.resultant_state = new_state
register_operation(ChangeStateOperation)
class NodeOperation(Operation):
async_operation = abortable_async_node_operation
host_cls = Node
......
......@@ -66,8 +66,8 @@ class InstanceTestCase(TestCase):
inst = MagicMock(spec=Instance, node=node, vnc_port=port)
inst.save.side_effect = AssertionError
with patch('vm.models.instance.InstanceActivity') as ia:
ia.create.side_effect = ActivityInProgressError(MagicMock())
Instance.vm_state_changed(inst, 'STOPPED')
ia.create.side_effect = ActivityInProgressError.create(MagicMock())
Instance.status = 'STOPPED'
self.assertEquals(inst.node, node)
self.assertEquals(inst.vnc_port, port)
......
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