Commit a3ceda07 by Bach Dániel

Merge branch 'feature-redeploy' into 'master'

Feature Redeploy

🚧 test

See merge request !218
parents f0929ad9 a431c8f2
......@@ -752,6 +752,7 @@ class VmStateChangeForm(forms.Form):
"but don't interrupt any tasks."))
new_state = forms.ChoiceField(Instance.STATUS, label=_(
"New status"))
reset_node = forms.BooleanField(required=False, label=_("Reset node"))
def __init__(self, *args, **kwargs):
show_interrupt = kwargs.pop('show_interrupt')
......@@ -769,6 +770,17 @@ class VmStateChangeForm(forms.Form):
return helper
class RedeployForm(forms.Form):
with_emergency_change_state = forms.BooleanField(
required=False, initial=True, label=_("use emergency state change"))
@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(
......
......@@ -68,6 +68,8 @@ node_ops = OrderedDict([
op='passivate', icon='play-circle-o', effect='info')),
('disable', NodeOperationView.factory(
op='disable', icon='times-circle-o', effect='danger')),
('reset', NodeOperationView.factory(
op='reset', icon='stethoscope', effect='danger')),
('flush', NodeOperationView.factory(
op='flush', icon='paint-brush', effect='danger')),
])
......
......@@ -58,7 +58,7 @@ from ..forms import (
AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm,
VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm,
VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
TransferOwnershipForm, VmDiskResizeForm,
TransferOwnershipForm, VmDiskResizeForm, RedeployForm,
)
from ..models import Favourite, Profile
......@@ -599,6 +599,15 @@ class VmStateChangeView(FormOperationMixin, VmOperationView):
return val
class RedeployView(FormOperationMixin, VmOperationView):
op = 'redeploy'
icon = 'stethoscope'
effect = 'danger'
show_in_toolbar = True
form_class = RedeployForm
wait_for_result = 0.5
vm_ops = OrderedDict([
('deploy', VmOperationView.factory(
op='deploy', icon='play', effect='success')),
......@@ -620,6 +629,7 @@ vm_ops = OrderedDict([
('recover', VmOperationView.factory(
op='recover', icon='medkit', effect='warning')),
('nostate', VmStateChangeView),
('redeploy', RedeployView),
('destroy', VmOperationView.factory(
extra_bases=[TokenOperationView],
op='destroy', icon='times', effect='danger')),
......
......@@ -275,6 +275,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
('change_resources', _('Can change resources of a running VM.')),
('set_resources', _('Can change resources of a new VM.')),
('create_vm', _('Can create a new VM.')),
('redeploy', _('Can redeploy a VM.')),
('config_ports', _('Can configure port forwards.')),
('recover', _('Can recover a destroyed VM.')),
('emergency_change_state', _('Can change VM state to NOSTATE.')),
......
......@@ -806,7 +806,8 @@ class ChangeStateOperation(InstanceOperation):
required_perms = ('vm.emergency_change_state', )
concurrency_check = False
def _operation(self, user, activity, new_state="NOSTATE", interrupt=False):
def _operation(self, user, activity, new_state="NOSTATE", interrupt=False,
reset_node=False):
activity.resultant_state = new_state
if interrupt:
msg_txt = ugettext_noop("Activity is forcibly interrupted.")
......@@ -816,6 +817,37 @@ class ChangeStateOperation(InstanceOperation):
i.finish(False, result=message)
logger.error('Forced finishing activity %s', i)
if reset_node:
self.instance.node = None
self.instance.save()
@register_operation
class RedeployOperation(InstanceOperation):
activity_code_suffix = 'redeploy'
id = 'redeploy'
name = _("redeploy")
description = _("Change the virtual machine state to NOSTATE "
"and redeploy the VM. This operation allows starting "
"machines formerly running on a failed node.")
acl_level = "owner"
required_perms = ('vm.redeploy', )
concurrency_check = False
def _operation(self, user, activity, with_emergency_change_state=True):
if with_emergency_change_state:
ChangeStateOperation(self.instance).call(
parent_activity=activity, user=user,
new_state='NOSTATE', interrupt=False, reset_node=True)
else:
ShutOffOperation(self.instance).call(
parent_activity=activity, user=user)
self.instance._update_status()
DeployOperation(self.instance).call(
parent_activity=activity, user=user)
class NodeOperation(Operation):
async_operation = abortable_async_node_operation
......@@ -855,6 +887,35 @@ class NodeOperation(Operation):
@register_operation
class ResetNodeOperation(NodeOperation):
activity_code_suffix = 'reset'
id = 'reset'
name = _("reset")
description = _("Disable missing node and redeploy all instances "
"on other ones.")
required_perms = ()
online_required = False
async_queue = "localhost.man.slow"
def check_precond(self):
super(ResetNodeOperation, self).check_precond()
if not self.node.enabled or self.node.online:
raise humanize_exception(ugettext_noop(
"You cannot reset a disabled or online node."), Exception())
def _operation(self, activity, user):
if self.node.enabled:
DisableOperation(self.node).call(parent_activity=activity,
user=user)
for i in self.node.instance_set.all():
name = create_readable(ugettext_noop(
"migrate %(instance)s (%(pk)s)"), instance=i.name, pk=i.pk)
with activity.sub_activity('migrate_instance_%d' % i.pk,
readable_name=name):
i.redeploy(user=user)
@register_operation
class FlushOperation(NodeOperation):
activity_code_suffix = 'flush'
id = 'flush'
......
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