Commit 23646e98 by Dudás Ádám

vm: moving code from one to vm, creating new model elements

parent 6ad839e2
......@@ -67,11 +67,12 @@ class Rule(models.Model):
help_text=_("The type of entity the rule "
"belongs to."))
nat = models.BooleanField(default=False, verbose_name=_("NAT"),
help_text=_("If network address translation "
"shoud be done."))
help_text=_("Indicates that network address "
"translation should be done."))
nat_dport = models.IntegerField(blank=True, null=True,
help_text=_(
"Rewrite destination port number to."),
help_text=_("Rewrite destination port "
"number to this if NAT is "
"needed."),
validators=[MinValueValidator(1),
MaxValueValidator(65535)])
created_at = models.DateTimeField(
......@@ -373,6 +374,14 @@ class Host(models.Model):
def __unicode__(self):
return self.hostname
@property
def incoming_rules(self):
return self.rules.filter(direction='1')
@property
def outgoing_rules(self):
return self.rules.filter(direction='0')
def save(self, *args, **kwargs):
id = self.id
if not self.id and self.ipv6 == "auto":
......@@ -549,6 +558,30 @@ class Host(models.Model):
"""
return self.hostname + u'.' + unicode(self.vlan.domain)
def get_public_endpoints(self, port, protocol='tcp'):
"""Get public IPv4 and IPv6 endpoints for local port.
Optionally the required protocol (e.g. TCP, UDP) can be specified.
"""
# IPv4
public_ipv4 = self.pub_ipv4 if self.pub_ipv4 else self.ipv4
# try get matching port(s) without NAT
ports = self.incoming_rules.filter(accept=True, dport=port,
nat=False, proto=protocol)
if ports.exists():
public_port = ports[0].dport
else:
# try get matching port(s) with NAT
ports = self.incoming_rules.filter(accept=True, nat_dport=port,
nat=True, proto=protocol)
public_port = ports[0].dport if ports.exists() else None
endpoints['ipv4'] = ((public_ipv4, public_port) if public_port else
None)
# IPv6
blocked = self.incoming_rules.filter(accept=False, dport=port,
proto=protocol).exists()
endpoints['ipv6'] = (self.ipv6, port) if not blocked else None
return endpoints
class Firewall(models.Model):
name = models.CharField(max_length=20, unique=True)
......
......@@ -17,6 +17,7 @@ from django.utils.translation import ugettext_lazy as _
from firewall.models import Host, Vlan
from store.api import StoreApi
from vm.models import Template
from .util import keygen
import django.conf
......@@ -183,8 +184,6 @@ class SshKey(models.Model):
return u"%s (%s)" % (keycomment, self.user)
TEMPLATE_STATES = (("INIT", _('init')), ("PREP", _('perparing')),
("SAVE", _('saving')), ("READY", _('ready')))
TYPES = {"LAB": {"verbose_name": _('lab'), "id": "LAB",
"suspend": td(hours=5), "delete": td(days=15),
"help_text": _('For lab or homework with short lifetime.')},
......@@ -264,485 +263,3 @@ class Share(models.Model):
def get_used_quota(self):
return self.template.get_credits_per_instance() * self.instance_limit
class Disk(models.Model):
"""Virtual disks automatically synchronized with OpenNebula."""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
class Meta:
ordering = ['name']
verbose_name = _('disk')
verbose_name_plural = _('disks')
def __unicode__(self):
return u"%s (#%d)" % (self.name, self.id)
@staticmethod
def update(delete=True):
"""Get and register virtual disks from OpenNebula."""
try:
from .tasks import UpdateDiskTask
x = UpdateDiskTask.delay().get(timeout=10)
x[0]
except:
return
with transaction.commit_on_success():
l = []
for d in x:
id = int(d['id'])
name = d['name']
try:
d, created = Disk.objects.get_or_create(id=id)
d.name = name
d.save()
except:
pass
l.append(id)
if delete:
Disk.objects.exclude(id__in=l).delete()
class Network(models.Model):
"""Virtual networks automatically synchronized with OpenNebula."""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
nat = models.BooleanField(verbose_name=_('NAT'),
help_text=_('If network address translation is '
'done.'))
public = models.BooleanField(verbose_name=_('public'),
help_text=_('If internet gateway is '
'available.'))
class Meta:
ordering = ['name']
verbose_name = _('network')
verbose_name_plural = _('networks')
def __unicode__(self):
return self.name
@staticmethod
def update():
"""Get and register virtual networks from OpenNebula."""
try:
from .tasks import UpdateNetworkTask
x = UpdateNetworkTask.delay().get(timeout=10)
x[0]
except:
return
with transaction.commit_on_success():
l = []
for n in x:
id = int(n['id'])
name = n['name']
try:
n, created = Network.objects.get_or_create(id=id)
n.name = name
n.save()
except:
pass
l.append(id)
Network.objects.exclude(id__in=l).delete()
def get_vlan(self):
return Vlan.objects.get(vid=self.id)
class InstanceType(models.Model):
"""Instance types in OCCI configuration (manually synchronized)."""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
CPU = models.IntegerField(help_text=_('CPU cores.'))
RAM = models.IntegerField(help_text=_('Mebibytes of memory.'))
credit = models.IntegerField(verbose_name=_('credits'),
help_text=_('Price of instance.'))
class Meta:
ordering = ['credit']
verbose_name = _('instance type')
verbose_name_plural = _('instance types')
def __unicode__(self):
return u"%s" % self.name
TEMPLATE_STATES = (('NEW', _('new')), ('SAVING', _('saving')),
('READY', _('ready')), )
class Template(models.Model):
"""Virtual machine template specifying OS, disk, type and network."""
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
access_type = models.CharField(max_length=10,
choices=[('rdp', 'rdp'), (
'nx', 'nx'), ('ssh', 'ssh')],
verbose_name=_('access method'))
disk = models.ForeignKey(Disk, verbose_name=_(
'disk'), related_name='template_set')
instance_type = models.ForeignKey(
InstanceType, related_name='template_set',
verbose_name=_('instance type'))
network = models.ForeignKey(Network, verbose_name=_('network'),
related_name='template_set')
owner = models.ForeignKey(User, verbose_name=_('owner'),
related_name='template_set')
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
state = models.CharField(max_length=10, choices=TEMPLATE_STATES,
default='NEW')
public = models.BooleanField(verbose_name=_('public'), default=False,
help_text=_('If other users can derive '
'templates of this one.'))
description = models.TextField(verbose_name=_('description'), blank=True)
system = models.TextField(verbose_name=_('operating system'), blank=True,
help_text=(_('Name of operating system in '
'format like "%s".') %
"Ubuntu 12.04 LTS Desktop amd64"))
class Meta:
verbose_name = _('template')
verbose_name_plural = _('templates')
ordering = ['name', ]
def __unicode__(self):
return self.name
def running_instances(self):
return self.instance_set.exclude(state='DONE').count()
@property
def os_type(self):
if self.access_type == 'rdp':
return "win"
else:
return "linux"
@transaction.autocommit
def safe_delete(self):
if not self.instance_set.exclude(state='DONE').exists():
self.delete()
return True
else:
logger.info("Could not delete template. Instances still running!")
return False
def get_credits_per_instance(self):
return self.instance_type.credit
def get_shares_for(self, user=None):
shares = Share.objects.all().filter(template=self)
if user:
return shares.filter(owner=user)
else:
return shares
def get_share_quota_usage_for(self, user=None, type=None):
if type is None:
c = self.get_credits_per_instance()
else:
c = type.credit
shares = self.get_shares_for(user)
usage = 0
for share in shares:
usage += share.instance_limit * c
return usage
class Instance(models.Model):
"""Virtual machine instance."""
name = models.CharField(max_length=100,
verbose_name=_('name'), blank=True)
ip = models.IPAddressField(blank=True, null=True,
verbose_name=_('IP address'))
template = models.ForeignKey(Template, verbose_name=_('template'),
related_name='instance_set')
owner = models.ForeignKey(User, verbose_name=_('owner'),
related_name='instance_set')
created_at = models.DateTimeField(auto_now_add=True,
verbose_name=_('created at'))
state = models.CharField(max_length=20,
choices=[('DEPLOYABLE', _('deployable')),
('PENDING', _('pending')),
('DONE', _('done')),
('ACTIVE', _('active')),
('UNKNOWN', _('unknown')),
('STOPPED', _('suspended')),
('FAILED', _('failed'))],
default='DEPLOYABLE')
active_since = models.DateTimeField(null=True, blank=True,
verbose_name=_('active since'),
help_text=_('Time stamp of successful '
'boot report.'))
firewall_host = models.ForeignKey(Host, blank=True, null=True,
verbose_name=_('host in firewall'),
related_name='instance_set')
pw = models.CharField(max_length=20, verbose_name=_('password'),
help_text=_('Original password of instance'))
one_id = models.IntegerField(unique=True, blank=True, null=True,
verbose_name=_('OpenNebula ID'))
share = models.ForeignKey('Share', blank=True, null=True,
verbose_name=_('share'),
related_name='instance_set')
time_of_suspend = models.DateTimeField(default=None,
verbose_name=_('time of suspend'),
null=True, blank=True)
time_of_delete = models.DateTimeField(default=None,
verbose_name=_('time of delete'),
null=True, blank=True)
waiting = models.BooleanField(default=False)
class Meta:
verbose_name = _('instance')
verbose_name_plural = _('instances')
ordering = ['pk', ]
def __unicode__(self):
return self.name
@models.permalink
def get_absolute_url(self):
return ('one.views.vm_show', None, {'iid': self.id})
def get_port(self, use_ipv6=False):
"""Get public port number for default access method."""
proto = self.template.access_type
if self.nat and not use_ipv6:
return ({"rdp": 23000, "nx": 22000, "ssh": 22000}[proto] +
int(self.ip.split('.')[2]) * 256 +
int(self.ip.split('.')[3]))
else:
return {"rdp": 3389, "nx": 22, "ssh": 22}[proto]
def get_connect_host(self, use_ipv6=False):
"""Get public hostname."""
if self.firewall_host is None:
return _('None')
proto = 'ipv6' if use_ipv6 else 'ipv4'
return self.firewall_host.get_hostname(proto=proto)
def get_connect_uri(self, use_ipv6=False):
"""Get access parameters in URI format."""
try:
proto = self.template.access_type
if proto == 'ssh':
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" %
{"port": port, "proto": proto, "pw": pw,
"host": host})
except:
return
@property
def nat(self):
if self.firewall_host is not None:
return self.firewall_host.shared_ip
elif self.template is not None:
return self.template.network.nat
else:
return False
def get_age(self):
"""Get age of VM in seconds."""
from datetime import datetime
age = 0
try:
age = (datetime.now().replace(tzinfo=None)
- self.active_since.replace(tzinfo=None)).seconds
except:
pass
return age
@staticmethod
def _create_context(pw, hostname, smb_password, ssh_private_key, owner,
token, extra):
"""Return XML context configuration with given parameters."""
ctx = 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
}
return ctx
def _create_host(self, hostname, occi_result):
"""Create firewall host for recently submitted Instance."""
host = Host(
vlan=Vlan.objects.get(name=self.template.network.name),
owner=self.owner, hostname=hostname,
mac=occi_result['interfaces'][0]['mac'],
ipv4=occi_result['interfaces'][0]['ip'], ipv6='auto',
)
if self.template.network.nat:
host.pub_ipv4 = Vlan.objects.get(
name=self.template.network.name).snat_ip
host.shared_ip = True
try:
host.save()
except:
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):
self._change_state("STOPPED")
def resume(self):
self._change_state("RESUME")
def poweroff(self):
self._change_state("POWEROFF")
def restart(self):
self._change_state("RESET")
self.waiting = False
self.save()
def renew(self, which='both'):
if which in ['suspend', 'both']:
self.time_of_suspend = self.share_type['suspendx']
if which in ['delete', 'both']:
self.time_of_delete = self.share_type['deletex']
if not (which in ['suspend', 'delete', 'both']):
raise ValueError('No such expiration type.')
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):
"""Save image and shut down."""
imgname = "template-%d-%d" % (self.template.id, self.id)
from .tasks import SaveAsTask
SaveAsTask.delay(one_id=self.one_id, new_img=imgname)
self._change_state("SHUTDOWN")
self.save()
t = self.template
t.state = 'SAVING'
t.save()
def check_if_is_save_as_done(self):
if self.state != 'DONE':
return False
Disk.update(delete=False)
imgname = "template-%d-%d" % (self.template.id, self.id)
disks = Disk.objects.filter(name=imgname)
if len(disks) != 1:
return False
self.template.disk_id = disks[0].id
self.template.state = 'READY'
self.template.save()
self.firewall_host_delete()
return True
def delete_instance_pre(sender, instance, using, **kwargs):
if instance.state != 'DONE':
instance.one_delete()
pre_delete.connect(delete_instance_pre, sender=Instance,
dispatch_uid="delete_instance_pre")
......@@ -6,6 +6,7 @@ import json
from django.db import models, transaction
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import post_save, post_delete
from model_utils.models import TimeStampedModel
from .tasks import StorageDriver
......@@ -27,20 +28,18 @@ class DataStore(models.Model):
return u'%s (%s)' % (self.name, self.path)
class Disk(models.Model):
"""Virtual disks automatically synchronized with OpenNebula."""
class Disk(models.Model, TimeStampedModel):
"""Virtual disks."""
FORMATS = [('qcow2', 'qcow2'), ('raw', 'raw'), ('iso', 'iso')]
TYPES = [('snapshot', 'snapshot'), ('normal', 'normal')]
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
datastore = models.ForeignKey('DataStore')
format = models.CharField(max_length=10,
choices=(('qcow2', 'qcow2'), ('raw', 'raw')))
format = models.CharField(max_length=10, choices=FORMAT)
size = models.IntegerField()
type = models.CharField(max_length=10,
choices=(('snapshot', 'snapshot'),
('normal', 'normal')))
type = models.CharField(max_length=10, choices=TYPES)
base = models.ForeignKey('Disk', related_name='snapshots',
null=True, blank=True)
original_parent = models.ForeignKey('Disk', null=True, blank=True)
created = models.BooleanField(default=False)
class Meta:
......@@ -97,7 +96,7 @@ class Disk(models.Model):
@classmethod
def update_disks(cls, delete=True):
"""Get and register virtual disks from OpenNebula."""
"""Get and register virtual disks from storage driver."""
try:
json_data = StorageDriver.list_disks.delay().get(timeout=10)
disks = json.loads(json_data)
......
from django.db import models
from model_utils.models import TimeStampedModel
from firewall.models import Vlan
from storage.models import Disk, Image
class BaseResourceConfigModel():
"""Abstract base class for models with base resource configuration
parameters.
"""
CPU = models.IntegerField(help_text=_('CPU cores.'))
RAM = models.IntegerField(help_text=_('Mebibytes of memory.'))
max_RAM = models.IntegerField(help_text=_('Upper memory size limit for '
'balloning.'))
arch = models.CharField(max_length=10, verbose_name=_('architecture'))
priority = models.IntegerField(help_text=_('instance priority'))
class Meta:
abstract = True
class NamedBaseResourceConfig(models.Model, BaseResourceConfigModel):
"""Pre-created, named base resource configurations.
"""
name = models.CharField(max_length=50, unique=True,
verbose_name=_('name'))
def __unicode__(self):
return self.name
class Interface(models.Model):
"""Network interface for an instance.
"""
vlan = models.ForeignKey(Vlan)
host = models.ForeignKey(Host)
instance = models.ForeignKey(Instance)
class InterfaceTemplate(models.Model):
"""Network interface template for an instance template.
"""
vlan = models.ForeignKey(Vlan)
managed = models.BooleanField()
template = models.ForeignKey(Template)
class Node(models.Model):
name = models.CharField(max_length=50, unique=True,
verbose_name=_('name'))
CPU = models.IntegerField(help_text=_('CPU cores.'))
RAM = models.IntegerField(help_text=_('Mebibytes of memory.'))
priority = models.IntegerField(help_text=_('node usage priority'))
host = models.ForeignKey(Host)
online = models.BooleanField(default=False)
class InstanceTemplate(models.Model, TimeStampedModel,
BaseResourceConfigModel):
"""Virtual machine template.
Every template has:
* a name and a description
* an optional parent template
* state of the template
* an OS name/description
* a method of access to the system
* default values of base resource configuration
* list of attached images
* set of interfaces
* time of creation and last modification
* ownership information
"""
STATES = [('NEW', _('new')), # template has just been created
('SAVING', _('saving')), # changes are being saved
('READY', _('ready'))] # template is ready for instantiation
ACCESS_METHODS = [('rdp', 'rdp'), ('nx', 'nx'), ('ssh', 'ssh'), ]
name = models.CharField(max_length=100, unique=True,
verbose_name=_('name'))
description = models.TextField(verbose_name=_('description'),
blank=True)
parent = models.ForeignKey('self', null=True, blank=True,
verbose_name=_('parent template'))
system = models.TextField(verbose_name=_('operating system'),
blank=True,
help_text=(_('Name of operating system in '
'format like "%s".') %
'Ubuntu 12.04 LTS Desktop amd64'))
access_method = models.CharField(max_length=10, choices=ACCESS_METHODS,
verbose_name=_('access method'))
state = models.CharField(max_length=10, choices=TEMPLATE_STATES,
default='NEW')
images = models.ManyToManyField(Image, verbose_name=_('images'),
related_name='template_set')
# TODO review
owner = models.ForeignKey(User, verbose_name=_('owner'),
related_name='template_set')
class Meta:
verbose_name = _('template')
verbose_name_plural = _('templates')
ordering = ['name', ]
def __unicode__(self):
return self.name
def running_instances(self):
"""Returns the number of running instances of the template.
"""
return self.instance_set.exclude(state='DONE').count()
@property
def os_type(self):
"""Get the type of the template's operating system.
"""
if self.access_method == 'rdp':
return "win"
else:
return "linux"
class Instance(models.Model, TimeStampedModel, BaseResourceConfigModel):
"""Virtual machine instance.
Every instance has:
* a name and a description
* an optional parent template
* associated share
* a generated password for login authentication
* time of deletion and time of suspension
* last boot timestamp
* host node
* current state (libvirt domain state) and operation (Celery job UUID)
* time of creation and last modification
* base resource configuration values
* ownership information
"""
STATES = [('NOSTATE', _('nostate')),
('RUNNING', _('running')),
('BLOCKED', _('blocked')),
('PAUSED', _('paused')),
('SHUTDOWN', _('shutdown')),
('SHUTOFF', _('shutoff')),
('CRASHED', _('crashed')),
('PMSUSPENDED', _('pmsuspended'))] # libvirt domain states
name = models.CharField(max_length=100, verbose_name=_('name'),
blank=True)
description = models.TextField(verbose_name=_('description'),
blank=True)
template = models.ForeignKey(Template, verbose_name=_('template'),
related_name='instance_set',
null=True, blank=True)
pw = models.CharField(max_length=20, verbose_name=_('password'),
help_text=_('Original password of instance'))
time_of_suspend = models.DateTimeField(default=None,
verbose_name=_('time of suspend'),
null=True, blank=True)
time_of_delete = models.DateTimeField(default=None,
verbose_name=_('time of delete'),
null=True, blank=True)
active_since = models.DateTimeField(null=True, blank=True,
verbose_name=_('active since'),
help_text=_('Time stamp of '
'successful boot '
'report.'))
share = models.ForeignKey('Share', blank=True, null=True,
verbose_name=_('share'),
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'))
# TODO review fields below
owner = models.ForeignKey(User, verbose_name=_('owner'),
related_name='instance_set')
class Meta:
verbose_name = _('instance')
verbose_name_plural = _('instances')
ordering = ['pk', ]
def __unicode__(self):
return self.name
@models.permalink
def get_absolute_url(self):
return ('one.views.vm_show', None, {'iid': self.id})
@property
def primary_host(self):
if not hosts.exists():
return None
hs = hosts.filter(ipv6__is_null=False)
if hs.exists():
return hs[0]
hs = hosts.filter(shared_ip=False)
if hs.exists():
return hs[0]
return hosts.all()[0]
@property
def ipv4(self):
"""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."""
return self.primary_host.ipv6 if self.primary_host else None
@property
def mac(self):
"""Primary MAC address of the instance."""
return self.primary_host.mac if self.primary_host else None
@property
def uptime(self):
"""Uptime of the instance."""
from datetime import datetime, timedelta
if self.active_since:
return (datetime.now().replace(tzinfo=None) -
self.active_since.replace(tzinfo=None))
else:
return timedelta()
def get_age(self):
"""Deprecated. Use uptime instead.
Get age of VM in seconds.
"""
return self.uptime.seconds
@property
def waiting(self):
return self.operation is not None
def get_port(self, use_ipv6=False):
"""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) = PROTOS[self.template.access_method]
if self.primary_host:
endpoints = self.primary_host.get_public_endpoints(port, proto)
endpoint = endpoints['ipv6'] if use_ipv6 else endpoints['ipv4']
return endpoint[1] if endpoint else None
else:
return None
def get_connect_host(self, use_ipv6=False):
"""Get public hostname."""
if self.firewall_host is None:
return _('None')
proto = 'ipv6' if use_ipv6 else 'ipv4'
return self.firewall_host.get_hostname(proto=proto)
def get_connect_uri(self, use_ipv6=False):
"""Get access parameters in URI format."""
try:
proto = self.template.access_type
if proto == 'ssh':
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" %
{"port": port, "proto": proto, "pw": pw,
"host": host})
except:
return
@staticmethod
def _create_context(pw, hostname, smb_password, ssh_private_key, owner,
token, extra):
"""Return XML context configuration with given parameters."""
ctx = 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
}
return ctx
def _create_host(self, hostname, occi_result):
"""Create firewall host for recently submitted Instance."""
host = Host(
vlan=Vlan.objects.get(name=self.template.network.name),
owner=self.owner, hostname=hostname,
mac=occi_result['interfaces'][0]['mac'],
ipv4=occi_result['interfaces'][0]['ip'], ipv6='auto',
)
if self.template.network.nat:
host.pub_ipv4 = Vlan.objects.get(
name=self.template.network.name).snat_ip
host.shared_ip = True
try:
host.save()
except:
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):
self._change_state("STOPPED")
def resume(self):
self._change_state("RESUME")
def poweroff(self):
self._change_state("POWEROFF")
def restart(self):
self._change_state("RESET")
self.waiting = False
self.save()
def renew(self, which='both'):
if which in ['suspend', 'both']:
self.time_of_suspend = self.share_type['suspendx']
if which in ['delete', 'both']:
self.time_of_delete = self.share_type['deletex']
if not (which in ['suspend', 'delete', 'both']):
raise ValueError('No such expiration type.')
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):
"""Save image and shut down."""
imgname = "template-%d-%d" % (self.template.id, self.id)
from .tasks import SaveAsTask
SaveAsTask.delay(one_id=self.one_id, new_img=imgname)
self._change_state("SHUTDOWN")
self.save()
t = self.template
t.state = 'SAVING'
t.save()
def check_if_is_save_as_done(self):
if self.state != 'DONE':
return False
Disk.update(delete=False)
imgname = "template-%d-%d" % (self.template.id, self.id)
disks = Disk.objects.filter(name=imgname)
if len(disks) != 1:
return False
self.template.disk_id = disks[0].id
self.template.state = 'READY'
self.template.save()
self.firewall_host_delete()
return True
def delete_instance_pre(sender, instance, using, **kwargs):
if instance.state != 'DONE':
instance.one_delete()
pre_delete.connect(delete_instance_pre, sender=Instance,
dispatch_uid="delete_instance_pre")
from django.test import TestCase
from .models import Template
class TemplateTestCase(TestCase):
def test_template_creation(self):
template = Template(name='My first template',
access_method='ssh', ) # TODO add images & net
# Create your views here.
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