Commit f3aa35f9 by Dudás Ádám

storage, vm: change activity log format to hierarchical

parent 8cd8d2bf
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'DiskActivity.state'
db.delete_column(u'storage_diskactivity', 'state')
# Adding field 'DiskActivity.parent'
db.add_column(u'storage_diskactivity', 'parent',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['storage.DiskActivity'], null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Adding field 'DiskActivity.state'
db.add_column(u'storage_diskactivity', 'state',
self.gf('django.db.models.fields.CharField')(default='PENDING', max_length=50),
keep_default=False)
# Deleting field 'DiskActivity.parent'
db.delete_column(u'storage_diskactivity', 'parent_id')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'storage.datastore': {
'Meta': {'ordering': "['name']", 'object_name': 'DataStore'},
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
u'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'derivatives'", 'null': 'True', 'to': u"orm['storage.Disk']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DataStore']"}),
'destroyed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'dev_num': ('django.db.models.fields.CharField', [], {'default': "'a'", 'max_length': '1'}),
'filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'size': ('sizefield.models.FileSizeField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
},
u'storage.diskactivity': {
'Meta': {'object_name': 'DiskActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'disk': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log'", 'to': u"orm['storage.Disk']"}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DiskActivity']", 'null': 'True', 'blank': 'True'}),
'result': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['storage']
\ No newline at end of file
# coding=utf-8 # coding=utf-8
from contextlib import contextmanager
import logging import logging
import uuid import uuid
from django.contrib.auth.models import User
from django.db.models import (Model, BooleanField, CharField, DateTimeField, from django.db.models import (Model, BooleanField, CharField, DateTimeField,
ForeignKey, TextField) ForeignKey)
from django.utils import timezone from django.utils import timezone
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from sizefield.models import FileSizeField from sizefield.models import FileSizeField
from .tasks import local_tasks, remote_tasks from .tasks import local_tasks, remote_tasks
from common.models import ActivityModel, activitycontextimpl
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -160,31 +161,25 @@ class Disk(TimeStampedModel): ...@@ -160,31 +161,25 @@ class Disk(TimeStampedModel):
if self.ready: if self.ready:
return False return False
act = DiskActivity(activity_code='storage.Disk.deploy') with disk_activity(code_suffix='deploy', disk=self,
act.disk = self task_uuid=task_uuid, user=user) as act:
act.started = timezone.now()
act.state = 'PENDING' # Delegate create / snapshot jobs
act.task_uuid = task_uuid queue_name = self.datastore.hostname + ".storage"
act.user = user disk_desc = self.get_disk_desc()
act.save() if self.type == 'qcow2-snap':
with act.sub_activity('creating_snapshot'):
# Delegate create / snapshot jobs remote_tasks.snapshot.apply_async(args=[disk_desc],
queue_name = self.datastore.hostname + ".storage" queue=queue_name).get()
disk_desc = self.get_disk_desc() else:
if self.type == 'qcow2-snap': with act.sub_activity('creating_disk'):
act.update_state('CREATING SNAPSHOT') remote_tasks.create.apply_async(args=[disk_desc],
remote_tasks.snapshot.apply_async(args=[disk_desc], queue=queue_name).get()
queue=queue_name).get()
else: self.ready = True
act.update_state('CREATING DISK') self.save()
remote_tasks.create.apply_async(args=[disk_desc],
queue=queue_name).get()
self.ready = True
self.save()
act.finish('SUCCESS') return True
return True
def deploy_async(self, user=None): def deploy_async(self, user=None):
"""Execute deploy asynchronously. """Execute deploy asynchronously.
...@@ -224,53 +219,55 @@ class Disk(TimeStampedModel): ...@@ -224,53 +219,55 @@ class Disk(TimeStampedModel):
# from this point on, the caller has to guarantee that the disk is not # from this point on, the caller has to guarantee that the disk is not
# going to be used until the operation is complete # going to be used until the operation is complete
act = DiskActivity(activity_code='storage.Disk.save_as') with disk_activity(code_suffix='save_as', disk=self,
act.disk = self task_uuid=task_uuid, user=user):
act.started = timezone.now()
act.state = 'PENDING' filename = str(uuid.uuid4())
act.task_uuid = task_uuid new_type, new_base = mapping[self.type]
act.user = user
disk = Disk.objects.create(base=new_base, datastore=self.datastore,
filename=filename, name=name,
size=self.size, type=new_type)
queue_name = self.datastore.hostname + ".storage"
remote_tasks.merge.apply_async(args=[self.get_disk_desc(),
disk.get_disk_desc()],
queue=queue_name).get()
disk.ready = True
disk.save()
return disk
class DiskActivity(ActivityModel):
disk = ForeignKey(Disk, related_name='activity_log',
help_text=_('Disk this activity works on.'),
verbose_name=_('disk'))
@classmethod
def create(cls, code_suffix, instance, task_uuid=None, user=None):
act = cls(activity_code='storage.Disk.' + code_suffix,
instance=instance, parent=None, started=timezone.now(),
task_uuid=task_uuid, user=user)
act.save() act.save()
return act
filename = str(uuid.uuid4()) def create_sub(self, code_suffix, task_uuid=None):
new_type, new_base = mapping[self.type] act = DiskActivity(
activity_code=self.activity_code + '.' + code_suffix,
instance=self.instance, parent=self, started=timezone.now(),
task_uuid=task_uuid, user=self.user)
act.save()
return act
disk = Disk.objects.create(base=new_base, datastore=self.datastore, @contextmanager
filename=filename, name=name, def sub_activity(self, code_suffix, task_uuid=None):
size=self.size, type=new_type) act = self.create_sub(code_suffix, task_uuid)
activitycontextimpl(act)
queue_name = self.datastore.hostname + ".storage"
remote_tasks.merge.apply_async(args=[self.get_disk_desc(),
disk.get_disk_desc()],
queue=queue_name).get()
disk.ready = True
disk.save()
act.finish('SUCCESS')
return disk
class DiskActivity(TimeStampedModel):
activity_code = CharField(verbose_name=_('activity_code'), max_length=100)
task_uuid = CharField(verbose_name=_('task_uuid'), blank=True,
max_length=50, null=True, unique=True)
disk = ForeignKey(Disk, verbose_name=_('disk'),
related_name='activity_log')
user = ForeignKey(User, verbose_name=_('user'), blank=True, null=True)
started = DateTimeField(verbose_name=_('started'), blank=True, null=True)
finished = DateTimeField(verbose_name=_('finished'), blank=True, null=True)
result = TextField(verbose_name=_('result'), blank=True, null=True)
state = CharField(verbose_name=_('state'), default='PENDING',
max_length=50)
def update_state(self, new_state):
self.state = new_state
self.save()
def finish(self, result=None): @contextmanager
if not self.finished: def disk_activity(code_suffix, instance, task_uuid=None, user=None):
self.finished = timezone.now() act = DiskActivity.create(code_suffix, instance, task_uuid, user)
self.result = result activitycontextimpl(act)
self.state = 'COMPLETED'
self.save()
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'InstanceActivity.state'
db.delete_column(u'vm_instanceactivity', 'state')
# Adding field 'InstanceActivity.parent'
db.add_column(u'vm_instanceactivity', 'parent',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vm.InstanceActivity'], null=True, blank=True),
keep_default=False)
# Deleting field 'NodeActivity.status'
db.delete_column(u'vm_nodeactivity', 'status')
# Adding field 'NodeActivity.parent'
db.add_column(u'vm_nodeactivity', 'parent',
self.gf('django.db.models.fields.related.ForeignKey')(to=orm['vm.NodeActivity'], null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Adding field 'InstanceActivity.state'
db.add_column(u'vm_instanceactivity', 'state',
self.gf('django.db.models.fields.CharField')(default='PENDING', max_length=50),
keep_default=False)
# Deleting field 'InstanceActivity.parent'
db.delete_column(u'vm_instanceactivity', 'parent_id')
# Adding field 'NodeActivity.status'
db.add_column(u'vm_nodeactivity', 'status',
self.gf('django.db.models.fields.CharField')(default='PENDING', max_length=50),
keep_default=False)
# Deleting field 'NodeActivity.parent'
db.delete_column(u'vm_nodeactivity', 'parent_id')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'firewall.domain': {
'Meta': {'object_name': 'Domain'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'ttl': ('django.db.models.fields.IntegerField', [], {'default': '600'})
},
u'firewall.group': {
'Meta': {'object_name': 'Group'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'firewall.host': {
'Meta': {'object_name': 'Host'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Group']", 'null': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ipv4': ('firewall.fields.IPAddressField', [], {'unique': 'True', 'max_length': '100'}),
'ipv6': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'mac': ('firewall.fields.MACAddressField', [], {'unique': 'True', 'max_length': '17'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'pub_ipv4': ('firewall.fields.IPAddressField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'reverse': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
'shared_ip': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'firewall.vlan': {
'Meta': {'object_name': 'Vlan'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'dhcp_pool': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Domain']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'interface': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '20'}),
'network4': ('firewall.fields.IPNetworkField', [], {'max_length': '100'}),
'network6': ('firewall.fields.IPNetworkField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'reverse_domain': ('django.db.models.fields.TextField', [], {'default': "'%(d)d.%(c)d.%(b)d.%(a)d.in-addr.arpa'"}),
'snat_ip': ('django.db.models.fields.GenericIPAddressField', [], {'max_length': '39', 'null': 'True', 'blank': 'True'}),
'snat_to': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['firewall.Vlan']", 'null': 'True', 'blank': 'True'}),
'vid': ('django.db.models.fields.IntegerField', [], {'unique': 'True'})
},
u'storage.datastore': {
'Meta': {'ordering': "['name']", 'object_name': 'DataStore'},
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'})
},
u'storage.disk': {
'Meta': {'ordering': "['name']", 'object_name': 'Disk'},
'base': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'derivatives'", 'null': 'True', 'to': u"orm['storage.Disk']"}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'datastore': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['storage.DataStore']"}),
'destroyed': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'dev_num': ('django.db.models.fields.CharField', [], {'default': "'a'", 'max_length': '1'}),
'filename': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'size': ('sizefield.models.FileSizeField', [], {}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '10'})
},
u'vm.instance': {
'Meta': {'ordering': "['pk']", 'object_name': 'Instance'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'active_since': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'destoryed': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'instance_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'instance_set'", 'null': 'True', 'to': u"orm['vm.Node']"}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'pw': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'default': "'NOSTATE'", 'max_length': '20'}),
'template': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'instance_set'", 'null': 'True', 'to': u"orm['vm.InstanceTemplate']"}),
'time_of_delete': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'time_of_suspend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
'vnc_port': ('django.db.models.fields.IntegerField', [], {})
},
u'vm.instanceactivity': {
'Meta': {'object_name': 'InstanceActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log'", 'to': u"orm['vm.Instance']"}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.InstanceActivity']", 'null': 'True', 'blank': 'True'}),
'result': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
u'vm.instancetemplate': {
'Meta': {'ordering': "['name']", 'object_name': 'InstanceTemplate'},
'access_method': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'disks': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'template_set'", 'symmetrical': 'False', 'to': u"orm['storage.Disk']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lease': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'template_set'", 'to': u"orm['vm.Lease']"}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.InstanceTemplate']", 'null': 'True', 'blank': 'True'}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'default': "'NEW'", 'max_length': '10'}),
'system': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'vm.interface': {
'Meta': {'object_name': 'Interface'},
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interface_set'", 'to': u"orm['vm.Instance']"}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'vm_interface'", 'to': u"orm['firewall.Vlan']"})
},
u'vm.interfacetemplate': {
'Meta': {'object_name': 'InterfaceTemplate'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'managed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interface_set'", 'to': u"orm['vm.InstanceTemplate']"}),
'vlan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Vlan']"})
},
u'vm.lease': {
'Meta': {'ordering': "['name']", 'object_name': 'Lease'},
'delete_interval_seconds': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}),
'suspend_interval_seconds': ('django.db.models.fields.IntegerField', [], {})
},
u'vm.namedbaseresourceconfig': {
'Meta': {'object_name': 'NamedBaseResourceConfig'},
'arch': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'boot_menu': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'max_ram_size': ('django.db.models.fields.IntegerField', [], {}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {}),
'raw_data': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
u'vm.node': {
'Meta': {'object_name': 'Node'},
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['firewall.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'}),
'num_cores': ('django.db.models.fields.IntegerField', [], {}),
'priority': ('django.db.models.fields.IntegerField', [], {}),
'ram_size': ('django.db.models.fields.IntegerField', [], {})
},
u'vm.nodeactivity': {
'Meta': {'object_name': 'NodeActivity'},
'activity_code': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'created': ('model_utils.fields.AutoCreatedField', [], {'default': 'datetime.datetime.now'}),
'finished': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('model_utils.fields.AutoLastModifiedField', [], {'default': 'datetime.datetime.now'}),
'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_log'", 'to': u"orm['vm.Node']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['vm.NodeActivity']", 'null': 'True', 'blank': 'True'}),
'result': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'task_uuid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'unique': 'True', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['vm']
\ No newline at end of file
from contextlib import contextmanager
from datetime import timedelta from datetime import timedelta
from importlib import import_module from importlib import import_module
import logging import logging
...@@ -13,6 +14,7 @@ from model_utils.models import TimeStampedModel ...@@ -13,6 +14,7 @@ from model_utils.models import TimeStampedModel
from netaddr import EUI, mac_unix from netaddr import EUI, mac_unix
from .tasks import local_tasks, vm_tasks, net_tasks from .tasks import local_tasks, vm_tasks, net_tasks
from common.models import ActivityModel, activitycontextimpl
from firewall.models import Vlan, Host from firewall.models import Vlan, Host
from storage.models import Disk from storage.models import Disk
...@@ -99,38 +101,37 @@ class Node(TimeStampedModel): ...@@ -99,38 +101,37 @@ class Node(TimeStampedModel):
return self.name return self.name
class NodeActivity(TimeStampedModel): class NodeActivity(ActivityModel):
activity_code = CharField(verbose_name=_('activity code'), node = ForeignKey(Node, related_name='activity_log',
max_length=100) # TODO help_text=_('Node this activity works on.'),
task_uuid = CharField(verbose_name=_('task_uuid'), blank=True, verbose_name=_('node'))
max_length=50, null=True, unique=True, help_text=_(
'Celery task unique identifier.')) @classmethod
node = ForeignKey(Node, verbose_name=_('node'), def create(cls, code_suffix, node, task_uuid=None, user=None):
related_name='activity_log', act = cls(activity_code='vm.Node.' + code_suffix,
help_text=_('Node this activity works on.')) node=node, parent=None, started=timezone.now(),
user = ForeignKey(User, verbose_name=_('user'), blank=True, null=True, task_uuid=task_uuid, user=user)
help_text=_('The person who started this activity.')) act.save()
started = DateTimeField(verbose_name=_('started at'), return act
blank=True, null=True,
help_text=_('Time of activity initiation.')) def create_sub(self, code_suffix, task_uuid=None):
finished = DateTimeField(verbose_name=_('finished at'), act = NodeActivity(
blank=True, null=True, activity_code=self.activity_code + '.' + code_suffix,
help_text=_('Time of activity finalization.')) node=self.node, parent=self, started=timezone.now(),
result = TextField(verbose_name=_('result'), blank=True, null=True, task_uuid=task_uuid, user=self.user)
help_text=_('Human readable result of activity.')) act.save()
status = CharField(verbose_name=_('status'), default='PENDING', return act
max_length=50, help_text=_('Actual state of activity'))
@contextmanager
def update_state(self, new_state): def sub_activity(self, code_suffix, task_uuid=None):
self.state = new_state act = self.create_sub(code_suffix, task_uuid)
self.save() activitycontextimpl(act)
def finish(self, result=None):
if not self.finished: @contextmanager
self.finished = timezone.now() def node_activity(code_suffix, node, task_uuid=None, user=None):
self.result = result act = InstanceActivity.create(code_suffix, node, task_uuid, user)
self.state = 'COMPLETED' activitycontextimpl(act)
self.save()
class Lease(Model): class Lease(Model):
...@@ -533,40 +534,34 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -533,40 +534,34 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
asynchronously. asynchronously.
:type task_uuid: str :type task_uuid: str
""" """
act = InstanceActivity(activity_code='vm.Instance.deploy') with instance_activity(code_suffix='deploy', instance=self,
act.instance = self task_uuid=task_uuid, user=user) as act:
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
# Schedule # Schedule
self.node = scheduler.get_node(self, Node.objects.all()) self.node = scheduler.get_node(self, Node.objects.all())
self.save() self.save()
# Create virtual images
act.update_state('DEPLOYING DISKS')
for disk in self.disks.all():
disk.deploy()
queue_name = self.get_remote_queue_name('vm') # Deploy virtual images
with act.sub_activity('deploying_disks'):
for disk in self.disks.all():
disk.deploy()
# Deploy VM on remote machine queue_name = self.get_remote_queue_name('vm')
act.update_state('DEPLOYING VM')
vm_tasks.create.apply_async(args=[self.get_vm_desc()],
queue=queue_name).get()
# Estabilish network connection (vmdriver) # Deploy VM on remote machine
act.update_state('DEPLOYING NET') with act.sub_activity('deploying_vm'):
for net in self.interface_set.all(): vm_tasks.create.apply_async(args=[self.get_vm_desc()],
net.deploy() queue=queue_name).get()
# Resume vm # Estabilish network connection (vmdriver)
act.update_state('BOOTING') with act.sub_activity('deploying_net'):
vm_tasks.resume.apply_async(args=[self.vm_name], for net in self.interface_set.all():
queue=queue_name).get() net.deploy()
act.finish(result='SUCCESS') # Resume vm
with act.sub_activity('booting'):
vm_tasks.resume.apply_async(args=[self.vm_name],
queue=queue_name).get()
def deploy_async(self, user=None): def deploy_async(self, user=None):
"""Execute deploy asynchronously. """Execute deploy asynchronously.
...@@ -587,32 +582,27 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -587,32 +582,27 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
asynchronously. asynchronously.
:type task_uuid: str :type task_uuid: str
""" """
act = InstanceActivity(activity_code='vm.Instance.destroy') with instance_activity(code_suffix='destroy', instance=self,
act.instance = self task_uuid=task_uuid, user=user) as act:
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
# Destroy networks # Destroy networks
act.update_state('DESTROYING NET') with act.sub_activity('destroying_net'):
for net in self.interface_set.all(): for net in self.interface_set.all():
net.destroy() net.destroy()
# Destroy virtual machine # Destroy virtual machine
act.update_state('DESTROYING VM') with act.sub_activity('destroying_vm'):
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.destroy.apply_async(args=[self.vm_name], vm_tasks.destroy.apply_async(args=[self.vm_name],
queue=queue_name).get() queue=queue_name).get()
# Destroy disks # Destroy disks
act.update_state('DESTROYING DISKS') with act.sub_activity('destroying_disks'):
for disk in self.disks.all(): for disk in self.disks.all():
disk.destroy() disk.destroy()
self.destoryed = timezone.now() self.destoryed = timezone.now()
self.save() self.save()
act.finish(result="SUCCESS")
def destroy_async(self, user=None): def destroy_async(self, user=None):
"""Execute destroy asynchronously. """Execute destroy asynchronously.
...@@ -623,18 +613,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -623,18 +613,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
def sleep(self, user=None, task_uuid=None): def sleep(self, user=None, task_uuid=None):
"""Suspend virtual machine with memory dump. """Suspend virtual machine with memory dump.
""" """
act = InstanceActivity(activity_code='vm.Instance.sleep') with instance_activity(code_suffix='sleep', instance=self,
act.instance = self task_uuid=task_uuid, user=user):
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.sleep.apply_async(args=[self.vm_name, self.mem_dump], vm_tasks.sleep.apply_async(args=[self.vm_name, self.mem_dump],
queue=queue_name).get() queue=queue_name).get()
act.finish(result='SUCCESS')
def sleep_async(self, user=None): def sleep_async(self, user=None):
"""Execute sleep asynchronously. """Execute sleep asynchronously.
...@@ -643,18 +627,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -643,18 +627,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
queue="localhost.man") queue="localhost.man")
def wake_up(self, user=None, task_uuid=None): def wake_up(self, user=None, task_uuid=None):
act = InstanceActivity(activity_code='vm.Instance.wake_up') with instance_activity(code_suffix='wake_up', instance=self,
act.instance = self task_uuid=task_uuid, user=user):
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
queue_name = self.get_remote_queue_name('vm') queue_name = self.get_remote_queue_name('vm')
vm_tasks.resume.apply_async(args=[self.vm_name, self.dump_mem], vm_tasks.resume.apply_async(args=[self.vm_name, self.dump_mem],
queue=queue_name).get() queue=queue_name).get()
act.finish(result='SUCCESS')
def wake_up_async(self, user=None): def wake_up_async(self, user=None):
"""Execute wake_up asynchronously. """Execute wake_up asynchronously.
...@@ -665,18 +643,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -665,18 +643,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
def shutdown(self, user=None, task_uuid=None): def shutdown(self, user=None, task_uuid=None):
"""Shutdown virtual machine with ACPI signal. """Shutdown virtual machine with ACPI signal.
""" """
act = InstanceActivity(activity_code='vm.Instance.shutdown') with instance_activity(code_suffix='shutdown', instance=self,
act.instance = self task_uuid=task_uuid, user=user):
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
queue_name = self.get_remote_queue_name('vm')
vm_tasks.shutdown.apply_async(args=[self.vm_name],
queue=queue_name).get()
act.finish(result='SUCCESS') queue_name = self.get_remote_queue_name('vm')
vm_tasks.shutdown.apply_async(args=[self.vm_name],
queue=queue_name).get()
def shutdown_async(self, user=None): def shutdown_async(self, user=None):
"""Execute shutdown asynchronously. """Execute shutdown asynchronously.
...@@ -687,18 +659,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -687,18 +659,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
def reset(self, user=None, task_uuid=None): def reset(self, user=None, task_uuid=None):
"""Reset virtual machine (reset button) """Reset virtual machine (reset button)
""" """
act = InstanceActivity(activity_code='vm.Instance.reset') with instance_activity(code_suffix='reset', instance=self,
act.instance = self task_uuid=task_uuid, user=user):
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
queue_name = self.get_remote_queue_name('vm')
vm_tasks.restart.apply_async(args=[self.vm_name],
queue=queue_name).get()
act.finish(result='SUCCESS') queue_name = self.get_remote_queue_name('vm')
vm_tasks.restart.apply_async(args=[self.vm_name],
queue=queue_name).get()
def reset_async(self, user=None): def reset_async(self, user=None):
"""Execute reset asynchronously. """Execute reset asynchronously.
...@@ -709,18 +675,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -709,18 +675,12 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
def reboot(self, user=None, task_uuid=None): def reboot(self, user=None, task_uuid=None):
"""Reboot virtual machine with Ctrl+Alt+Del signal. """Reboot virtual machine with Ctrl+Alt+Del signal.
""" """
act = InstanceActivity(activity_code='vm.Instance.reboot') with instance_activity(code_suffix='reboot', instance=self,
act.instance = self task_uuid=task_uuid, user=user):
act.user = user
act.started = timezone.now()
act.task_uuid = task_uuid
act.save()
queue_name = self.get_remote_queue_name('vm')
vm_tasks.reboot.apply_async(args=[self.vm_name],
queue=queue_name).get()
act.finish(result='SUCCESS') queue_name = self.get_remote_queue_name('vm')
vm_tasks.reboot.apply_async(args=[self.vm_name],
queue=queue_name).get()
def reboot_async(self, user=None): def reboot_async(self, user=None):
"""Execute reboot asynchronously. """Execute reboot asynchronously.
...@@ -728,29 +688,38 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): ...@@ -728,29 +688,38 @@ class Instance(BaseResourceConfigModel, TimeStampedModel):
return local_tasks.reboot.apply_async(args=[self, user], return local_tasks.reboot.apply_async(args=[self, user],
queue="localhost.man") queue="localhost.man")
class InstanceActivity(TimeStampedModel):
activity_code = CharField(verbose_name=_('activity_code'), max_length=100)
task_uuid = CharField(verbose_name=_('task_uuid'), blank=True,
max_length=50, null=True, unique=True)
instance = ForeignKey(Instance, verbose_name=_('instance'),
related_name='activity_log')
user = ForeignKey(User, verbose_name=_('user'), blank=True, null=True)
started = DateTimeField(verbose_name=_('started'), blank=True, null=True)
finished = DateTimeField(verbose_name=_('finished'), blank=True, null=True)
result = TextField(verbose_name=_('result'), blank=True, null=True)
state = CharField(verbose_name=_('state'),
default='PENDING', max_length=50)
def update_state(self, new_state):
self.state = new_state
self.save()
def finish(self, result=None): class InstanceActivity(ActivityModel):
if not self.finished: instance = ForeignKey(Instance, related_name='activity_log',
self.finished = timezone.now() help_text=_('Instance this activity works on.'),
self.result = result verbose_name=_('instance'))
self.state = 'COMPLETED'
self.save() @classmethod
def create(cls, code_suffix, instance, task_uuid=None, user=None):
act = cls(activity_code='vm.Instance.' + code_suffix,
instance=instance, parent=None, started=timezone.now(),
task_uuid=task_uuid, user=user)
act.save()
return act
def create_sub(self, code_suffix, task_uuid=None):
act = InstanceActivity(
activity_code=self.activity_code + '.' + code_suffix,
instance=self.instance, 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)
activitycontextimpl(act)
@contextmanager
def instance_activity(code_suffix, instance, task_uuid=None, user=None):
act = InstanceActivity.create(code_suffix, instance, task_uuid, user)
activitycontextimpl(act)
class Interface(Model): class Interface(Model):
......
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