activity.py 6.49 KB
Newer Older
1
from __future__ import absolute_import, unicode_literals
Őry Máté committed
2 3 4
from contextlib import contextmanager
from logging import getLogger

Kálmán Viktor committed
5 6
from celery.contrib.abortable import AbortableAsyncResult

7
from django.core.urlresolvers import reverse
8
from django.db.models import CharField, ForeignKey
Őry Máté committed
9 10 11
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

12
from common.models import (
13
    ActivityModel, activitycontextimpl, join_activity_code, split_activity_code
14 15
)

Kálmán Viktor committed
16 17
from manager.mancelery import celery

18

Őry Máté committed
19 20 21
logger = getLogger(__name__)


22 23 24 25 26 27 28 29 30 31 32 33
class ActivityInProgressError(Exception):

        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


Őry Máté committed
34
class InstanceActivity(ActivityModel):
35
    ACTIVITY_CODE_BASE = join_activity_code('vm', 'Instance')
Őry Máté committed
36 37 38
    instance = ForeignKey('Instance', related_name='activity_log',
                          help_text=_('Instance this activity works on.'),
                          verbose_name=_('instance'))
39
    resultant_state = CharField(blank=True, max_length=20, null=True)
Őry Máté committed
40 41 42 43

    class Meta:
        app_label = 'vm'
        db_table = 'vm_instanceactivity'
44
        ordering = ['-finished', '-started', 'instance', '-id']
Őry Máté committed
45 46 47 48 49 50 51 52 53 54

    def __unicode__(self):
        if self.parent:
            return '{}({})->{}'.format(self.parent.activity_code,
                                       self.instance,
                                       self.activity_code)
        else:
            return '{}({})'.format(self.activity_code,
                                   self.instance)

55 56 57
    def get_absolute_url(self):
        return reverse('dashboard.views.vm-activity', args=[self.pk])

Őry Máté committed
58
    def get_readable_name(self):
59 60
        activity_code_last_suffix = split_activity_code(self.activity_code)[-1]
        return activity_code_last_suffix.replace('_', ' ').capitalize()
Őry Máté committed
61

62 63 64 65 66 67 68 69
    def get_status_id(self):
        if self.succeeded is None:
            return 'wait'
        elif self.succeeded:
            return 'success'
        else:
            return 'failed'

Őry Máté committed
70
    @classmethod
71 72
    def create(cls, code_suffix, instance, task_uuid=None, user=None,
               concurrency_check=True):
73 74
        # Check for concurrent activities
        active_activities = instance.activity_log.filter(finished__isnull=True)
75
        if concurrency_check and active_activities.exists():
76 77
            raise ActivityInProgressError(active_activities[0])

78 79 80 81
        activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
        act = cls(activity_code=activity_code, instance=instance, parent=None,
                  resultant_state=None, started=timezone.now(),
                  task_uuid=task_uuid, user=user)
Őry Máté committed
82 83 84
        act.save()
        return act

85
    def create_sub(self, code_suffix, task_uuid=None, concurrency_check=True):
86 87
        # Check for concurrent activities
        active_children = self.children.filter(finished__isnull=True)
88
        if concurrency_check and active_children.exists():
89 90
            raise ActivityInProgressError(active_children[0])

Őry Máté committed
91
        act = InstanceActivity(
92
            activity_code=join_activity_code(self.activity_code, code_suffix),
93 94
            instance=self.instance, parent=self, resultant_state=None,
            started=timezone.now(), task_uuid=task_uuid, user=self.user)
Őry Máté committed
95 96 97 98
        act.save()
        return act

    @contextmanager
99
    def sub_activity(self, code_suffix, on_abort=None, on_commit=None,
100
                     task_uuid=None, concurrency_check=True):
101 102
        """Create a transactional context for a nested instance activity.
        """
103
        act = self.create_sub(code_suffix, task_uuid, concurrency_check)
104
        return activitycontextimpl(act, on_abort=on_abort, on_commit=on_commit)
Őry Máté committed
105

106 107 108 109 110
    def save(self, *args, **kwargs):
        ret = super(InstanceActivity, self).save(*args, **kwargs)
        self.instance._update_status()
        return ret

Kálmán Viktor committed
111 112 113 114 115 116 117
    def is_abortable(self):
        op = self.instance.get_operation_from_activity_code(self.activity_code)
        return False if op is None else (op.abortable and not self.finished)

    def abort(self):
        AbortableAsyncResult(self.task_uuid, backend=celery.backend).abort()

Őry Máté committed
118 119

@contextmanager
120
def instance_activity(code_suffix, instance, on_abort=None, on_commit=None,
121
                      task_uuid=None, user=None, concurrency_check=True):
122 123
    """Create a transactional context for an instance activity.
    """
124 125
    act = InstanceActivity.create(code_suffix, instance, task_uuid, user,
                                  concurrency_check)
126
    return activitycontextimpl(act, on_abort=on_abort, on_commit=on_commit)
127 128 129


class NodeActivity(ActivityModel):
130
    ACTIVITY_CODE_BASE = join_activity_code('vm', 'Node')
131 132 133 134 135 136 137 138
    node = ForeignKey('Node', related_name='activity_log',
                      help_text=_('Node this activity works on.'),
                      verbose_name=_('node'))

    class Meta:
        app_label = 'vm'
        db_table = 'vm_nodeactivity'

139 140 141 142 143 144 145 146 147 148 149 150
    def __unicode__(self):
        if self.parent:
            return '{}({})->{}'.format(self.parent.activity_code,
                                       self.node,
                                       self.activity_code)
        else:
            return '{}({})'.format(self.activity_code,
                                   self.node)

    def get_readable_name(self):
        return self.activity_code.split('.')[-1].replace('_', ' ').capitalize()

151 152
    @classmethod
    def create(cls, code_suffix, node, task_uuid=None, user=None):
153 154 155
        activity_code = join_activity_code(cls.ACTIVITY_CODE_BASE, code_suffix)
        act = cls(activity_code=activity_code, node=node, parent=None,
                  started=timezone.now(), task_uuid=task_uuid, user=user)
156 157 158 159 160
        act.save()
        return act

    def create_sub(self, code_suffix, task_uuid=None):
        act = NodeActivity(
161
            activity_code=join_activity_code(self.activity_code, code_suffix),
162 163 164 165 166 167 168 169 170 171 172 173 174
            node=self.node, parent=self, started=timezone.now(),
            task_uuid=task_uuid, user=self.user)
        act.save()
        return act

    @contextmanager
    def sub_activity(self, code_suffix, task_uuid=None):
        act = self.create_sub(code_suffix, task_uuid)
        return activitycontextimpl(act)


@contextmanager
def node_activity(code_suffix, node, task_uuid=None, user=None):
175
    act = NodeActivity.create(code_suffix, node, task_uuid, user)
176
    return activitycontextimpl(act)