mirror of
https://github.com/retspen/webvirtcloud
synced 2026-03-23 11:04:49 +00:00
Added V2 from scratch
This commit is contained in:
parent
5c2232f4e8
commit
6c2925a35d
478 changed files with 21437 additions and 134206 deletions
0
backend/cloudinit/__init__.py
Normal file
0
backend/cloudinit/__init__.py
Normal file
3
backend/cloudinit/admin.py
Normal file
3
backend/cloudinit/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/cloudinit/apps.py
Normal file
5
backend/cloudinit/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CloudinitConfig(AppConfig):
|
||||
name = 'cloudinit'
|
||||
0
backend/cloudinit/migrations/__init__.py
Normal file
0
backend/cloudinit/migrations/__init__.py
Normal file
3
backend/cloudinit/models.py
Normal file
3
backend/cloudinit/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
backend/cloudinit/tests.py
Normal file
3
backend/cloudinit/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/cloudinit/views.py
Normal file
3
backend/cloudinit/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
backend/compute/__init__.py
Normal file
0
backend/compute/__init__.py
Normal file
3
backend/compute/admin.py
Normal file
3
backend/compute/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/compute/apps.py
Normal file
5
backend/compute/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ComputeConfig(AppConfig):
|
||||
name = 'compute'
|
||||
39
backend/compute/migrations/0001_initial.py
Normal file
39
backend/compute/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# Generated by Django 2.0.8 on 2018-09-22 07:20
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('region', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Compute',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator('^[a-zA-Z0-9.-_]+$')])),
|
||||
('description', models.CharField(blank=True, max_length=255)),
|
||||
('address', models.CharField(max_length=50, validators=[django.core.validators.RegexValidator('^[a-zA-Z0-9._-]+$')], verbose_name='FQDN or IP Address')),
|
||||
('login', models.CharField(blank=True, max_length=20)),
|
||||
('password', models.CharField(blank=True, max_length=20)),
|
||||
('conn', models.IntegerField(choices=[(1, 'TCP'), (3, 'TLS'), (2, 'SSH'), (4, 'SOCKET')], default=1)),
|
||||
('is_active', models.BooleanField(default=False, verbose_name='Active')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='Deleted')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True)),
|
||||
('region', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='region.Region')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Compute',
|
||||
'verbose_name_plural': 'Computes',
|
||||
'ordering': ['-id'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
backend/compute/migrations/__init__.py
Normal file
0
backend/compute/migrations/__init__.py
Normal file
81
backend/compute/models.py
Normal file
81
backend/compute/models.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
from django.db import models
|
||||
from django.core.validators import RegexValidator
|
||||
from region.models import Region
|
||||
|
||||
|
||||
class Compute(models.Model):
|
||||
|
||||
TCP = 1
|
||||
TLS = 3
|
||||
SSH = 2
|
||||
SOCKET = 4
|
||||
|
||||
CONN_CHOICES = (
|
||||
(TCP, 'TCP'),
|
||||
(TLS, 'TLS'),
|
||||
(SSH, 'SSH'),
|
||||
(SOCKET, 'SOCKET'),
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=200,
|
||||
validators=[RegexValidator('^[a-zA-Z0-9.-_]+$')],
|
||||
)
|
||||
|
||||
region = models.ForeignKey(
|
||||
Region,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
description = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
address = models.CharField(
|
||||
'FQDN or IP Address',
|
||||
max_length=50,
|
||||
validators=[RegexValidator('^[a-zA-Z0-9._-]+$')],
|
||||
)
|
||||
|
||||
login = models.CharField(
|
||||
max_length=20,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
password = models.CharField(
|
||||
max_length=20,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
conn = models.IntegerField(
|
||||
choices=CONN_CHOICES,
|
||||
default=TCP,
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
'Active',
|
||||
default=False,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
is_deleted = models.BooleanField(
|
||||
'Deleted',
|
||||
default=False,
|
||||
)
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = "Compute"
|
||||
verbose_name_plural = "Computes"
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
3
backend/compute/tests.py
Normal file
3
backend/compute/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/compute/views.py
Normal file
3
backend/compute/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
backend/flavor/__init__.py
Normal file
0
backend/flavor/__init__.py
Normal file
3
backend/flavor/admin.py
Normal file
3
backend/flavor/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/flavor/apps.py
Normal file
5
backend/flavor/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class FlavorConfig(AppConfig):
|
||||
name = 'flavor'
|
||||
61
backend/flavor/migrations/0001_initial.py
Normal file
61
backend/flavor/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# Generated by Django 2.0.8 on 2018-09-22 07:20
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import re
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Image',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.SlugField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('url', models.URLField()),
|
||||
('md5sum', models.CharField(max_length=50)),
|
||||
('is_active', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_deleted', models.BooleanField(default=False)),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Instance Image',
|
||||
'verbose_name_plural': 'Instance Images',
|
||||
'ordering': ['-id'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Size',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.CharField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])),
|
||||
('name', models.CharField(blank=True, max_length=255)),
|
||||
('cpu', models.IntegerField(default=1)),
|
||||
('disk_bytes', models.BigIntegerField(default=21474836480)),
|
||||
('memory_bytes', models.BigIntegerField(default=536870912)),
|
||||
('is_active', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_deleted', models.BooleanField(default=False)),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Instance Size',
|
||||
'verbose_name_plural': 'Instance Sizes',
|
||||
'ordering': ['-id'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='image',
|
||||
name='required_size',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='flavor.Size'),
|
||||
),
|
||||
]
|
||||
0
backend/flavor/migrations/__init__.py
Normal file
0
backend/flavor/migrations/__init__.py
Normal file
111
backend/flavor/models.py
Normal file
111
backend/flavor/models.py
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
from django.db import models
|
||||
from django.core.validators import validate_slug
|
||||
|
||||
|
||||
class Size(models.Model):
|
||||
|
||||
slug = models.CharField(
|
||||
max_length=200,
|
||||
validators=[validate_slug],
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
cpu = models.IntegerField(
|
||||
default=1,
|
||||
)
|
||||
|
||||
disk_bytes = models.BigIntegerField(
|
||||
default=21474836480,
|
||||
)
|
||||
|
||||
memory_bytes = models.BigIntegerField(
|
||||
default=536870912,
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
is_deleted = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = "Instance Size"
|
||||
verbose_name_plural = "Instance Sizes"
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
def save(self):
|
||||
if self.ram < 1 * (1024 ** 2):
|
||||
self.ram = self.ram * (1024 ** 2)
|
||||
if self.disk < 1 * (1024 ** 3):
|
||||
self.disk = self.disk * (1024 ** 3)
|
||||
super(Size, self).save()
|
||||
|
||||
|
||||
class Image(models.Model):
|
||||
|
||||
slug = models.SlugField(
|
||||
max_length=200,
|
||||
validators=[validate_slug],
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
url = models.URLField(
|
||||
max_length=200,
|
||||
)
|
||||
|
||||
md5sum = models.CharField(
|
||||
max_length=50,
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
is_deleted = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
required_size = models.ForeignKey(
|
||||
Size,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = "Instance Image"
|
||||
verbose_name_plural = "Instance Images"
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
3
backend/flavor/tests.py
Normal file
3
backend/flavor/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/flavor/views.py
Normal file
3
backend/flavor/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
backend/instance/__init__.py
Normal file
0
backend/instance/__init__.py
Normal file
3
backend/instance/admin.py
Normal file
3
backend/instance/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/instance/apps.py
Normal file
5
backend/instance/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class InstanceConfig(AppConfig):
|
||||
name = 'instance'
|
||||
0
backend/instance/migrations/__init__.py
Normal file
0
backend/instance/migrations/__init__.py
Normal file
107
backend/instance/models.py
Normal file
107
backend/instance/models.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
from region.models import Region
|
||||
from compute.models import Compute
|
||||
from flavor.models import Size, Image
|
||||
|
||||
|
||||
class Instance(models.Model):
|
||||
|
||||
ACTIVE = 1
|
||||
INACTIVE = 0
|
||||
STATUS_CHOICES = (
|
||||
(ACTIVE, 'active'),
|
||||
(INACTIVE, 'inactive'),
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
status = models.IntegerField(
|
||||
choices=STATUS_CHOICES,
|
||||
default=INACTIVE,
|
||||
)
|
||||
|
||||
uuid = models.CharField(
|
||||
max_length=40,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
compute = models.ForeignKey(
|
||||
Compute,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
size = models.ForeignKey(
|
||||
Size,
|
||||
)
|
||||
|
||||
image = models.ForeignKey(
|
||||
Image,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
region = models.ForeignKey(
|
||||
Region,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
disk_bytes = models.BigIntegerField(
|
||||
default=21474836480,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
is_task = models.BooleanField(
|
||||
default=True,
|
||||
)
|
||||
|
||||
task_id = models.IntegerField(
|
||||
default=1,
|
||||
)
|
||||
|
||||
is_private_network = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
is_locked = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
is_deleted = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
public_net_mac = models.CharField(
|
||||
max_length=18,
|
||||
default="52:54:00:01:02:03",
|
||||
)
|
||||
|
||||
private_net_mac = models.CharField(
|
||||
max_length=18,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = "Instance"
|
||||
verbose_name_plural = "Instances"
|
||||
|
||||
def __unicode__(self):
|
||||
return str(self.name)
|
||||
3
backend/instance/tests.py
Normal file
3
backend/instance/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/instance/views.py
Normal file
3
backend/instance/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
2
backend/libvmgr/__init__.py
Normal file
2
backend/libvmgr/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
__author__ = "Anatoliy Guskov"
|
||||
__license__ = "Apache 2.0"
|
||||
499
backend/libvmgr/connect.py
Normal file
499
backend/libvmgr/connect.py
Normal file
|
|
@ -0,0 +1,499 @@
|
|||
import libvirt
|
||||
import threading
|
||||
import socket
|
||||
from libvmgr import util
|
||||
from libvmgr.rwlock import ReadWriteLock
|
||||
from libvirt import libvirtError
|
||||
|
||||
|
||||
CONN_SOCKET = 4
|
||||
CONN_TLS = 3
|
||||
CONN_SSH = 2
|
||||
CONN_TCP = 1
|
||||
TLS_PORT = 16514
|
||||
SSH_PORT = 22
|
||||
TCP_PORT = 16509
|
||||
KEEPALIVE_COUNT = 30
|
||||
KEEPALIVE_INTERVAL = 5
|
||||
|
||||
|
||||
def host_is_up(conn_type, hostname):
|
||||
"""
|
||||
returns True if the given host is up and we are able to establish
|
||||
a connection using the given credentials.
|
||||
"""
|
||||
try:
|
||||
socket_host = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
socket_host.settimeout(2)
|
||||
if conn_type == CONN_SSH:
|
||||
if ':' in hostname:
|
||||
LIBVIRT_HOST, PORT = (hostname).split(":")
|
||||
PORT = int(PORT)
|
||||
else:
|
||||
PORT = SSH_PORT
|
||||
LIBVIRT_HOST = hostname
|
||||
socket_host.connect((LIBVIRT_HOST, PORT))
|
||||
if conn_type == CONN_TCP:
|
||||
socket_host.connect((hostname, TCP_PORT))
|
||||
if conn_type == CONN_TLS:
|
||||
socket_host.connect((hostname, TLS_PORT))
|
||||
socket_host.close()
|
||||
except socket.error:
|
||||
raise libvirtError('Unable to connect to host server: Operation timed out')
|
||||
|
||||
|
||||
class wvcEventLoop(threading.Thread):
|
||||
def __init__(self, group=None, target=None, name=None, args=(), kwargs={}):
|
||||
# register the default event implementation
|
||||
# of libvirt, as we do not have an existing
|
||||
# event loop.
|
||||
libvirt.virEventRegisterDefaultImpl()
|
||||
|
||||
if name is None:
|
||||
name = 'libvirt event loop'
|
||||
|
||||
super(wvcEventLoop, self).__init__(group, target, name, args, kwargs)
|
||||
|
||||
# we run this thread in deamon mode, so it does
|
||||
# not block shutdown of the server
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
# if this method will fail it raises libvirtError
|
||||
# we do not catch the exception here so it will show up
|
||||
# in the logs. Not sure when this call will ever fail
|
||||
libvirt.virEventRunDefaultImpl()
|
||||
|
||||
|
||||
class wvcConnection(object):
|
||||
"""
|
||||
class representing a single connection stored in the Connection Manager
|
||||
# to-do: may also need some locking to ensure to not connect simultaniously in 2 threads
|
||||
"""
|
||||
|
||||
def __init__(self, host, login, passwd, conn):
|
||||
"""
|
||||
Sets all class attributes and tries to open the connection
|
||||
"""
|
||||
# connection lock is used to lock all changes to the connection state attributes
|
||||
# (connection and last_error)
|
||||
self.connection_state_lock = threading.Lock()
|
||||
self.connection = None
|
||||
self.last_error = None
|
||||
|
||||
# credentials
|
||||
self.host = host
|
||||
self.login = login
|
||||
self.passwd = passwd
|
||||
self.type = conn
|
||||
|
||||
# connect
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
# recheck if we have a connection (it may have been
|
||||
if not self.connected:
|
||||
if self.type == CONN_TCP:
|
||||
self.__connect_tcp()
|
||||
elif self.type == CONN_SSH:
|
||||
self.__connect_ssh()
|
||||
elif self.type == CONN_TLS:
|
||||
self.__connect_tls()
|
||||
elif self.type == CONN_SOCKET:
|
||||
self.__connect_socket()
|
||||
else:
|
||||
raise ValueError('"{type}" is not a valid connection type'.format(type=self.type))
|
||||
|
||||
if self.connected:
|
||||
# do some preprocessing of the connection:
|
||||
# * set keep alive interval
|
||||
# * set connection close/fail handler
|
||||
try:
|
||||
self.connection.setKeepAlive(connection_manager.keepalive_interval,
|
||||
connection_manager.keepalive_count)
|
||||
try:
|
||||
self.connection.registerCloseCallback(self.__connection_close_callback, None)
|
||||
except Exception:
|
||||
# Temporary fix for libvirt > libvirt-0.10.2-41
|
||||
pass
|
||||
except libvirtError as e:
|
||||
# hypervisor driver does not seem to support persistent connections
|
||||
self.last_error = str(e)
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
@property
|
||||
def connected(self):
|
||||
try:
|
||||
return self.connection is not None and self.connection.isAlive()
|
||||
except libvirtError:
|
||||
# isAlive failed for some reason
|
||||
return False
|
||||
|
||||
def __libvirt_auth_credentials_callback(self, credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def __connection_close_callback(self, connection, reason, opaque=None):
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
# on server shutdown libvirt module gets freed before the close callbacks are called
|
||||
# so we just check here if it is still present
|
||||
if libvirt is not None:
|
||||
self.last_error = 'Connection closed'
|
||||
|
||||
# prevent other threads from using the connection (in the future)
|
||||
self.connection = None
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
def __connect_tcp(self):
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, self.__libvirt_auth_credentials_callback, None]
|
||||
uri = 'qemu+tcp://%s/system' % self.host
|
||||
|
||||
try:
|
||||
self.connection = libvirt.openAuth(uri, auth, 0)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def __connect_ssh(self):
|
||||
uri = 'qemu+ssh://%s@%s/system' % (self.login, self.host)
|
||||
|
||||
try:
|
||||
self.connection = libvirt.open(uri)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e) + ' --- ' + repr(libvirt.virGetLastError())
|
||||
self.connection = None
|
||||
|
||||
def __connect_tls(self):
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, self.__libvirt_auth_credentials_callback, None]
|
||||
uri = 'qemu+tls://%s@%s/system' % (self.login, self.host)
|
||||
|
||||
try:
|
||||
self.connection = libvirt.openAuth(uri, auth, 0)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def __connect_socket(self):
|
||||
uri = 'qemu:///system'
|
||||
|
||||
try:
|
||||
self.connection = libvirt.open(uri)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.connection = None
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
closes the connection (if it is active)
|
||||
"""
|
||||
self.connection_state_lock.acquire()
|
||||
try:
|
||||
if self.connected:
|
||||
try:
|
||||
# to-do: handle errors?
|
||||
self.connection.close()
|
||||
except libvirtError:
|
||||
pass
|
||||
|
||||
self.connection = None
|
||||
self.last_error = None
|
||||
finally:
|
||||
self.connection_state_lock.release()
|
||||
|
||||
def __del__(self):
|
||||
if self.connection is not None:
|
||||
# unregister callback (as it is no longer valid if this instance gets deleted)
|
||||
try:
|
||||
self.connection.unregisterCloseCallback()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def __unicode__(self):
|
||||
if self.type == CONN_TCP:
|
||||
type_str = u'tcp'
|
||||
elif self.type == CONN_SSH:
|
||||
type_str = u'ssh'
|
||||
elif self.type == CONN_TLS:
|
||||
type_str = u'tls'
|
||||
else:
|
||||
type_str = u'invalid_type'
|
||||
|
||||
return u'qemu+{type}://{user}@{host}/system'.format(type=type_str, user=self.login, host=self.host)
|
||||
|
||||
def __repr__(self):
|
||||
return '<wvcConnection {connection_str}>'.format(connection_str=str(self))
|
||||
|
||||
|
||||
class wvcConnectionManager(object):
|
||||
def __init__(self, keepalive_interval=5, keepalive_count=5):
|
||||
self.keepalive_interval = keepalive_interval
|
||||
self.keepalive_count = keepalive_count
|
||||
|
||||
# connection dict
|
||||
# maps hostnames to a list of connection objects for this hostname
|
||||
# atm it is possible to create more than one connection per hostname
|
||||
# with different logins or auth methods
|
||||
# connections are shared between all threads, see:
|
||||
# http://wiki.libvirt.org/page/FAQ#Is_libvirt_thread_safe.3F
|
||||
self._connections = dict()
|
||||
self._connections_lock = ReadWriteLock()
|
||||
|
||||
# start event loop to handle keepalive requests and other events
|
||||
self._event_loop = wvcEventLoop()
|
||||
self._event_loop.start()
|
||||
|
||||
def _search_connection(self, host, login, passwd, conn):
|
||||
"""
|
||||
search the connection dict for a connection with the given credentials
|
||||
if it does not exist return None
|
||||
"""
|
||||
self._connections_lock.acquireRead()
|
||||
try:
|
||||
if (host in self._connections):
|
||||
connections = self._connections[host]
|
||||
|
||||
for connection in connections:
|
||||
if (connection.login == login and connection.passwd == passwd and connection.type == conn):
|
||||
return connection
|
||||
finally:
|
||||
self._connections_lock.release()
|
||||
|
||||
return None
|
||||
|
||||
def get_connection(self, host, login, passwd, conn):
|
||||
"""
|
||||
returns a connection object (as returned by the libvirt.open* methods) for the given host and credentials
|
||||
raises libvirtError if (re)connecting fails
|
||||
"""
|
||||
# force all string values to unicode changed for Python3 to str
|
||||
host = str(host)
|
||||
login = str(login)
|
||||
passwd = str(passwd) if passwd is not None else None
|
||||
|
||||
connection = self._search_connection(host, login, passwd, conn)
|
||||
|
||||
if (connection is None):
|
||||
self._connections_lock.acquireWrite()
|
||||
try:
|
||||
# we have to search for the connection again after aquireing the write lock
|
||||
# as the thread previously holding the write lock may have already added our connection
|
||||
connection = self._search_connection(host, login, passwd, conn)
|
||||
if (connection is None):
|
||||
# create a new connection if a matching connection does not already exist
|
||||
connection = wvcConnection(host, login, passwd, conn)
|
||||
|
||||
# add new connection to connection dict
|
||||
if host in self._connections:
|
||||
self._connections[host].append(connection)
|
||||
else:
|
||||
self._connections[host] = [connection]
|
||||
finally:
|
||||
self._connections_lock.release()
|
||||
|
||||
elif not connection.connected:
|
||||
# try to (re-)connect if connection is closed
|
||||
connection.connect()
|
||||
|
||||
if connection.connected:
|
||||
# return libvirt connection object
|
||||
return connection.connection
|
||||
else:
|
||||
# raise libvirt error
|
||||
raise libvirtError(connection.last_error)
|
||||
|
||||
|
||||
connection_manager = wvcConnectionManager(KEEPALIVE_INTERVAL, KEEPALIVE_COUNT)
|
||||
|
||||
|
||||
class wvcConnect(object):
|
||||
def __init__(self, host, login=None, passwd=None, conn_type=CONN_SOCKET, keepalive=True):
|
||||
self.login = login
|
||||
self.host = host
|
||||
self.passwd = passwd
|
||||
self.conn_type = conn_type
|
||||
self.keepalive = keepalive
|
||||
|
||||
# is host up?
|
||||
host_is_up(self.conn_type, self.host)
|
||||
|
||||
# get connection from connection manager
|
||||
if self.keepalive:
|
||||
self.conn = connection_manager.get_connection(host, login, passwd, conn_type)
|
||||
else:
|
||||
if self.conn_type == CONN_TCP:
|
||||
def creds(credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, creds, None]
|
||||
uri = 'qemu+tcp://%s/system' % self.host
|
||||
try:
|
||||
self.conn = libvirt.openAuth(uri, auth, 0)
|
||||
except libvirtError:
|
||||
raise libvirtError('Connection Failed')
|
||||
|
||||
if self.conn_type == CONN_SSH:
|
||||
uri = 'qemu+ssh://%s@%s/system' % (self.login, self.host)
|
||||
try:
|
||||
self.conn = libvirt.open(uri)
|
||||
except libvirtError as err:
|
||||
raise err
|
||||
|
||||
if self.conn_type == CONN_TLS:
|
||||
def creds(credentials, user_data):
|
||||
for credential in credentials:
|
||||
if credential[0] == libvirt.VIR_CRED_AUTHNAME:
|
||||
credential[4] = self.login
|
||||
if len(credential[4]) == 0:
|
||||
credential[4] = credential[3]
|
||||
elif credential[0] == libvirt.VIR_CRED_PASSPHRASE:
|
||||
credential[4] = self.passwd
|
||||
else:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, creds, None]
|
||||
uri = 'qemu+tls://%s@%s/system' % (self.login, self.host)
|
||||
try:
|
||||
self.conn = libvirt.openAuth(uri, auth, 0)
|
||||
except libvirtError:
|
||||
raise libvirtError('Connection Failed')
|
||||
|
||||
def get_cap_xml(self):
|
||||
"""Return xml capabilities"""
|
||||
return self.conn.getCapabilities()
|
||||
|
||||
def is_kvm_supported(self):
|
||||
"""Return KVM capabilities."""
|
||||
return util.is_kvm_available(self.get_cap_xml())
|
||||
|
||||
def get_storages(self):
|
||||
storages = []
|
||||
for pool in self.conn.listStoragePools():
|
||||
storages.append(pool)
|
||||
for pool in self.conn.listDefinedStoragePools():
|
||||
storages.append(pool)
|
||||
return storages
|
||||
|
||||
def get_networks(self):
|
||||
virtnet = []
|
||||
for net in self.conn.listNetworks():
|
||||
virtnet.append(net)
|
||||
for net in self.conn.listDefinedNetworks():
|
||||
virtnet.append(net)
|
||||
return virtnet
|
||||
|
||||
def get_ifaces(self):
|
||||
interface = []
|
||||
for inface in self.conn.listInterfaces():
|
||||
interface.append(inface)
|
||||
for inface in self.conn.listDefinedInterfaces():
|
||||
interface.append(inface)
|
||||
return interface
|
||||
|
||||
def get_iface(self, name):
|
||||
return self.conn.interfaceLookupByName(name)
|
||||
|
||||
def get_secrets(self):
|
||||
return self.conn.listSecrets()
|
||||
|
||||
def get_secret(self, uuid):
|
||||
return self.conn.secretLookupByUUIDString(uuid)
|
||||
|
||||
def get_storage(self, name):
|
||||
return self.conn.storagePoolLookupByName(name)
|
||||
|
||||
def get_volume_by_path(self, path):
|
||||
return self.conn.storageVolLookupByPath(path)
|
||||
|
||||
def get_network(self, net):
|
||||
return self.conn.networkLookupByName(net)
|
||||
|
||||
def get_instance(self, name):
|
||||
return self.conn.lookupByName(name)
|
||||
|
||||
def get_instance_status(self, name):
|
||||
dom = self.conn.lookupByName(name)
|
||||
return dom.info()[0]
|
||||
|
||||
def get_instances(self):
|
||||
instances = []
|
||||
for inst_id in self.conn.listDomainsID():
|
||||
dom = self.conn.lookupByID(int(inst_id))
|
||||
instances.append(dom.name())
|
||||
for name in self.conn.listDefinedDomains():
|
||||
instances.append(name)
|
||||
return instances
|
||||
|
||||
def get_snapshots(self):
|
||||
instance = []
|
||||
for snap_id in self.conn.listDomainsID():
|
||||
dom = self.conn.lookupByID(int(snap_id))
|
||||
if dom.snapshotNum(0) != 0:
|
||||
instance.append(dom.name())
|
||||
for name in self.conn.listDefinedDomains():
|
||||
dom = self.conn.lookupByName(name)
|
||||
if dom.snapshotNum(0) != 0:
|
||||
instance.append(dom.name())
|
||||
return instance
|
||||
|
||||
def get_net_device(self):
|
||||
netdevice = []
|
||||
for dev in self.conn.listAllDevices(0):
|
||||
xml = dev.XMLDesc(0)
|
||||
if util.get_xml_data(xml, 'capability', 'type') == 'net':
|
||||
netdevice.append(util.get_xml_data(xml, 'capability/interface'))
|
||||
return netdevice
|
||||
|
||||
def get_host_instances(self):
|
||||
vname = {}
|
||||
for name in self.get_instances():
|
||||
dom = self.get_instance(name)
|
||||
mem = util.get_xml_data(dom.XMLDesc(0), 'currentMemory')
|
||||
mem = round(int(mem) / 1024)
|
||||
cur_vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu', 'current')
|
||||
if cur_vcpu:
|
||||
vcpu = cur_vcpu
|
||||
else:
|
||||
vcpu = util.get_xml_data(dom.XMLDesc(0), 'vcpu')
|
||||
vname[dom.name()] = {'status': dom.info()[0], 'uuid': dom.UUIDString(), 'vcpu': vcpu, 'memory': mem}
|
||||
return vname
|
||||
|
||||
def close(self):
|
||||
"""Close connection"""
|
||||
if not self.keepalive:
|
||||
self.conn.close()
|
||||
81
backend/libvmgr/host.py
Normal file
81
backend/libvmgr/host.py
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import time
|
||||
from libvmgr import util
|
||||
from libvmgr.connect import wvcConnect
|
||||
|
||||
|
||||
class wvcHost(wvcConnect):
|
||||
def __init__(self, conn):
|
||||
self.conn = conn
|
||||
|
||||
def get_node_info(self):
|
||||
"""
|
||||
Function return host server information: hostname, cpu, memory, ...
|
||||
"""
|
||||
info = list()
|
||||
info.append(self.conn.getHostname())
|
||||
info.append(self.conn.getInfo()[0])
|
||||
info.append(self.conn.getInfo()[1] * (1024**2))
|
||||
info.append(self.conn.getInfo()[2])
|
||||
info.append(util.get_xml_data(self.conn.getSysinfo(0), 'processor/entry[6]'))
|
||||
info.append(self.conn.getURI())
|
||||
return info
|
||||
|
||||
def hypervisor_type(self):
|
||||
"""
|
||||
Return hypervisor type
|
||||
"""
|
||||
return util.get_xml_data(self.get_cap_xml(), 'guest/arch/domain', 'type')
|
||||
|
||||
def get_memory_usage(self):
|
||||
"""
|
||||
Function return memory usage on node.
|
||||
"""
|
||||
host_mem = self.conn.getInfo()[1] * (1024**2)
|
||||
free_mem = self.conn.getMemoryStats(-1, 0)
|
||||
if isinstance(free_mem, dict):
|
||||
mem = list(free_mem.values())
|
||||
free = (mem[1] + mem[2] + mem[3]) * 1024
|
||||
percent = (100 - ((free * 100) / host_mem))
|
||||
usage = (host_mem - free)
|
||||
mem_usage = {'size': host_mem, 'usage': usage, 'percent': round(percent)}
|
||||
else:
|
||||
mem_usage = {'size': 0, 'usage': 0, 'percent': 0}
|
||||
return mem_usage
|
||||
|
||||
def get_storage_usage(self, name):
|
||||
"""
|
||||
Function return storage usage on node by name.
|
||||
"""
|
||||
pool = self.get_storage(name)
|
||||
pool.refresh()
|
||||
if pool.isActive():
|
||||
size = pool.info()[1]
|
||||
free = pool.info()[3]
|
||||
used = size - free
|
||||
percent = (used * 100) / size
|
||||
return {'size': size, 'used': used, 'percent': percent}
|
||||
else:
|
||||
return {'size': 0, 'used': 0, 'percent': 0}
|
||||
|
||||
def get_cpu_usage(self):
|
||||
"""
|
||||
Function return cpu usage on node.
|
||||
"""
|
||||
prev_idle = 0
|
||||
prev_total = 0
|
||||
diff_usage = 0
|
||||
cpu = self.conn.getCPUStats(-1, 0)
|
||||
if isinstance(cpu, dict):
|
||||
for num in range(2):
|
||||
idle = self.conn.getCPUStats(-1, 0)['idle']
|
||||
total = sum(self.conn.getCPUStats(-1, 0).values())
|
||||
diff_idle = idle - prev_idle
|
||||
diff_total = total - prev_total
|
||||
diff_usage = (1000 * (diff_total - diff_idle) / diff_total + 5) / 10
|
||||
prev_total = total
|
||||
prev_idle = idle
|
||||
if num == 0:
|
||||
time.sleep(1)
|
||||
if diff_usage < 0:
|
||||
diff_usage = 0
|
||||
return {'usage': round(diff_usage)}
|
||||
226
backend/libvmgr/rwlock.py
Normal file
226
backend/libvmgr/rwlock.py
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
# -*- coding: iso-8859-15 -*-
|
||||
"""locks.py - Read-Write lock thread lock implementation
|
||||
|
||||
See the class documentation for more info.
|
||||
|
||||
Copyright (C) 2007, Heiko Wundram.
|
||||
Released under the BSD-license.
|
||||
|
||||
found at: http://code.activestate.com/recipes/502283-read-write-lock-class-rlock-like/
|
||||
"""
|
||||
|
||||
# Imports
|
||||
# -------
|
||||
|
||||
from threading import Condition, Lock, currentThread
|
||||
from time import time
|
||||
|
||||
|
||||
# Read write lock
|
||||
# ---------------
|
||||
|
||||
class ReadWriteLock(object):
|
||||
"""Read-Write lock class. A read-write lock differs from a standard
|
||||
threading.RLock() by allowing multiple threads to simultaneously hold a
|
||||
read lock, while allowing only a single thread to hold a write lock at the
|
||||
same point of time.
|
||||
|
||||
When a read lock is requested while a write lock is held, the reader
|
||||
is blocked; when a write lock is requested while another write lock is
|
||||
held or there are read locks, the writer is blocked.
|
||||
|
||||
Writers are always preferred by this implementation: if there are blocked
|
||||
threads waiting for a write lock, current readers may request more read
|
||||
locks (which they eventually should free, as they starve the waiting
|
||||
writers otherwise), but a new thread requesting a read lock will not
|
||||
be granted one, and block. This might mean starvation for readers if
|
||||
two writer threads interweave their calls to acquireWrite() without
|
||||
leaving a window only for readers.
|
||||
|
||||
In case a current reader requests a write lock, this can and will be
|
||||
satisfied without giving up the read locks first, but, only one thread
|
||||
may perform this kind of lock upgrade, as a deadlock would otherwise
|
||||
occur. After the write lock has been granted, the thread will hold a
|
||||
full write lock, and not be downgraded after the upgrading call to
|
||||
acquireWrite() has been match by a corresponding release().
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize this read-write lock."""
|
||||
|
||||
# Condition variable, used to signal waiters of a change in object
|
||||
# state.
|
||||
self.__condition = Condition(Lock())
|
||||
|
||||
# Initialize with no writers.
|
||||
self.__writer = None
|
||||
self.__upgradewritercount = 0
|
||||
self.__pendingwriters = []
|
||||
|
||||
# Initialize with no readers.
|
||||
self.__readers = {}
|
||||
|
||||
def acquireRead(self, timeout=None):
|
||||
"""Acquire a read lock for the current thread, waiting at most
|
||||
timeout seconds or doing a non-blocking check in case timeout is <= 0.
|
||||
|
||||
In case timeout is None, the call to acquireRead blocks until the
|
||||
lock request can be serviced.
|
||||
|
||||
In case the timeout expires before the lock could be serviced, a
|
||||
RuntimeError is thrown."""
|
||||
|
||||
if timeout is not None:
|
||||
endtime = time() + timeout
|
||||
me = currentThread()
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# If we are the writer, grant a new read lock, always.
|
||||
self.__writercount += 1
|
||||
return
|
||||
while True:
|
||||
if self.__writer is None:
|
||||
# Only test anything if there is no current writer.
|
||||
if self.__upgradewritercount or self.__pendingwriters:
|
||||
if me in self.__readers:
|
||||
# Only grant a read lock if we already have one
|
||||
# in case writers are waiting for their turn.
|
||||
# This means that writers can't easily get starved
|
||||
# (but see below, readers can).
|
||||
self.__readers[me] += 1
|
||||
return
|
||||
# No, we aren't a reader (yet), wait for our turn.
|
||||
else:
|
||||
# Grant a new read lock, always, in case there are
|
||||
# no pending writers (and no writer).
|
||||
self.__readers[me] = self.__readers.get(me, 0) + 1
|
||||
return
|
||||
if timeout is not None:
|
||||
remaining = endtime - time()
|
||||
if remaining <= 0:
|
||||
# Timeout has expired, signal caller of this.
|
||||
raise RuntimeError("Acquiring read lock timed out")
|
||||
self.__condition.wait(remaining)
|
||||
else:
|
||||
self.__condition.wait()
|
||||
finally:
|
||||
self.__condition.release()
|
||||
|
||||
def acquireWrite(self, timeout=None):
|
||||
"""Acquire a write lock for the current thread, waiting at most
|
||||
timeout seconds or doing a non-blocking check in case timeout is <= 0.
|
||||
|
||||
In case the write lock cannot be serviced due to the deadlock
|
||||
condition mentioned above, a ValueError is raised.
|
||||
|
||||
In case timeout is None, the call to acquireWrite blocks until the
|
||||
lock request can be serviced.
|
||||
|
||||
In case the timeout expires before the lock could be serviced, a
|
||||
RuntimeError is thrown."""
|
||||
|
||||
if timeout is not None:
|
||||
endtime = time() + timeout
|
||||
me, upgradewriter = currentThread(), False
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# If we are the writer, grant a new write lock, always.
|
||||
self.__writercount += 1
|
||||
return
|
||||
elif me in self.__readers:
|
||||
# If we are a reader, no need to add us to pendingwriters,
|
||||
# we get the upgradewriter slot.
|
||||
if self.__upgradewritercount:
|
||||
# If we are a reader and want to upgrade, and someone
|
||||
# else also wants to upgrade, there is no way we can do
|
||||
# this except if one of us releases all his read locks.
|
||||
# Signal this to user.
|
||||
raise ValueError(
|
||||
"Inevitable dead lock, denying write lock"
|
||||
)
|
||||
upgradewriter = True
|
||||
self.__upgradewritercount = self.__readers.pop(me)
|
||||
else:
|
||||
# We aren't a reader, so add us to the pending writers queue
|
||||
# for synchronization with the readers.
|
||||
self.__pendingwriters.append(me)
|
||||
while True:
|
||||
if not self.__readers and self.__writer is None:
|
||||
# Only test anything if there are no readers and writers.
|
||||
if self.__upgradewritercount:
|
||||
if upgradewriter:
|
||||
# There is a writer to upgrade, and it's us. Take
|
||||
# the write lock.
|
||||
self.__writer = me
|
||||
self.__writercount = self.__upgradewritercount + 1
|
||||
self.__upgradewritercount = 0
|
||||
return
|
||||
# There is a writer to upgrade, but it's not us.
|
||||
# Always leave the upgrade writer the advance slot,
|
||||
# because he presumes he'll get a write lock directly
|
||||
# from a previously held read lock.
|
||||
elif self.__pendingwriters[0] is me:
|
||||
# If there are no readers and writers, it's always
|
||||
# fine for us to take the writer slot, removing us
|
||||
# from the pending writers queue.
|
||||
# This might mean starvation for readers, though.
|
||||
self.__writer = me
|
||||
self.__writercount = 1
|
||||
self.__pendingwriters = self.__pendingwriters[1:]
|
||||
return
|
||||
if timeout is not None:
|
||||
remaining = endtime - time()
|
||||
if remaining <= 0:
|
||||
# Timeout has expired, signal caller of this.
|
||||
if upgradewriter:
|
||||
# Put us back on the reader queue. No need to
|
||||
# signal anyone of this change, because no other
|
||||
# writer could've taken our spot before we got
|
||||
# here (because of remaining readers), as the test
|
||||
# for proper conditions is at the start of the
|
||||
# loop, not at the end.
|
||||
self.__readers[me] = self.__upgradewritercount
|
||||
self.__upgradewritercount = 0
|
||||
else:
|
||||
# We were a simple pending writer, just remove us
|
||||
# from the FIFO list.
|
||||
self.__pendingwriters.remove(me)
|
||||
raise RuntimeError("Acquiring write lock timed out")
|
||||
self.__condition.wait(remaining)
|
||||
else:
|
||||
self.__condition.wait()
|
||||
finally:
|
||||
self.__condition.release()
|
||||
|
||||
def release(self):
|
||||
"""Release the currently held lock.
|
||||
|
||||
In case the current thread holds no lock, a ValueError is thrown."""
|
||||
|
||||
me = currentThread()
|
||||
self.__condition.acquire()
|
||||
try:
|
||||
if self.__writer is me:
|
||||
# We are the writer, take one nesting depth away.
|
||||
self.__writercount -= 1
|
||||
if not self.__writercount:
|
||||
# No more write locks; take our writer position away and
|
||||
# notify waiters of the new circumstances.
|
||||
self.__writer = None
|
||||
self.__condition.notifyAll()
|
||||
elif me in self.__readers:
|
||||
# We are a reader currently, take one nesting depth away.
|
||||
self.__readers[me] -= 1
|
||||
if not self.__readers[me]:
|
||||
# No more read locks, take our reader position away.
|
||||
del self.__readers[me]
|
||||
if not self.__readers:
|
||||
# No more readers, notify waiters of the new
|
||||
# circumstances.
|
||||
self.__condition.notifyAll()
|
||||
else:
|
||||
raise ValueError("Trying to release unheld lock")
|
||||
finally:
|
||||
self.__condition.release()
|
||||
159
backend/libvmgr/util.py
Normal file
159
backend/libvmgr/util.py
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
import re
|
||||
import socket
|
||||
import random
|
||||
import libvirt
|
||||
import paramiko
|
||||
from time import sleep
|
||||
from xml.etree import ElementTree
|
||||
from string import ascii_letters, digits
|
||||
from passlib.hash import sha512_crypt
|
||||
|
||||
|
||||
def is_kvm_available(xml):
|
||||
tree = ElementTree.fromstring(xml)
|
||||
for dom in tree.findall('guest/arch/domain'):
|
||||
if 'kvm' in dom.get('type'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def randomMAC():
|
||||
"""Generate a random MAC address."""
|
||||
# qemu MAC
|
||||
oui = [0x52, 0x54, 0x00]
|
||||
|
||||
mac = oui + [random.randint(0x00, 0xff),
|
||||
random.randint(0x00, 0xff),
|
||||
random.randint(0x00, 0xff)]
|
||||
return ':'.join(map(lambda x: "%02x" % x, mac))
|
||||
|
||||
|
||||
def randomUUID():
|
||||
"""Generate a random UUID."""
|
||||
|
||||
u = [random.randint(0, 255) for dummy in range(0, 16)]
|
||||
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u)
|
||||
|
||||
|
||||
def get_max_vcpus(conn, type=None):
|
||||
"""@param conn: libvirt connection to poll for max possible vcpus
|
||||
@type type: optional guest type (kvm, etc.)"""
|
||||
if type is None:
|
||||
type = conn.getType()
|
||||
try:
|
||||
m = conn.getMaxVcpus(type.lower())
|
||||
except libvirt.libvirtError:
|
||||
m = 32
|
||||
return m
|
||||
|
||||
|
||||
def xml_escape(str):
|
||||
"""Replaces chars ' " < > & with xml safe counterparts"""
|
||||
if str is None:
|
||||
return None
|
||||
|
||||
str = str.replace("&", "&")
|
||||
str = str.replace("'", "'")
|
||||
str = str.replace("\"", """)
|
||||
str = str.replace("<", "<")
|
||||
str = str.replace(">", ">")
|
||||
return str
|
||||
|
||||
|
||||
def compareMAC(p, q):
|
||||
"""Compare two MAC addresses"""
|
||||
pa = p.split(":")
|
||||
qa = q.split(":")
|
||||
|
||||
if len(pa) != len(qa):
|
||||
if p > q:
|
||||
return 1
|
||||
else:
|
||||
return -1
|
||||
|
||||
for i in range(len(pa)):
|
||||
n = int(pa[i], 0x10) - int(qa[i], 0x10)
|
||||
if n > 0:
|
||||
return 1
|
||||
elif n < 0:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
|
||||
def get_xml_data(xml, path=None, element=None):
|
||||
res = ''
|
||||
if not path and not element:
|
||||
return ''
|
||||
|
||||
tree = ElementTree.fromstring(xml)
|
||||
if path:
|
||||
child = tree.find(path)
|
||||
if child is not None:
|
||||
if element:
|
||||
res = child.get(element)
|
||||
else:
|
||||
res = child.text
|
||||
else:
|
||||
res = tree.get(element)
|
||||
return res
|
||||
|
||||
|
||||
def get_xml_findall(xml, string):
|
||||
tree = ElementTree.fromstring(xml)
|
||||
return tree.findall(string)
|
||||
|
||||
|
||||
def pretty_mem(val):
|
||||
val = int(val)
|
||||
if val > (10 * 1024 * 1024):
|
||||
return "%2.2f GB" % (val / (1024.0 * 1024.0))
|
||||
else:
|
||||
return "%2.0f MB" % (val / 1024.0)
|
||||
|
||||
|
||||
def pretty_bytes(val):
|
||||
val = int(val)
|
||||
if val > (1024 * 1024 * 1024):
|
||||
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
|
||||
else:
|
||||
return "%2.2f MB" % (val / (1024.0 * 1024.0))
|
||||
|
||||
|
||||
def gen_password(length=14):
|
||||
password = ''.join(
|
||||
[random.choice(ascii_letters + digits) for dummy in range(length)]
|
||||
)
|
||||
return password
|
||||
|
||||
|
||||
def password_to_hash(password):
|
||||
salt = gen_password(8)
|
||||
password_hash = sha512_crypt.encrypt(password, salt=salt, rounds=5000)
|
||||
return password_hash
|
||||
|
||||
|
||||
def similar_name(pattern, names):
|
||||
res = []
|
||||
for name in names:
|
||||
match = re.match(pattern, name)
|
||||
if match:
|
||||
res.append(name)
|
||||
return res
|
||||
|
||||
|
||||
def check_ssh_connection(hostname, password, username='root', timeout=90):
|
||||
ssh = paramiko.SSHClient()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
for i in range(timeout):
|
||||
try:
|
||||
ssh.connect(hostname, username=username, password=password)
|
||||
ssh.close()
|
||||
return True
|
||||
except (paramiko.BadHostKeyException,
|
||||
paramiko.AuthenticationException,
|
||||
paramiko.SSHException,
|
||||
socket.error,
|
||||
EOFError):
|
||||
sleep(1)
|
||||
return False
|
||||
15
backend/manage.py
Executable file
15
backend/manage.py
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webvirtcloud.settings.prod')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
0
backend/network/__init__.py
Normal file
0
backend/network/__init__.py
Normal file
3
backend/network/admin.py
Normal file
3
backend/network/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/network/apps.py
Normal file
5
backend/network/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class NetworkConfig(AppConfig):
|
||||
name = 'network'
|
||||
0
backend/network/migrations/__init__.py
Normal file
0
backend/network/migrations/__init__.py
Normal file
3
backend/network/models.py
Normal file
3
backend/network/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
backend/network/tests.py
Normal file
3
backend/network/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/network/views.py
Normal file
3
backend/network/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
backend/region/__init__.py
Normal file
0
backend/region/__init__.py
Normal file
3
backend/region/admin.py
Normal file
3
backend/region/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/region/apps.py
Normal file
5
backend/region/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class RegionConfig(AppConfig):
|
||||
name = 'region'
|
||||
33
backend/region/migrations/0001_initial.py
Normal file
33
backend/region/migrations/0001_initial.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 2.0.8 on 2018-09-22 07:20
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import re
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Region',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.SlugField(max_length=200, validators=[django.core.validators.RegexValidator(re.compile('^[-a-zA-Z0-9_]+\\Z'), "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.", 'invalid')])),
|
||||
('name', models.CharField(blank=True, max_length=255)),
|
||||
('is_active', models.BooleanField(default=False)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('is_deleted', models.BooleanField(default=False)),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Region',
|
||||
'verbose_name_plural': 'Regions',
|
||||
'ordering': ['-id'],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
backend/region/migrations/__init__.py
Normal file
0
backend/region/migrations/__init__.py
Normal file
40
backend/region/models.py
Normal file
40
backend/region/models.py
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
from django.db import models
|
||||
from django.core.validators import validate_slug
|
||||
|
||||
|
||||
class Region(models.Model):
|
||||
|
||||
slug = models.SlugField(
|
||||
max_length=200,
|
||||
validators=[validate_slug],
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
is_active = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
|
||||
is_deleted = models.BooleanField(
|
||||
default=False
|
||||
)
|
||||
|
||||
deleted_at = models.DateTimeField(
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-id']
|
||||
verbose_name = "Region"
|
||||
verbose_name_plural = "Regions"
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
3
backend/region/tests.py
Normal file
3
backend/region/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/region/views.py
Normal file
3
backend/region/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
6
backend/requirements-dev.txt
Normal file
6
backend/requirements-dev.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
-r requirements.txt
|
||||
flake8
|
||||
bpython
|
||||
django-cors-headers
|
||||
django-rest-swagger
|
||||
django-debug-toolbar
|
||||
8
backend/requirements.txt
Normal file
8
backend/requirements.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Django==2.0.8
|
||||
djangorestframework==3.8.2
|
||||
libvirt-python==4.6.0
|
||||
passlib==1.7.1
|
||||
paramiko==2.4.1
|
||||
django-rest-auth==0.9.3
|
||||
django-allauth==0.37.1
|
||||
mysqlclient==1.3.13
|
||||
0
backend/support/__init__.py
Normal file
0
backend/support/__init__.py
Normal file
3
backend/support/admin.py
Normal file
3
backend/support/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/support/apps.py
Normal file
5
backend/support/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class SupportConfig(AppConfig):
|
||||
name = 'support'
|
||||
0
backend/support/migrations/__init__.py
Normal file
0
backend/support/migrations/__init__.py
Normal file
3
backend/support/models.py
Normal file
3
backend/support/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
3
backend/support/tests.py
Normal file
3
backend/support/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
backend/support/views.py
Normal file
3
backend/support/views.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
backend/user/__init__.py
Normal file
0
backend/user/__init__.py
Normal file
3
backend/user/admin.py
Normal file
3
backend/user/admin.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
5
backend/user/apps.py
Normal file
5
backend/user/apps.py
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class UserConfig(AppConfig):
|
||||
name = 'user'
|
||||
0
backend/user/migrations/__init__.py
Normal file
0
backend/user/migrations/__init__.py
Normal file
3
backend/user/models.py
Normal file
3
backend/user/models.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
10
backend/user/serializers.py
Normal file
10
backend/user/serializers.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from rest_framework import serializers
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
|
||||
class Meta:
|
||||
fields = ('id', 'email', 'username', 'first_name', 'last_name', 'date_joined', 'last_login', 'is_active')
|
||||
model = User
|
||||
3
backend/user/tests.py
Normal file
3
backend/user/tests.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
7
backend/user/urls.py
Normal file
7
backend/user/urls.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
from django.urls import path
|
||||
|
||||
from .views import *
|
||||
|
||||
urlpatterns = [
|
||||
path('', UserView.as_view(), name="user_view"),
|
||||
]
|
||||
13
backend/user/views.py
Normal file
13
backend/user/views.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from rest_framework.generics import RetrieveUpdateAPIView
|
||||
from .serializers import UserSerializer
|
||||
|
||||
class UserView(RetrieveUpdateAPIView):
|
||||
serializer_class = UserSerializer
|
||||
queryset = User.objects.all()
|
||||
|
||||
def get_object(self):
|
||||
return self.queryset.get(pk=self.request.user.id)
|
||||
0
backend/webvirtcloud/__init__.py
Normal file
0
backend/webvirtcloud/__init__.py
Normal file
0
backend/webvirtcloud/settings/__init__.py
Normal file
0
backend/webvirtcloud/settings/__init__.py
Normal file
144
backend/webvirtcloud/settings/base.py
Normal file
144
backend/webvirtcloud/settings/base.py
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
"""
|
||||
Django settings for webvirtcloud project.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'ky-fn$j*x6$tair==i626b%$7^o5@7^a$p)lol(3dp*4na^u8^'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = False
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
]
|
||||
|
||||
# Project application
|
||||
|
||||
INSTALLED_APPS += [
|
||||
'user',
|
||||
'region',
|
||||
'flavor',
|
||||
'compute',
|
||||
]
|
||||
|
||||
# Third party application
|
||||
|
||||
INSTALLED_APPS += [
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
'rest_auth',
|
||||
'rest_auth.registration',
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'webvirtcloud.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': ['templates'],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'webvirtcloud',
|
||||
'USER': 'root',
|
||||
'PASSWORD': 'root',
|
||||
'HOST': 'localhsot',
|
||||
'OPTIONS': {
|
||||
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/2.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
68
backend/webvirtcloud/settings/dev.py
Normal file
68
backend/webvirtcloud/settings/dev.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""
|
||||
Django development settings for webvirtcloud project.
|
||||
|
||||
"""
|
||||
try:
|
||||
from .base import *
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
DEBUG = True
|
||||
ADMIN_ENABLED = True
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
INSTALLED_APPS += [
|
||||
'corsheaders',
|
||||
'debug_toolbar',
|
||||
'rest_framework_swagger',
|
||||
]
|
||||
|
||||
MIDDLEWARE += [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
]
|
||||
|
||||
# DebugToolBar
|
||||
INTERNAL_IPS = (
|
||||
'127.0.0.1',
|
||||
)
|
||||
DEBUG_TOOLBAR_CONFIG = {
|
||||
'INTERCEPT_REDIRECTS': False,
|
||||
}
|
||||
|
||||
# CORS settings
|
||||
CORS_ORIGIN_WHITELIST = (
|
||||
'localhost:8080',
|
||||
'localhost:3000',
|
||||
)
|
||||
|
||||
# Swagger settings
|
||||
SWAGGER_SETTINGS = {
|
||||
'LOGIN_URL': 'login',
|
||||
'LOGOUT_URL': 'logout',
|
||||
'VALIDATOR_URL': None,
|
||||
'JSON_EDITOR': True,
|
||||
'USE_SESSION_AUTH': True,
|
||||
'SHOW_REQUEST_HEADERS': True,
|
||||
'DOC_EXPANSION': 'list',
|
||||
'APIS_SORTER': 'alpha',
|
||||
'SECURITY_DEFINITIONS': None,
|
||||
}
|
||||
|
||||
# DB connections
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'webvirtcloud',
|
||||
'USER': os.environ.get('DB_USER', 'root'),
|
||||
'PASSWORD': os.environ.get('DB_PASSWORD', 'root'),
|
||||
'HOST': os.environ.get('DB_HOST', 'localhost'),
|
||||
'OPTIONS': {
|
||||
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Celery settings
|
||||
CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://guest:guest@localhost:5672')
|
||||
27
backend/webvirtcloud/settings/prod.py
Normal file
27
backend/webvirtcloud/settings/prod.py
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
"""
|
||||
Django production settings for webvirtcloud project.
|
||||
"""
|
||||
try:
|
||||
from .base import *
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
DEBUG = False
|
||||
ADMIN_ENABLED = True
|
||||
|
||||
# DB connections
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'webvirtcloud',
|
||||
'USER': os.environ.get('DB_USER', 'root'),
|
||||
'PASSWORD': os.environ.get('DB_PASSWORD', 'root'),
|
||||
'HOST': os.environ.get('DB_HOST', 'localhost'),
|
||||
'OPTIONS': {
|
||||
'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
# Celery settings
|
||||
CELERY_BROKER_URL = os.environ.get('BROKER_URL', 'amqp://guest:guest@localhost:5672')
|
||||
24
backend/webvirtcloud/urls.py
Normal file
24
backend/webvirtcloud/urls.py
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
"""webvirtcloud URL Configuration
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
|
||||
from rest_framework_swagger.views import get_swagger_view
|
||||
|
||||
admin.site.site_header = "Webvirtcloud"
|
||||
|
||||
api_urlpatterns = [
|
||||
path("rest-auth/registration/", include("rest_auth.registration.urls")),
|
||||
path("rest-auth/", include("rest_auth.urls")),
|
||||
path("user/", include("user.urls"))
|
||||
]
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('api/v1/', include(api_urlpatterns)),
|
||||
]
|
||||
|
||||
if settings.DEBUG:
|
||||
schema_view = get_swagger_view(title="Webvirtcloud API")
|
||||
urlpatterns += [path('docs/', schema_view)]
|
||||
16
backend/webvirtcloud/wsgi.py
Normal file
16
backend/webvirtcloud/wsgi.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for webvirtcloud project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'webvirtcloud.settings.production')
|
||||
|
||||
application = get_wsgi_application()
|
||||
Loading…
Add table
Add a link
Reference in a new issue