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): ...@@ -752,6 +752,7 @@ class VmStateChangeForm(forms.Form):
"but don't interrupt any tasks.")) "but don't interrupt any tasks."))
new_state = forms.ChoiceField(Instance.STATUS, label=_( new_state = forms.ChoiceField(Instance.STATUS, label=_(
"New status")) "New status"))
reset_node = forms.BooleanField(required=False, label=_("Reset node"))
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
show_interrupt = kwargs.pop('show_interrupt') show_interrupt = kwargs.pop('show_interrupt')
...@@ -769,6 +770,17 @@ class VmStateChangeForm(forms.Form): ...@@ -769,6 +770,17 @@ class VmStateChangeForm(forms.Form):
return helper 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): class VmCreateDiskForm(forms.Form):
name = forms.CharField(max_length=100, label=_("Name")) name = forms.CharField(max_length=100, label=_("Name"))
size = forms.CharField( size = forms.CharField(
......
...@@ -68,6 +68,8 @@ node_ops = OrderedDict([ ...@@ -68,6 +68,8 @@ node_ops = OrderedDict([
op='passivate', icon='play-circle-o', effect='info')), op='passivate', icon='play-circle-o', effect='info')),
('disable', NodeOperationView.factory( ('disable', NodeOperationView.factory(
op='disable', icon='times-circle-o', effect='danger')), op='disable', icon='times-circle-o', effect='danger')),
('reset', NodeOperationView.factory(
op='reset', icon='stethoscope', effect='danger')),
('flush', NodeOperationView.factory( ('flush', NodeOperationView.factory(
op='flush', icon='paint-brush', effect='danger')), op='flush', icon='paint-brush', effect='danger')),
]) ])
......
...@@ -58,7 +58,7 @@ from ..forms import ( ...@@ -58,7 +58,7 @@ from ..forms import (
AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm, AclUserOrGroupAddForm, VmResourcesForm, TraitsForm, RawDataForm,
VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm, VmAddInterfaceForm, VmCreateDiskForm, VmDownloadDiskForm, VmSaveForm,
VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm, VmRenewForm, VmStateChangeForm, VmListSearchForm, VmCustomizeForm,
TransferOwnershipForm, VmDiskResizeForm, TransferOwnershipForm, VmDiskResizeForm, RedeployForm,
) )
from ..models import Favourite, Profile from ..models import Favourite, Profile
...@@ -599,6 +599,15 @@ class VmStateChangeView(FormOperationMixin, VmOperationView): ...@@ -599,6 +599,15 @@ class VmStateChangeView(FormOperationMixin, VmOperationView):
return val 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([ vm_ops = OrderedDict([
('deploy', VmOperationView.factory( ('deploy', VmOperationView.factory(
op='deploy', icon='play', effect='success')), op='deploy', icon='play', effect='success')),
...@@ -620,6 +629,7 @@ vm_ops = OrderedDict([ ...@@ -620,6 +629,7 @@ vm_ops = OrderedDict([
('recover', VmOperationView.factory( ('recover', VmOperationView.factory(
op='recover', icon='medkit', effect='warning')), op='recover', icon='medkit', effect='warning')),
('nostate', VmStateChangeView), ('nostate', VmStateChangeView),
('redeploy', RedeployView),
('destroy', VmOperationView.factory( ('destroy', VmOperationView.factory(
extra_bases=[TokenOperationView], extra_bases=[TokenOperationView],
op='destroy', icon='times', effect='danger')), op='destroy', icon='times', effect='danger')),
......
...@@ -275,6 +275,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin, ...@@ -275,6 +275,7 @@ class Instance(AclBase, VirtualMachineDescModel, StatusModel, OperatedMixin,
('change_resources', _('Can change resources of a running VM.')), ('change_resources', _('Can change resources of a running VM.')),
('set_resources', _('Can change resources of a new VM.')), ('set_resources', _('Can change resources of a new VM.')),
('create_vm', _('Can create a new VM.')), ('create_vm', _('Can create a new VM.')),
('redeploy', _('Can redeploy a VM.')),
('config_ports', _('Can configure port forwards.')), ('config_ports', _('Can configure port forwards.')),
('recover', _('Can recover a destroyed VM.')), ('recover', _('Can recover a destroyed VM.')),
('emergency_change_state', _('Can change VM state to NOSTATE.')), ('emergency_change_state', _('Can change VM state to NOSTATE.')),
......
...@@ -806,7 +806,8 @@ class ChangeStateOperation(InstanceOperation): ...@@ -806,7 +806,8 @@ class ChangeStateOperation(InstanceOperation):
required_perms = ('vm.emergency_change_state', ) required_perms = ('vm.emergency_change_state', )
concurrency_check = False 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 activity.resultant_state = new_state
if interrupt: if interrupt:
msg_txt = ugettext_noop("Activity is forcibly interrupted.") msg_txt = ugettext_noop("Activity is forcibly interrupted.")
...@@ -816,6 +817,37 @@ class ChangeStateOperation(InstanceOperation): ...@@ -816,6 +817,37 @@ class ChangeStateOperation(InstanceOperation):
i.finish(False, result=message) i.finish(False, result=message)
logger.error('Forced finishing activity %s', i) 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): class NodeOperation(Operation):
async_operation = abortable_async_node_operation async_operation = abortable_async_node_operation
...@@ -855,6 +887,35 @@ class NodeOperation(Operation): ...@@ -855,6 +887,35 @@ class NodeOperation(Operation):
@register_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): class FlushOperation(NodeOperation):
activity_code_suffix = 'flush' activity_code_suffix = 'flush'
id = '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