diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py index 2cb9dc8..fc93883 100644 --- a/circle/dashboard/forms.py +++ b/circle/dashboard/forms.py @@ -714,6 +714,27 @@ class VmRenewForm(forms.Form): return helper +class VmStateChangeForm(forms.Form): + + interrupt = forms.BooleanField(required=False, label=_( + "Forcibly interrupt all running activities."), + help_text=_("Set all activities to finished state, " + "but don't interrupt any tasks.")) + + def __init__(self, *args, **kwargs): + show_interrupt = kwargs.pop('show_interrupt') + super(VmStateChangeForm, self).__init__(*args, **kwargs) + + if not show_interrupt: + self.fields['interrupt'].widget = HiddenInput() + + @property + def helper(self): + helper = FormHelper(self) + helper.form_tag = False + return helper + + class VmCreateDiskForm(forms.Form): name = forms.CharField(max_length=100, label=_("Name")) size = forms.CharField( diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index ab5053b..a89042f 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -67,7 +67,7 @@ from .forms import ( CircleAuthenticationForm, HostForm, LeaseForm, MyProfileForm, NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm, UserCreationForm, GroupProfileUpdateForm, UnsubscribeForm, - VmSaveForm, UserKeyForm, VmRenewForm, + VmSaveForm, UserKeyForm, VmRenewForm, VmStateChangeForm, CirclePasswordChangeForm, VmCreateDiskForm, VmDownloadDiskForm, TraitsForm, RawDataForm, GroupPermissionForm, AclUserAddForm, VmResourcesForm, VmAddInterfaceForm, VmListSearchForm @@ -936,6 +936,22 @@ class VmRenewView(FormOperationMixin, TokenOperationView, VmOperationView): return extra +class VmStateChangeView(FormOperationMixin, VmOperationView): + op = 'emergency_change_state' + icon = 'legal' + effect = 'danger' + show_in_toolbar = True + form_class = VmStateChangeForm + wait_for_result = 0.5 + + def get_form_kwargs(self): + inst = self.get_op().instance + show_interrupt = active_activities.exists() + val = super(VmStateChangeView, self).get_form_kwargs() + val.update({'show_interrupt': show_interrupt}) + return val + + vm_ops = OrderedDict([ ('deploy', VmOperationView.factory( op='deploy', icon='play', effect='success')), @@ -956,8 +972,7 @@ 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')), + ('nostate', VmStateChangeView), ('destroy', VmOperationView.factory( extra_bases=[TokenOperationView], op='destroy', icon='times', effect='danger')), diff --git a/circle/vm/operations.py b/circle/vm/operations.py index 5f7322e..5150d7d 100644 --- a/circle/vm/operations.py +++ b/circle/vm/operations.py @@ -788,9 +788,17 @@ class ChangeStateOperation(InstanceOperation): "resources.") acl_level = "owner" required_perms = ('vm.emergency_change_state', ) + concurrency_check = False - def _operation(self, user, activity, new_state="NOSTATE"): + def _operation(self, user, activity, new_state="NOSTATE", interrupt=False): activity.resultant_state = new_state + if interrupt: + msg_txt = ugettext_noop("Activity is forcibly interrupted.") + message = create_readable(msg_txt, msg_txt) + for i in InstanceActivity.objects.filter( + finished__isnull=True, instance=self.instance): + i.finish(False, result=message) + logger.error('Forced finishing activity %s', i) register_operation(ChangeStateOperation)