diff --git a/circle/vm/models.py b/circle/vm/models.py index 63b2407..ca9b709 100755 --- a/circle/vm/models.py +++ b/circle/vm/models.py @@ -5,13 +5,10 @@ import logging from . import tasks -# from django.conf.settings import CLOUD_URL from django.contrib.auth.models import User -# from django.core import signing from django.db import models from django.db.models.signals import pre_delete from django.dispatch import receiver -# from django.template.defaultfilters import escape from django.utils import timezone from django.utils.translation import ugettext_lazy as _ @@ -19,6 +16,7 @@ from model_utils.models import TimeStampedModel from firewall.models import Vlan, Host from storage.models import Disk +import manager logger = logging.getLogger(__name__) pwgen = User.objects.make_random_password @@ -33,7 +31,6 @@ ACCESS_METHODS = [(k, ap[0]) for k, ap in ACCESS_PROTOCOLS.iteritems()] class BaseResourceConfigModel(models.Model): - """Abstract base class for models with base resource configuration parameters. """ @@ -43,15 +40,14 @@ class BaseResourceConfigModel(models.Model): 'for balloning.')) arch = models.CharField(max_length=10, verbose_name=_('architecture')) priority = models.IntegerField(help_text=_('instance priority')) - boot_menu = models.BooleanField() - raw_data = models.TextField() + boot_menu = models.BooleanField(default=False) + raw_data = models.TextField(blank=True, null=True) class Meta: abstract = True class NamedBaseResourceConfig(BaseResourceConfigModel, TimeStampedModel): - """Pre-created, named base resource configurations. """ name = models.CharField(max_length=50, unique=True, @@ -62,6 +58,8 @@ class NamedBaseResourceConfig(BaseResourceConfigModel, TimeStampedModel): class Node(TimeStampedModel): + """A VM host machine. + """ name = models.CharField(max_length=50, unique=True, verbose_name=_('name')) num_cores = models.IntegerField(help_text=_('Number of CPU cores.')) @@ -82,8 +80,18 @@ class Node(TimeStampedModel): pass # TODO implement check -class Lease(models.Model): +class NodeActivity(TimeStampedModel): + activity_code = models.CharField(max_length=100) + task_uuid = models.CharField(max_length=50, unique=True) + node = models.ForeignKey(Node, related_name='activity_log') + user = models.ForeignKey(User, blank=True, null=True) + started = models.DateTimeField(blank=True, null=True) + finished = models.DateTimeField(blank=True, null=True) + result = models.TextField(blank=True, null=True) + status = models.CharField(default='PENDING', max_length=50) + +class Lease(models.Model): """Lease times for VM instances. Specifies a time duration until suspension and deletion of a VM @@ -94,6 +102,9 @@ class Lease(models.Model): suspend_interval_seconds = models.IntegerField() delete_interval_seconds = models.IntegerField() + class Meta: + ordering = ['name', ] + @property def suspend_interval(self): return timedelta(seconds=self.suspend_interval_seconds) @@ -112,7 +123,6 @@ class Lease(models.Model): class InstanceTemplate(BaseResourceConfigModel, TimeStampedModel): - """Virtual machine template. Every template has: @@ -126,7 +136,6 @@ class InstanceTemplate(BaseResourceConfigModel, TimeStampedModel): * set of interfaces * lease times (suspension & deletion) * time of creation and last modification - * ownership information """ STATES = [('NEW', _('new')), # template has just been created ('SAVING', _('saving')), # changes are being saved @@ -169,13 +178,12 @@ class InstanceTemplate(BaseResourceConfigModel, TimeStampedModel): """Get the type of the template's operating system. """ if self.access_method == 'rdp': - return "win" + return 'win' else: - return "linux" + return 'linux' class InterfaceTemplate(models.Model): - """Network interface template for an instance template. If the interface is managed, a host will be created for it. @@ -192,7 +200,6 @@ class InterfaceTemplate(models.Model): class Instance(BaseResourceConfigModel, TimeStampedModel): - """Virtual machine instance. Every instance has: @@ -204,10 +211,10 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): * lease times (suspension & deletion) * last boot timestamp * host node - * current state (libvirt domain state) and operation (Celery job UUID) + * current state (libvirt domain state) * time of creation and last modification * base resource configuration values - * ownership information + * owner and privilege information """ STATES = [('NOSTATE', _('nostate')), ('RUNNING', _('running')), @@ -236,8 +243,6 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): related_name='instance_set', verbose_name=_('host nose')) state = models.CharField(choices=STATES, default='NOSTATE', max_length=20) - operation = models.CharField(blank=True, max_length=100, null=True, - verbose_name=_('operation')) disks = models.ManyToManyField(Disk, related_name='instance_set', verbose_name=_('disks')) lease = models.ForeignKey(Lease) @@ -255,7 +260,7 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): return self.name @classmethod - def create_from_template(cls, template, **kwargs): + def create_from_template(cls, template, owner, **kwargs): """Create a new instance based on an InstanceTemplate. Can also specify parameters as keyword arguments which should override @@ -263,6 +268,7 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): """ # prepare parameters kwargs['template'] = template + kwargs['owner'] = owner kwargs.setdefault('name', template.name) kwargs.setdefault('description', template.description) kwargs.setdefault('pw', pwgen()) @@ -289,9 +295,9 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): return inst - # TODO is this obsolete? @models.permalink def get_absolute_url(self): + # TODO is this obsolete? return ('one.views.vm_show', None, {'iid': self.id}) @property @@ -310,17 +316,20 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): @property def ipv4(self): - """Primary IPv4 address of the instance.""" + """Primary IPv4 address of the instance. + """ return self.primary_host.ipv4 if self.primary_host else None @property def ipv6(self): - """Primary IPv6 address of the instance.""" + """Primary IPv6 address of the instance. + """ return self.primary_host.ipv6 if self.primary_host else None @property def mac(self): - """Primary MAC address of the instance.""" + """Primary MAC address of the instance. + """ return self.primary_host.mac if self.primary_host else None @property @@ -343,7 +352,7 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): def waiting(self): """Indicates whether the instance's waiting for an operation to finish. """ - return self.operation is not None + return self.activity_log.filter(finished__isnull=True).exists() def get_connect_port(self, use_ipv6=False): """Get public port number for default access method. @@ -373,9 +382,9 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): proto = self.access_method if proto == 'ssh': proto = 'sshterm' - return ("%(proto)s:cloud:%(pw)s:%(host)s:%(port)d" % - {"port": port, "proto": proto, "pw": self.pw, - "host": host}) + return ('%(proto)s:cloud:%(pw)s:%(host)s:%(port)d' % + {'port': port, 'proto': proto, 'pw': self.pw, + 'host': host}) except: return @@ -459,12 +468,23 @@ class Instance(BaseResourceConfigModel, TimeStampedModel): return True -@receiver(pre_delete, sender=Instance, dispatch_uid="delete_instance_pre") +@receiver(pre_delete, sender=Instance, dispatch_uid='delete_instance_pre') def delete_instance_pre(sender, instance, using, **kwargs): # TODO implement pass +class InstanceActivity(TimeStampedModel): + activity_code = models.CharField(max_length=100) + task_uuid = models.CharField(max_length=50, unique=True) + instance = models.ForeignKey(Instance, related_name='activity_log') + user = models.ForeignKey(User, blank=True, null=True) + started = models.DateTimeField(blank=True, null=True) + finished = models.DateTimeField(blank=True, null=True) + result = models.TextField(blank=True, null=True) + status = models.CharField(default='PENDING', max_length=50) + + class Interface(models.Model): """Network interface for an instance.