Commit 43e810d2 by Dudás Ádám

vm: refactor old code and add some new bits

* reorder classes
* make context creator method a module level function
* class method for creating an instance from a template
* fix primary host selection code
* use timezone.now() instead of datetime.now() to get timezone aware dates
* remove obsolete methods and implementations (leaving TODOs)
parent fd75f6f9
...@@ -46,6 +46,15 @@ class Disk(TimeStampedModel): ...@@ -46,6 +46,15 @@ class Disk(TimeStampedModel):
verbose_name = _('disk') verbose_name = _('disk')
verbose_name_plural = _('disks') verbose_name_plural = _('disks')
def get_exculsive(self):
"""Get an instance of the disk for exclusive usage.
It might mean copying the disk, creating a snapshot or creating a
symbolic link to a read-only image.
"""
# TODO implement (or call) logic
return self
def to_json(self): def to_json(self):
self.base_name = self.base.name if self.base else None self.base_name = self.base.name if self.base else None
self.dir = self.datastore.path self.dir = self.datastore.path
......
from datetime import timedelta
import logging
from django.conf.settings import CLOUD_URL
from django.contrib.auth.models import User from django.contrib.auth.models import User
# from django.core import signing
from django.db import models from django.db import models
from django.db.models.signals import pre_delete 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 _ from django.utils.translation import ugettext_lazy as _
from model_utils.models import TimeStampedModel from model_utils.models import TimeStampedModel
...@@ -8,15 +16,26 @@ from model_utils.models import TimeStampedModel ...@@ -8,15 +16,26 @@ from model_utils.models import TimeStampedModel
from firewall.models import Vlan, Host from firewall.models import Vlan, Host
from storage.models import Disk from storage.models import Disk
logger = logging.getLogger(__name__)
pwgen = User.objects.make_random_password
# TODO get this from config
ACCESS_PROTOCOLS = {
# format: id: (name, port, protocol)
'rdp': ('rdp', 3389, 'tcp'),
'nx': ('nx', 22, 'tcp'),
'ssh': ('ssh', 22, 'tcp'),
}
ACCESS_METHODS = [(k, ap[0]) for k, ap in ACCESS_PROTOCOLS.iteritems()]
class BaseResourceConfigModel(models.Model): class BaseResourceConfigModel(models.Model):
"""Abstract base class for models with base resource configuration """Abstract base class for models with base resource configuration
parameters. parameters.
""" """
CPU = models.IntegerField(help_text=_('CPU cores.')) num_cores = models.IntegerField(help_text=_('Number of CPU cores.'))
RAM = models.IntegerField(help_text=_('Mebibytes of memory.')) ram_size = models.IntegerField(help_text=_('Mebibytes of memory.'))
max_RAM = models.IntegerField(help_text=_('Upper memory size limit for ' max_ram_size = models.IntegerField(help_text=_('Upper memory size limit '
'balloning.')) 'for balloning.'))
arch = models.CharField(max_length=10, verbose_name=_('architecture')) arch = models.CharField(max_length=10, verbose_name=_('architecture'))
priority = models.IntegerField(help_text=_('instance priority')) priority = models.IntegerField(help_text=_('instance priority'))
...@@ -24,7 +43,7 @@ class BaseResourceConfigModel(models.Model): ...@@ -24,7 +43,7 @@ class BaseResourceConfigModel(models.Model):
abstract = True abstract = True
class NamedBaseResourceConfig(BaseResourceConfigModel): class NamedBaseResourceConfig(BaseResourceConfigModel, TimeStampedModel):
"""Pre-created, named base resource configurations. """Pre-created, named base resource configurations.
""" """
name = models.CharField(max_length=50, unique=True, name = models.CharField(max_length=50, unique=True,
...@@ -34,33 +53,56 @@ class NamedBaseResourceConfig(BaseResourceConfigModel): ...@@ -34,33 +53,56 @@ class NamedBaseResourceConfig(BaseResourceConfigModel):
return self.name return self.name
class Interface(models.Model): class Node(TimeStampedModel):
"""Network interface for an instance. name = models.CharField(max_length=50, unique=True,
""" verbose_name=_('name'))
vlan = models.ForeignKey(Vlan) num_cores = models.IntegerField(help_text=_('Number of CPU cores.'))
ram_size = models.IntegerField(help_text=_('Mebibytes of memory.'))
priority = models.IntegerField(help_text=_('node usage priority'))
host = models.ForeignKey(Host) host = models.ForeignKey(Host)
instance = models.ForeignKey('Instance') enabled = models.BooleanField(default=False,
help_text=_('Indicates whether the node can '
'be used for hosting.'))
class Meta:
permissions = ()
class InterfaceTemplate(models.Model): @property
"""Network interface template for an instance template. def online(self):
""" """Indicates whether the node is connected and functional.
vlan = models.ForeignKey(Vlan) """
managed = models.BooleanField() pass # TODO implement check
template = models.ForeignKey('Template')
class Node(models.Model): class Lease(models.Model):
name = models.CharField(max_length=50, unique=True, """Lease times for VM instances.
Specifies a time duration until suspension and deletion of a VM
instance.
"""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name')) verbose_name=_('name'))
CPU = models.IntegerField(help_text=_('CPU cores.')) suspend_interval_seconds = models.IntegerField()
RAM = models.IntegerField(help_text=_('Mebibytes of memory.')) delete_interval_seconds = models.IntegerField()
priority = models.IntegerField(help_text=_('node usage priority'))
host = models.ForeignKey(Host) @property
online = models.BooleanField(default=False) def suspend_interval(self):
return timedelta(seconds=self.suspend_interval_seconds)
@suspend_interval.setter
def suspend_interval(self, value):
self.suspend_interval_seconds = value.seconds
class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel): @property
def delete_interval(self):
return timedelta(seconds=self.delete_interval_seconds)
@delete_interval.setter
def delete_interval(self, value):
self.delete_interval_seconds = value.seconds
class InstanceTemplate(BaseResourceConfigModel, TimeStampedModel):
"""Virtual machine template. """Virtual machine template.
Every template has: Every template has:
...@@ -72,13 +114,13 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel): ...@@ -72,13 +114,13 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel):
* default values of base resource configuration * default values of base resource configuration
* list of attached images * list of attached images
* set of interfaces * set of interfaces
* lease times (suspension & deletion)
* time of creation and last modification * time of creation and last modification
* ownership information * ownership information
""" """
STATES = [('NEW', _('new')), # template has just been created STATES = [('NEW', _('new')), # template has just been created
('SAVING', _('saving')), # changes are being saved ('SAVING', _('saving')), # changes are being saved
('READY', _('ready'))] # template is ready for instantiation ('READY', _('ready'))] # template is ready for instantiation
ACCESS_METHODS = [('rdp', 'rdp'), ('nx', 'nx'), ('ssh', 'ssh'), ]
name = models.CharField(max_length=100, unique=True, name = models.CharField(max_length=100, unique=True,
verbose_name=_('name')) verbose_name=_('name'))
description = models.TextField(verbose_name=_('description'), description = models.TextField(verbose_name=_('description'),
...@@ -95,15 +137,14 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel): ...@@ -95,15 +137,14 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel):
state = models.CharField(max_length=10, choices=STATES, state = models.CharField(max_length=10, choices=STATES,
default='NEW') default='NEW')
disks = models.ManyToManyField(Disk, verbose_name=_('disks'), disks = models.ManyToManyField(Disk, verbose_name=_('disks'),
related_name='template_set') related_name='template_set')
# TODO review lease = models.ForeignKey(Lease, related_name='template_set')
owner = models.ForeignKey(User, verbose_name=_('owner'),
related_name='template_set')
class Meta: class Meta:
ordering = ['name', ]
permissions = ()
verbose_name = _('template') verbose_name = _('template')
verbose_name_plural = _('templates') verbose_name_plural = _('templates')
ordering = ['name', ]
def __unicode__(self): def __unicode__(self):
return self.name return self.name
...@@ -111,7 +152,7 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel): ...@@ -111,7 +152,7 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel):
def running_instances(self): def running_instances(self):
"""Returns the number of running instances of the template. """Returns the number of running instances of the template.
""" """
return self.instance_set.exclude(state='DONE').count() return self.instance_set.filter(state='RUNNING').count()
@property @property
def os_type(self): def os_type(self):
...@@ -123,7 +164,48 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel): ...@@ -123,7 +164,48 @@ class InstanceTemplate(TimeStampedModel, BaseResourceConfigModel):
return "linux" return "linux"
class Instance(TimeStampedModel, BaseResourceConfigModel): class InterfaceTemplate(models.Model):
"""Network interface template for an instance template.
If the interface is managed, a host will be created for it.
"""
vlan = models.ForeignKey(Vlan)
managed = models.BooleanField(default=True)
template = models.ForeignKey(InstanceTemplate,
related_name='interface_set')
class Meta:
permissions = ()
verbose_name = _('interface template')
verbose_name_plural = _('interface templates')
def create_context(pw, hostname, smb_password, ssh_private_key, owner, token,
extra):
"""Return XML context configuration with given parameters.
"""
return u'''
<SOURCE>web</SOURCE>
<HOSTNAME>%(hostname)s</HOSTNAME>
<NEPTUN>%(neptun)s</NEPTUN>
<USERPW>%(pw)s</USERPW>
<SMBPW>%(smbpw)s</SMBPW>
<SSHPRIV>%(sshkey)s</SSHPRIV>
<BOOTURL>%(booturl)s</BOOTURL>
<SERVER>store.cloud.ik.bme.hu</SERVER>
%(extra)s
''' % {
"pw": escape(pw),
"hostname": escape(hostname),
"smbpw": escape(smb_password),
"sshkey": escape(ssh_private_key),
"neptun": escape(owner),
"booturl": "%sb/%s/" % (CLOUD_URL, token),
"extra": extra
}
class Instance(BaseResourceConfigModel, TimeStampedModel):
"""Virtual machine instance. """Virtual machine instance.
Every instance has: Every instance has:
...@@ -132,6 +214,7 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -132,6 +214,7 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
* associated share * associated share
* a generated password for login authentication * a generated password for login authentication
* time of deletion and time of suspension * time of deletion and time of suspension
* lease times (suspension & deletion)
* last boot timestamp * last boot timestamp
* host node * host node
* current state (libvirt domain state) and operation (Celery job UUID) * current state (libvirt domain state) and operation (Celery job UUID)
...@@ -147,62 +230,96 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -147,62 +230,96 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
('SHUTOFF', _('shutoff')), ('SHUTOFF', _('shutoff')),
('CRASHED', _('crashed')), ('CRASHED', _('crashed')),
('PMSUSPENDED', _('pmsuspended'))] # libvirt domain states ('PMSUSPENDED', _('pmsuspended'))] # libvirt domain states
name = models.CharField(max_length=100, verbose_name=_('name'), name = models.CharField(blank=True, max_length=100, verbose_name=_('name'))
blank=True) description = models.TextField(blank=True, verbose_name=_('description'))
description = models.TextField(verbose_name=_('description'), template = models.ForeignKey(InstanceTemplate, blank=True, null=True,
blank=True)
template = models.ForeignKey('Template', verbose_name=_('template'),
related_name='instance_set', related_name='instance_set',
null=True, blank=True) verbose_name=_('template'))
pw = models.CharField(max_length=20, verbose_name=_('password'), pw = models.CharField(help_text=_('Original password of instance'),
help_text=_('Original password of instance')) max_length=20, verbose_name=_('password'))
time_of_suspend = models.DateTimeField(default=None, time_of_suspend = models.DateTimeField(blank=True, default=None, null=True,
verbose_name=_('time of suspend'), verbose_name=_('time of suspend'))
null=True, blank=True) time_of_delete = models.DateTimeField(blank=True, default=None, null=True,
time_of_delete = models.DateTimeField(default=None, verbose_name=_('time of delete'))
verbose_name=_('time of delete'), active_since = models.DateTimeField(blank=True, null=True,
null=True, blank=True) help_text=_('Time stamp of successful '
active_since = models.DateTimeField(null=True, blank=True, 'boot report.'),
verbose_name=_('active since'), verbose_name=_('active since'))
help_text=_('Time stamp of ' node = models.ForeignKey(Node, blank=True, null=True,
'successful boot ' related_name='instance_set',
'report.')) verbose_name=_('host nose'))
share = models.ForeignKey('Share', blank=True, null=True, state = models.CharField(choices=STATES, default='NOSTATE', max_length=20)
verbose_name=_('share'), operation = models.CharField(blank=True, max_length=100, null=True,
related_name='instance_set')
node = models.ForeignKey(Node, verbose_name=_('host nose'),
related_name='instance_set')
state = models.CharField(max_length=20, choices=STATES,
default='NOSTATE')
operation = models.CharField(max_length=100, null=True, blank=True,
verbose_name=_('operation')) verbose_name=_('operation'))
# TODO review fields below disks = models.ManyToManyField(Disk, related_name='instance_set',
owner = models.ForeignKey(User, verbose_name=_('owner'), verbose_name=_('disks'))
related_name='instance_set') lease = models.ForeignKey(Lease)
access_method = models.CharField(max_length=10, choices=ACCESS_METHODS,
verbose_name=_('access method'))
owner = models.ForeignKey(User)
class Meta: class Meta:
ordering = ['pk', ]
permissions = ()
verbose_name = _('instance') verbose_name = _('instance')
verbose_name_plural = _('instances') verbose_name_plural = _('instances')
ordering = ['pk', ]
def __unicode__(self): def __unicode__(self):
return self.name return self.name
@classmethod
def create_from_template(cls, template, **kwargs):
"""Create a new instance based on an InstanceTemplate.
Can also specify parameters as keyword arguments which should override
template settings.
"""
# prepare parameters
kwargs['template'] = template
kwargs.setdefault('name', template.name)
kwargs.setdefault('description', template.description)
kwargs.setdefault('pw', pwgen())
kwargs.setdefault('num_cores', template.num_cores)
kwargs.setdefault('ram_size', template.ram_size)
kwargs.setdefault('max_ram_size', template.max_ram_size)
kwargs.setdefault('arch', template.arch)
kwargs.setdefault('priority', template.priority)
kwargs.setdefault('lease', template.lease)
kwargs.setdefault('access_method', template.access_method)
# create instance and do additional setup
inst = cls(**kwargs)
for disk in template.disks:
inst.disks.add(disk.get_exculsive())
# save instance
inst.save()
# create related entities
for iftmpl in template.interface_set.all():
i = Interface.create_from_template(instance=inst, template=iftmpl)
if i.host:
i.host.enable_net()
port, proto = ACCESS_PROTOCOLS[i.access_method][1:3]
i.host.add_port(proto, i.get_port(), port)
return inst
# TODO is this obsolete?
@models.permalink @models.permalink
def get_absolute_url(self): def get_absolute_url(self):
return ('one.views.vm_show', None, {'iid': self.id}) return ('one.views.vm_show', None, {'iid': self.id})
@property @property
def primary_host(self): def primary_host(self):
if not hosts.exists(): interfaces = self.interface_set.select_related('host')
hosts = [i.host for i in interfaces if i.host]
if not hosts:
return None return None
hs = hosts.filter(ipv6__is_null=False) hs = [h for h in hosts if h.ipv6]
if hs.exists(): if hs:
return hs[0] return hs[0]
hs = hosts.filter(shared_ip=False) hs = [h for h in hosts if not h.shared_ip]
if hs.exists(): if hs:
return hs[0] return hs[0]
return hosts.all()[0] return hosts[0]
@property @property
def ipv4(self): def ipv4(self):
...@@ -221,13 +338,12 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -221,13 +338,12 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
@property @property
def uptime(self): def uptime(self):
"""Uptime of the instance.""" """Uptime of the instance.
from datetime import datetime, timedelta """
if self.active_since: if self.active_since:
return (datetime.now().replace(tzinfo=None) - return timezone.now() - self.active_since
self.active_since.replace(tzinfo=None))
else: else:
return timedelta() return timedelta() # zero
def get_age(self): def get_age(self):
"""Deprecated. Use uptime instead. """Deprecated. Use uptime instead.
...@@ -238,13 +354,14 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -238,13 +354,14 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
@property @property
def waiting(self): def waiting(self):
"""Indicates whether the instance's waiting for an operation to finish.
"""
return self.operation is not None return self.operation is not None
def get_port(self, use_ipv6=False): def get_connect_port(self, use_ipv6=False):
"""Get public port number for default access method.""" """Get public port number for default access method.
# TODO move PROTOS to config """
PROTOS = {"rdp": (3389,'tcp'), "nx": (22,'tcp'), "ssh": (22,'tcp')} port, proto = ACCESS_PROTOCOLS[self.access_method][1:3]
(port, proto) = PROTOS[self.template.access_method]
if self.primary_host: if self.primary_host:
endpoints = self.primary_host.get_public_endpoints(port, proto) endpoints = self.primary_host.get_public_endpoints(port, proto)
endpoint = endpoints['ipv6'] if use_ipv6 else endpoints['ipv4'] endpoint = endpoints['ipv6'] if use_ipv6 else endpoints['ipv4']
...@@ -253,183 +370,98 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -253,183 +370,98 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
return None return None
def get_connect_host(self, use_ipv6=False): def get_connect_host(self, use_ipv6=False):
"""Get public hostname.""" """Get public hostname.
if self.firewall_host is None: """
if not self.firewall_host:
return _('None') return _('None')
proto = 'ipv6' if use_ipv6 else 'ipv4' proto = 'ipv6' if use_ipv6 else 'ipv4'
return self.firewall_host.get_hostname(proto=proto) return self.firewall_host.get_hostname(proto=proto)
def get_connect_uri(self, use_ipv6=False): def get_connect_uri(self, use_ipv6=False):
"""Get access parameters in URI format.""" """Get access parameters in URI format.
"""
try: try:
proto = self.template.access_type port = self.get_connect_port(use_ipv6=use_ipv6)
host = self.get_connect_host(use_ipv6=use_ipv6)
proto = self.access_method
if proto == 'ssh': if proto == 'ssh':
proto = 'sshterm' proto = 'sshterm'
port = self.get_port(use_ipv6=use_ipv6)
host = self.get_connect_host(use_ipv6=use_ipv6)
pw = self.pw
return ("%(proto)s:cloud:%(pw)s:%(host)s:%(port)d" % return ("%(proto)s:cloud:%(pw)s:%(host)s:%(port)d" %
{"port": port, "proto": proto, "pw": pw, {"port": port, "proto": proto, "pw": self.pw,
"host": host}) "host": host})
except: except:
return return
@staticmethod def deploy(self, extra=""):
def _create_context(pw, hostname, smb_password, ssh_private_key, owner, # TODO implement
token, extra): pass
"""Return XML context configuration with given parameters.""" # """Submit a new instance to OpenNebula."""
ctx = u''' # inst = Instance(pw=pwgen(), template=template, owner=owner,
<SOURCE>web</SOURCE> # share=share, state='PENDING', waiting=True)
<HOSTNAME>%(hostname)s</HOSTNAME> # inst.save()
<NEPTUN>%(neptun)s</NEPTUN> # hostname = u"%d" % (inst.id, )
<USERPW>%(pw)s</USERPW> # token = signing.dumps(inst.id, salt='activate')
<SMBPW>%(smbpw)s</SMBPW> # try:
<SSHPRIV>%(sshkey)s</SSHPRIV> # details = owner.cloud_details
<BOOTURL>%(booturl)s</BOOTURL> # except:
<SERVER>store.cloud.ik.bme.hu</SERVER> # details = UserCloudDetails(user=owner)
%(extra)s # details.save()
''' % { #
"pw": escape(pw), # ctx = create_context(inst.pw, hostname, details.smb_password,
"hostname": escape(hostname), # details.ssh_private_key, owner.username,
"smbpw": escape(smb_password), # token, extra)
"sshkey": escape(ssh_private_key), # try:
"neptun": escape(owner), # from .tasks import CreateInstanceTask
"booturl": "%sb/%s/" % (CLOUD_URL, token), # x = CreateInstanceTask.delay(
"extra": extra # name=u"%s %d" % (owner.username, inst.id),
} # instance_type=template.instance_type.name,
return ctx # disk_id=int(template.disk.id),
# network_id=int(template.network.id),
def _create_host(self, hostname, occi_result): # ctx=ctx,
"""Create firewall host for recently submitted Instance.""" # )
host = Host( # res = x.get(timeout=10)
vlan=Vlan.objects.get(name=self.template.network.name), # res['one_id']
owner=self.owner, hostname=hostname, # except:
mac=occi_result['interfaces'][0]['mac'], # inst.delete()
ipv4=occi_result['interfaces'][0]['ip'], ipv6='auto', # raise Exception("Unable to create VM instance.")
) #
# inst.one_id = res['one_id']
if self.template.network.nat: # inst.ip = res['interfaces'][0]['ip']
host.pub_ipv4 = Vlan.objects.get( # inst.name = ("%(neptun)s %(template)s (%(id)d)" %
name=self.template.network.name).snat_ip # {'neptun': owner.username, 'template': template.name,
host.shared_ip = True # 'id': inst.one_id})
# inst.save()
try: #
host.save() # inst._create_host(hostname, res)
except: # return inst
for i in Host.objects.filter(ipv4=host.ipv4).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, self))
i.delete()
for i in Host.objects.filter(mac=host.mac).all():
logger.warning('Delete orphan fw host (%s) of %s.' % (i, self))
i.delete()
host.save()
host.enable_net()
port = {"rdp": 3389, "nx": 22, "ssh": 22}[self.template.access_type]
host.add_port("tcp", self.get_port(), port)
self.firewall_host = host
self.save()
@classmethod
def submit(cls, template, owner, extra="", share=None):
"""Submit a new instance to OpenNebula."""
inst = Instance(pw=pwgen(), template=template, owner=owner,
share=share, state='PENDING', waiting=True)
inst.save()
hostname = u"%d" % (inst.id, )
token = signing.dumps(inst.id, salt='activate')
try:
details = owner.cloud_details
except:
details = UserCloudDetails(user=owner)
details.save()
ctx = cls._create_context(inst.pw, hostname, details.smb_password,
details.ssh_private_key, owner.username,
token, extra)
try:
from .tasks import CreateInstanceTask
x = CreateInstanceTask.delay(
name=u"%s %d" % (owner.username, inst.id),
instance_type=template.instance_type.name,
disk_id=int(template.disk.id),
network_id=int(template.network.id),
ctx=ctx,
)
res = x.get(timeout=10)
res['one_id']
except:
inst.delete()
raise Exception("Unable to create VM instance.")
inst.one_id = res['one_id']
inst.ip = res['interfaces'][0]['ip']
inst.name = ("%(neptun)s %(template)s (%(id)d)" %
{'neptun': owner.username, 'template': template.name,
'id': inst.one_id})
inst.save()
inst._create_host(hostname, res)
return inst
def one_delete(self):
"""Delete host in OpenNebula."""
if self.template.state != "DONE":
self.check_if_is_save_as_done()
if self.one_id and self.state != 'DONE':
self.waiting = True
self.save()
from .tasks import DeleteInstanceTask
DeleteInstanceTask.delay(one_id=self.one_id)
self.firewall_host_delete()
def firewall_host_delete(self):
if self.firewall_host:
h = self.firewall_host
self.firewall_host = None
try:
self.save()
except:
pass
h.delete()
def _change_state(self, new_state):
"""Change host state in OpenNebula."""
from .tasks import ChangeInstanceStateTask
ChangeInstanceStateTask.delay(one_id=self.one_id, new_state=new_state)
self.waiting = True
self.save()
def stop(self): def stop(self):
self._change_state("STOPPED") # TODO implement
pass
def resume(self): def resume(self):
self._change_state("RESUME") # TODO implement
pass
def poweroff(self): def poweroff(self):
self._change_state("POWEROFF") # TODO implement
pass
def restart(self): def restart(self):
self._change_state("RESET") # TODO implement
self.waiting = False pass
self.save()
def renew(self, which='both'): def renew(self, which='both'):
"""Renew virtual machine instance leases.
"""
if which not in ['suspend', 'delete', 'both']:
raise ValueError('No such expiration type.')
if which in ['suspend', 'both']: if which in ['suspend', 'both']:
self.time_of_suspend = self.share_type['suspendx'] self.time_of_suspend = timezone.now() + self.lease.suspend_interval
if which in ['delete', 'both']: if which in ['delete', 'both']:
self.time_of_delete = self.share_type['deletex'] self.time_of_delete = timezone.now() + self.lease.delete_interval
if not (which in ['suspend', 'delete', 'both']):
raise ValueError('No such expiration type.')
self.save() self.save()
@property
def share_type(self):
if self.share:
return self.share.get_type()
else:
return Share.extend_type(DEFAULT_TYPE)
def save_as(self): def save_as(self):
"""Save image and shut down.""" """Save image and shut down."""
imgname = "template-%d-%d" % (self.template.id, self.id) imgname = "template-%d-%d" % (self.template.id, self.id)
...@@ -456,9 +488,25 @@ class Instance(TimeStampedModel, BaseResourceConfigModel): ...@@ -456,9 +488,25 @@ class Instance(TimeStampedModel, BaseResourceConfigModel):
return True return True
@receiver(pre_delete, sender=Instance, dispatch_uid="delete_instance_pre")
def delete_instance_pre(sender, instance, using, **kwargs): def delete_instance_pre(sender, instance, using, **kwargs):
if instance.state != 'DONE': # TODO implement
instance.one_delete() pass
pre_delete.connect(delete_instance_pre, sender=Instance,
dispatch_uid="delete_instance_pre") class Interface(models.Model):
"""Network interface for an instance.
"""
vlan = models.ForeignKey(Vlan)
host = models.ForeignKey(Host, blank=True, null=True)
instance = models.ForeignKey(Instance, related_name='interface_set')
@classmethod
def create_from_template(cls, instance, template):
"""Create a new interface for an instance based on an
InterfaceTemplate.
"""
host = Host(vlan=template.vlan) if template.managed else None
iface = cls(vlan=template.vlan, host=host, instance=instance)
iface.save()
return iface
from celery import task
@task
def create_instance_task(parameters):
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