Commit a9e4ec53 by Dudás Ádám

storage: refactoring model and tasks' interfaces

parent 6bb44292
# coding=utf-8 # coding=utf-8
import logging import logging
import jsonpickle from django.db import models
import json
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save, post_delete from django.db.models.signals import post_delete
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
from .tasks import StorageDriver from .tasks import StorageDriver
...@@ -14,9 +12,8 @@ logger = logging.getLogger(__name__) ...@@ -14,9 +12,8 @@ logger = logging.getLogger(__name__)
class DataStore(models.Model): class DataStore(models.Model):
''' """Collection of virtual disks.
"""
'''
name = models.CharField(max_length=100, unique=True, name = models.CharField(max_length=100, unique=True,
verbose_name=_('name')) verbose_name=_('name'))
path = models.CharField(max_length=200, unique=True, path = models.CharField(max_length=200, unique=True,
...@@ -32,25 +29,41 @@ class DataStore(models.Model): ...@@ -32,25 +29,41 @@ class DataStore(models.Model):
class Disk(TimeStampedModel): class Disk(TimeStampedModel):
"""Virtual disks.""" """A virtual disk.
FORMATS = [('qcow2', 'qcow2'), ('raw', 'raw'), ('iso', 'iso')] """
TYPES = [('snapshot', 'snapshot'), ('normal', 'normal')] TYPES = [('qcow2-norm', 'qcow2 normal'), ('qcow2-snap', 'qcow2 snapshot'),
name = models.CharField(max_length=100, unique=True, ('iso', 'iso'), ('raw-ro', 'raw read-only'), ('raw-rw', 'raw')]
verbose_name=_('name')) name = models.CharField(max_length=100, verbose_name=_('name'))
datastore = models.ForeignKey('DataStore') filename = models.CharField(max_length=256, unique=True,
format = models.CharField(max_length=10, choices=FORMATS) verbose_name=_('filename'))
size = models.IntegerField() datastore = models.ForeignKey(DataStore)
type = models.CharField(max_length=10, choices=TYPES) type = models.CharField(max_length=10, choices=TYPES)
size = models.IntegerField()
base = models.ForeignKey('self', blank=True, null=True, base = models.ForeignKey('self', blank=True, null=True,
related_name='derivatives') related_name='derivatives')
ready = models.BooleanField(default=False) ready = models.BooleanField(default=False)
dev_num = models.CharField(max_length=1, verbose_name="device number") dev_num = models.CharField(default='a', max_length=1,
verbose_name="device number")
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _('disk') verbose_name = _('disk')
verbose_name_plural = _('disks') verbose_name_plural = _('disks')
@property
def path(self):
return self.datastore.path + '/' + self.filename
@property
def format(self):
return {
'qcow2-norm': 'qcow2',
'qcow2-snap': 'qcow2',
'iso': 'iso',
'raw-ro': 'raw',
'raw-rw': 'raw',
}[self.type]
def get_exclusive(self): def get_exclusive(self):
"""Get an instance of the disk for exclusive usage. """Get an instance of the disk for exclusive usage.
...@@ -70,75 +83,35 @@ class Disk(TimeStampedModel): ...@@ -70,75 +83,35 @@ class Disk(TimeStampedModel):
def get_vmdisk_desc(self): def get_vmdisk_desc(self):
return { return {
'source': self.datastore.path + '/' + self.name, 'source': self.path,
'driver_type': self.format, 'driver_type': self.format,
'driver_cache': 'default', 'driver_cache': 'default',
'target_device': self.device_type + self.dev_num 'target_device': self.device_type + self.dev_num
} }
def to_json(self):
self.base_name = self.base.name if self.base else None
self.dir = self.datastore.path
return jsonpickle.encode(self, unpicklable=True)
def __unicode__(self): def __unicode__(self):
return u"%s (#%d)" % (self.name, self.id) return u"%s (#%d)" % (self.name, self.id)
@classmethod def deploy(self):
def create_signal(cls, sender, instance, created, **kwargs): if self.ready:
if not instance.created: return
StorageDriver.create_disk.delay(instance.to_json()).get()
instance.created = True
instance.save()
@classmethod disk_desc = {
def delete_signal(cls, sender, instance, using, **kwargs): 'name': self.name,
StorageDriver.delete_disk.delay(instance.to_json()).get() 'dir': self.datastore.path,
'format': self.format,
'size': self.size,
'base_name': self.base.name if self.base else None,
'type': self.type
}
StorageDriver.create_disk.delay(disk_desc).get()
self.ready = True
self.save()
@classmethod @classmethod
def update_disk(cls, disk): def delete_signal(cls, sender, instance, using, **kwargs):
name = disk['name'] # TODO
modified = False # StorageDriver.delete_disk.delay(instance.to_json()).get()
try: pass
base = cls.objects.get(name=disk['base_name'])
except cls.DoesNotExist:
base = None
try:
d = cls.objects.get(name=name)
except Disk.DoesNotExist:
d = Disk(name=name,
created=True,
datastore=DataStore.objects.get(path=disk['dir']),
format=disk['format'],
type=disk['type'])
modified = True
if d.size != disk['size'] or d.base != base:
d.size = disk['size']
d.base = base
modified = True
if modified:
d.full_clean()
d.save()
@classmethod
def update_disks(cls, delete=True):
"""Get and register virtual disks from storage driver."""
try:
json_data = StorageDriver.list_disks.delay().get(timeout=10)
disks = json.loads(json_data)
except:
return
with transaction.commit_on_success():
l = []
for disk in disks:
print disk
cls.update_disk(disk)
l.append(disk['name'])
if delete:
cls.objects.exclude(name__in=l).delete()
post_save.connect(Disk.create_signal, sender=Disk)
post_delete.connect(Disk.delete_signal, sender=Disk) post_delete.connect(Disk.delete_signal, sender=Disk)
...@@ -8,17 +8,19 @@ logger = logging.getLogger(__name__) ...@@ -8,17 +8,19 @@ logger = logging.getLogger(__name__)
class StorageDriver: class StorageDriver:
@celery.task(filter=task_method, name='storagedriver.list_disks') @celery.task(filter=task_method, name='storagedriver.list_disks')
def list_disks(): def list_disks(dir):
pass pass
@celery.task(filter=task_method, name='storagedriver.create_disk') @celery.task(filter=task_method, name='storagedriver.create_disk')
def create_disk(json_data): def create_disk(disk_desc):
pass pass
@celery.task(filter=task_method, name='storagedriver.delete_disk') @celery.task(filter=task_method, name='storagedriver.delete_disk')
def delete_disk(json_data): def delete_disk(json_data):
# TODO review
pass pass
@celery.task(filter=task_method, name='storagedriver.get_disk') @celery.task(filter=task_method, name='storagedriver.get_disk')
def get_disk(json_data): def get_disk(json_data):
# TODO review
pass pass
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