From cbf6979aaea215220e18b9a25a5d6d08f8f3ac3e Mon Sep 17 00:00:00 2001
From: Őry Máté <ory.mate@cloud.bme.hu>
Date: Tue, 23 Sep 2014 21:31:39 +0200
Subject: [PATCH] vm: add new Node operations

---
 circle/vm/models/node.py | 29 +++--------------------------
 circle/vm/operations.py  | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 82 insertions(+), 36 deletions(-)

diff --git a/circle/vm/models/node.py b/circle/vm/models/node.py
index b0dab3d..30554cd 100644
--- a/circle/vm/models/node.py
+++ b/circle/vm/models/node.py
@@ -27,7 +27,7 @@ from django.db.models import (
     FloatField, permalink,
 )
 from django.utils import timezone
-from django.utils.translation import ugettext_lazy as _, ugettext_noop
+from django.utils.translation import ugettext_lazy as _
 
 from celery.exceptions import TimeoutError
 from model_utils.models import TimeStampedModel
@@ -37,7 +37,7 @@ from common.models import method_cache, WorkerNotFound, HumanSortField
 from common.operations import OperatedMixin
 from firewall.models import Host
 from ..tasks import vm_tasks
-from .activity import node_activity, NodeActivity
+from .activity import NodeActivity
 from .common import Trait
 
 
@@ -145,31 +145,8 @@ class Node(OperatedMixin, TimeStampedModel):
     def get_status_display(self):
         return self.STATES[self.enabled][self.online][1]
 
-    def disable(self, user=None, base_activity=None):
-        ''' Disable the node.'''
-        if self.enabled:
-            if base_activity:
-                act_ctx = base_activity.sub_activity(
-                    'disable', readable_name=ugettext_noop("disable node"))
-            else:
-                act_ctx = node_activity(
-                    'disable', node=self, user=user,
-                    readable_name=ugettext_noop("disable node"))
-            with act_ctx:
-                self.enabled = False
-                self.save()
-
     def enable(self, user=None, base_activity=None):
-        ''' Enable the node. '''
-        if self.enabled is not True:
-            if base_activity:
-                act_ctx = base_activity.sub_activity('enable')
-            else:
-                act_ctx = node_activity('enable', node=self, user=user)
-            with act_ctx:
-                self.enabled = True
-                self.save()
-            self.get_info(invalidate_cache=True)
+        raise NotImplementedError("Use activate or passivate instead.")
 
     @property
     @node_available
diff --git a/circle/vm/operations.py b/circle/vm/operations.py
index 43e6828..b42c19a 100644
--- a/circle/vm/operations.py
+++ b/circle/vm/operations.py
@@ -803,11 +803,20 @@ class ChangeStateOperation(InstanceOperation):
 class NodeOperation(Operation):
     async_operation = abortable_async_node_operation
     host_cls = Node
+    online_required = True
+    superuser_required = True
 
     def __init__(self, node):
         super(NodeOperation, self).__init__(subject=node)
         self.node = node
 
+    def check_precond(self):
+        super(NodeOperation, self).check_precond()
+        if self.online_required and not self.node.online:
+            raise humanize_exception(ugettext_noop(
+                "You cannot call this operation on an offline node."),
+                Exception())
+
     def create_activity(self, parent, user, kwargs):
         name = self.get_activity_name(kwargs)
         if parent:
@@ -833,20 +842,14 @@ class FlushOperation(NodeOperation):
     activity_code_suffix = 'flush'
     id = 'flush'
     name = _("flush")
-    description = _("Disable node and move all instances to other ones.")
+    description = _("Passivate node and move all instances to other ones.")
     required_perms = ()
-    superuser_required = True
     async_queue = "localhost.man.slow"
 
-    def on_abort(self, activity, error):
-        from manager.scheduler import TraitsUnsatisfiableException
-        if isinstance(error, TraitsUnsatisfiableException):
-            if self.node_enabled:
-                self.node.enable(activity.user, activity)
-
     def _operation(self, activity, user):
-        self.node_enabled = self.node.enabled
-        self.node.disable(user, activity)
+        if self.node.schedule_enabled:
+            PassivateOperation(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)
@@ -855,6 +858,72 @@ class FlushOperation(NodeOperation):
                 i.migrate(user=user)
 
 
+@register_operation
+class ActivateOperation(NodeOperation):
+    activity_code_suffix = 'activate'
+    id = 'activate'
+    name = _("activate")
+    description = _("Make node active, i.e. scheduler is allowed to deploy "
+                    "virtual machines to it.")
+    required_perms = ()
+
+    def check_precond(self):
+        super(ActivateOperation, self).check_precond()
+        if self.node.enabled and self.node.schedule_enabled:
+            raise humanize_exception(ugettext_noop(
+                "You cannot activate an active node."), Exception())
+
+    def _operation(self):
+        self.node.enabled = True
+        self.node.schedule_enabled = True
+        self.node.save()
+
+
+@register_operation
+class PassivateOperation(NodeOperation):
+    activity_code_suffix = 'passivate'
+    id = 'passivate'
+    name = _("passivate")
+    description = _("Make node passive, i.e. scheduler is denied to deploy "
+                    "virtual machines to it, but remaining instances and "
+                    "the ones manually migrated will continue running.")
+    required_perms = ()
+
+    def check_precond(self):
+        if self.node.enabled and not self.node.schedule_enabled:
+            raise humanize_exception(ugettext_noop(
+                "You cannot passivate a passive node."), Exception())
+        super(PassivateOperation, self).check_precond()
+
+    def _operation(self):
+        self.node.enabled = True
+        self.node.schedule_enabled = False
+        self.node.save()
+
+
+@register_operation
+class DisableOperation(NodeOperation):
+    activity_code_suffix = 'disable'
+    id = 'disable'
+    name = _("disable")
+    description = _("Disable node.")
+    required_perms = ()
+    online_required = False
+
+    def check_precond(self):
+        if not self.node.enabled:
+            raise humanize_exception(ugettext_noop(
+                "You cannot disable a disabled node."), Exception())
+        if self.node.instance_set.exists():
+            raise humanize_exception(ugettext_noop(
+                "You cannot disable a node which is hosting instances."),
+                Exception())
+        super(DisableOperation, self).check_precond()
+
+    def _operation(self):
+        self.node.enabled = False
+        self.node.schedule_enabled = False
+        self.node.save()
 
 
 @register_operation
--
libgit2 0.26.0