1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-10-31 19:44:16 +00:00

Python3 & Django 2.2 Migration - Fix & Updates

This commit is contained in:
catborise 2020-03-16 16:59:45 +03:00
parent fc8612c604
commit 4d40de1b55
98 changed files with 1525 additions and 6658 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
.vagrant
venv
venv2
.vscode
.idea
.DS_*

View file

@ -1,8 +1,8 @@
language: python
python:
- "2.7"
- "3.6"
env:
- DJANGO=1.11.26
- DJANGO=2.2.10
install:
- pip install -r dev/requirements.txt
script:

View file

@ -6,21 +6,21 @@ RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean
RUN apt-get update -qqy
RUN DEBIAN_FRONTEND=noninteractive apt-get -qyy install \
-o APT::Install-Suggests=false \
git python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev nginx libsasl2-modules
git python3-virtualenv python3-dev python3-lxml virtualenv libvirt-dev zlib1g-dev nginx libsasl2-modules
ADD . /srv/webvirtcloud
RUN chown -R www-data:www-data /srv/webvirtcloud
# Setup webvirtcloud
RUN cd /srv/webvirtcloud && \
virtualenv venv && \
virtualenv --python=python3 venv && \
. venv/bin/activate && \
pip install -U pip && \
pip install -r conf/requirements.txt && \
pip3 install -U pip && \
pip3 install -r conf/requirements.txt && \
chown -R www-data:www-data /srv/webvirtcloud
RUN cd /srv/webvirtcloud && . venv/bin/activate && \
python manage.py migrate && \
python3 manage.py migrate && \
chown -R www-data:www-data /srv/webvirtcloud
# Setup Nginx

View file

@ -1,5 +1,5 @@
## WebVirtCloud
###### Python3 & Django 2.11
## Features
* QEMU/KVM Hypervisor Management
@ -38,10 +38,10 @@ haystack = string.ascii_letters + string.digits + string.punctuation
print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)]))
```
### Install WebVirtCloud panel (Ubuntu)
### Install WebVirtCloud panel (Ubuntu 18.04+ LTS)
```bash
sudo apt-get -y install git virtualenv python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python-guestfs
sudo apt-get -y install git virtualenv python3-virtualenv python3-dev python3-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs
git clone https://github.com/retspen/webvirtcloud
cd webvirtcloud
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
@ -52,7 +52,7 @@ cd ..
sudo mv webvirtcloud /srv
sudo chown -R www-data:www-data /srv/webvirtcloud
cd /srv/webvirtcloud
virtualenv venv
virtualenv -p python3 venv
source venv/bin/activate
pip install -r conf/requirements.txt
python manage.py migrate
@ -76,11 +76,11 @@ Done!!
Go to http://serverip and you should see the login screen.
### Install WebVirtCloud panel (CentOS)
### Install WebVirtCloud panel (CentOS8/OEL8)
```bash
sudo yum -y install python-virtualenv python-devel libvirt-devel glibc gcc nginx supervisor python-lxml git python-libguestfs iproute-tc
sudo yum -y install epel-release
sudo yum -y install python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml git python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs
```
#### Creating directories and cloning repo
@ -90,15 +90,17 @@ sudo mkdir /srv && cd /srv
sudo git clone https://github.com/retspen/webvirtcloud && cd webvirtcloud
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
# now put secret key to webvirtcloud/settings.py
# create secret key manually or use that command
sudo sed -r "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
```
#### Start installation webvirtcloud
```
sudo virtualenv venv
sudo source venv/bin/activate
sudo venv/bin/pip install -r conf/requirements.txt
sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
sudo venv/bin/python manage.py migrate
virtualenv-3 venv
source venv/bin/activate
pip3 install -r conf/requirements.txt
cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
python3 manage.py migrate
```
#### Configure the supervisor for CentOS
@ -115,7 +117,7 @@ autorestart=true
redirect_stderr=true
[program:novncd]
command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd
command=/srv/webvirtcloud/venv/bin/python3 /srv/webvirtcloud/console/novncd
directory=/srv/webvirtcloud
user=nginx
autostart=true
@ -191,11 +193,20 @@ Change permission for selinux:
```bash
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webvirtcloud(/.*)"
sudo setsebool -P httpd_can_network_connect on -P
```
Add required user to the kvm group:
Add required user to the kvm group(if you not install with root):
```bash
sudo usermod -G kvm -a webvirtmgr
sudo usermod -G kvm -a <username>
```
Allow http ports on firewall:
```bash
sudo firewall-cmd --add-service=http
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-port=6080/tcp
sudo firewall-cmd --add-port=6080/tcp --permanent
```
Let's restart nginx and the supervisord services:
@ -226,7 +237,7 @@ Done!!
Go to http://serverip and you should see the login screen.
### Alternative running novncd via runit
### Alternative running novncd via runit(Debian)
Alternative to running nonvcd via supervisor is runit.
On Debian systems install runit and configure novncd service
@ -272,11 +283,12 @@ datasource:
### How To Update
```bash
cd <installation-directory>
sudo source venv/bin/activate
# Go to Installation Directory
cd /srv/webvirtcloud
source venv/bin/activate
git pull
pip install -U -r conf/requirements.txt
python manage.py migrate
pip3 install -U -r conf/requirements.txt
python3 manage.py migrate
sudo service supervisor restart
```
### Screenshots

17
Vagrantfile vendored
View file

@ -12,10 +12,10 @@ Vagrant.configure(2) do |config|
sudo sed -i 's/auth_tcp = \"sasl\"/auth_tcp = \"none\"/g' /etc/libvirt/libvirtd.conf
sudo service libvirt-bin restart
sudo adduser vagrant libvirtd
sudo apt-get -y install python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev
virtualenv /vagrant/venv
sudo apt-get -y install python3-virtualenv virtualenv python3-pip python3-dev python3-lxml libvirt-dev zlib1g-dev python3-guestfs
virtualenv -p python3 /vagrant/venv
source /vagrant/venv/bin/activate
pip install -r /vagrant/dev/requirements.txt
pip3 install -r /vagrant/dev/requirements.txt
SHELL
end
# To start this machine run "vagrant up prod"
@ -24,6 +24,7 @@ Vagrant.configure(2) do |config|
prod.vm.box = "ubuntu/bionic64"
prod.vm.hostname = "webvirtcloud"
prod.vm.network "private_network", ip: "192.168.33.11"
prod.vm.network "forwarded_port", guest: 80, host: 8081
#prod.vm.synced_folder ".", "/srv/webvirtcloud"
prod.vm.provision "shell", inline: <<-SHELL
sudo mkdir /srv/webvirtcloud
@ -33,15 +34,15 @@ Vagrant.configure(2) do |config|
sudo service libvirt-bin restart
sudo adduser vagrant libvirtd
sudo chown -R vagrant:vagrant /srv/webvirtcloud
sudo apt-get -y install python-virtualenv python-dev python-lxml python-pip libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python-guestfs
virtualenv /srv/webvirtcloud/venv
sudo apt-get -y install python3-virtualenv python3-dev python3-lxml python3-pip virtualenv libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs
virtualenv -p python3 /srv/webvirtcloud/venv
source /srv/webvirtcloud/venv/bin/activate
pip install -r /srv/webvirtcloud/dev/requirements.txt
pip3 install -r /srv/webvirtcloud/requirements.txt
sudo cp /srv/webvirtcloud/conf/supervisor/webvirtcloud.conf /etc/supervisor/conf.d
sudo cp /srv/webvirtcloud/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d
sudo cp /srv/webvirtcloud/webvirtcloud/settings.py.template /srv/webvirtcloud/webvirtcloud/settings.py
sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
python /srv/webvirtcloud/manage.py migrate
sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
python3 /srv/webvirtcloud/manage.py migrate
sudo rm /etc/nginx/sites-enabled/default
sudo chown -R www-data:www-data /srv/webvirtcloud
sudo service nginx restart

View file

@ -9,7 +9,8 @@ class UserAddForm(forms.Form):
name = forms.CharField(label="Name",
error_messages={'required': _('No User name has been entered')},
max_length=20)
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD, error_messages={'required': _('No password has been entered')},)
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD,
error_messages={'required': _('No password has been entered')},)
def clean_name(self):
name = self.cleaned_data['name']

View file

@ -1,29 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instances', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserSSHKey',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('keyname', models.CharField(max_length=25)),
('keypublic', models.CharField(max_length=500)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserInstance',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_change', models.BooleanField(default=False)),
('is_delete', models.BooleanField(default=False)),
('instance', models.ForeignKey(to='instances.Instance')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('is_vnc', models.BooleanField(default=False)),
('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='instances.Instance')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='UserAttributes',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('can_clone_instances', models.BooleanField(default=True)),
('max_instances', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_cpus', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_memory', models.IntegerField(default=2048, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('max_disk_size', models.IntegerField(default=20, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def add_useradmin(apps, schema_editor):
from django.utils import timezone
from django.contrib.auth.models import User
User.objects.create_superuser('admin', None, 'admin',
last_login=timezone.now()
)
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.RunPython(add_useradmin),
]

View file

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0002_auto_20150325_0846'),
]
operations = [
migrations.CreateModel(
name='UserSSHKey',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('keyname', models.CharField(max_length=25)),
('keypublic', models.CharField(max_length=500)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('accounts', '0003_usersshkey'),
]
operations = [
migrations.CreateModel(
name='UserAttributes',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('max_instances', models.IntegerField(default=0)),
('max_cpus', models.IntegerField(default=0)),
('max_memory', models.IntegerField(default=0)),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0003_usersshkey'),
]
operations = [
migrations.AddField(
model_name='userinstance',
name='is_vnc',
field=models.BooleanField(default=False),
),
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0004_userattributes'),
]
operations = [
migrations.AddField(
model_name='userattributes',
name='can_clone_instances',
field=models.BooleanField(default=False),
),
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('accounts', '0005_userattributes_can_clone_instances'),
]
operations = [
migrations.AddField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=0),
),
]

View file

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0006_userattributes_max_disk_size'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048),
),
]

View file

@ -1,15 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0004_userinstance_is_vnc'),
('accounts', '0007_auto_20160426_0635'),
]
operations = [
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0008_merge'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='can_clone_instances',
field=models.BooleanField(default=True),
),
]

View file

@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-06-25 12:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0009_auto_20171026_0805'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value'),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value'),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value'),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value'),
),
]

View file

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-06-25 13:13
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0010_auto_20180625_1236'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
]

View file

@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-06-25 13:31
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import re
class Migration(migrations.Migration):
dependencies = [
('accounts', '0011_auto_20180625_1313'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.')]),
),
]

View file

@ -1,37 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-06-25 13:58
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import re
class Migration(migrations.Migration):
dependencies = [
('accounts', '0012_auto_20180625_1331'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.RegexValidator(re.compile('^-?\\d+\\Z'), code='invalid', message='Enter a valid integer.'), django.core.validators.MinValueValidator(-1)]),
),
]

View file

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-08-08 11:36
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0013_auto_20180625_1358'),
]
operations = [
migrations.AlterField(
model_name='userattributes',
name='max_cpus',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_disk_size',
field=models.IntegerField(default=20, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_instances',
field=models.IntegerField(default=1, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
migrations.AlterField(
model_name='userattributes',
name='max_memory',
field=models.IntegerField(default=2048, help_text=b'-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)]),
),
]

View file

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-08-08 11:49
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('accounts', '0014_auto_20180808_1436'),
]
operations = [
migrations.AlterField(
model_name='usersshkey',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL),
),
]

View file

@ -1,36 +1,39 @@
from django.db import models
from django.db.models import Model, BooleanField, IntegerField, CharField
from django.db.models import ForeignKey, OneToOneField
from django.db.models import CASCADE, DO_NOTHING
from django.contrib.auth.models import User
from django.conf import settings
from instances.models import Instance
from django.core.validators import MinValueValidator
class UserInstance(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
is_change = models.BooleanField(default=False)
is_delete = models.BooleanField(default=False)
is_vnc = models.BooleanField(default=False)
class UserInstance(Model):
user = ForeignKey(User, on_delete=CASCADE)
instance = ForeignKey(Instance, on_delete=CASCADE)
is_change = BooleanField(default=False)
is_delete = BooleanField(default=False)
is_vnc = BooleanField(default=False)
def __unicode__(self):
return self.instance.name
class UserSSHKey(models.Model):
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
keyname = models.CharField(max_length=25)
keypublic = models.CharField(max_length=500)
class UserSSHKey(Model):
user = ForeignKey(User, on_delete=DO_NOTHING)
keyname = CharField(max_length=25)
keypublic = CharField(max_length=500)
def __unicode__(self):
return self.keyname
class UserAttributes(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
can_clone_instances = models.BooleanField(default=True)
max_instances = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1),])
max_cpus = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_memory = models.IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_disk_size = models.IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
class UserAttributes(Model):
user = OneToOneField(User, on_delete=CASCADE)
can_clone_instances = BooleanField(default=True)
max_instances = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1), ])
max_cpus = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_memory = IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
max_disk_size = IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
@staticmethod
def create_missing_userattributes(user):

View file

@ -87,8 +87,8 @@
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "VNC" %}</label>
<div class="col-sm-6">
<select type="text" class="form-control" name="inst_vnc">
<option value="">False</option>
<select class="form-control" name="inst_vnc">
<option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_vnc %}selected{% endif %}>True</option>
</select>
</div>
@ -96,8 +96,8 @@
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Resize" %}</label>
<div class="col-sm-6">
<select type="text" class="form-control" name="inst_change">
<option value="">False</option>
<select class="form-control" name="inst_change">
<option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_change %}selected{% endif %}>True</option>
</select>
</div>
@ -105,8 +105,8 @@
<div class="form-group">
<label class="col-sm-4 control-label">{% trans "Delete" %}</label>
<div class="col-sm-6">
<select type="text" class="form-control" name="inst_delete">
<option value="">False</option>
<select class="form-control" name="inst_delete">
<option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_delete %}selected{% endif %}>True</option>
</select>
</div>
@ -124,7 +124,7 @@
<td style="width:5px;">
<form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="user_inst" value="{{ inst.id }}">
<button type="submit" class="btn btn-xs btn-default" name="delete" tittle="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<button type="submit" class="btn btn-xs btn-default" name="delete" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button>
</form>

View file

@ -1,10 +1,10 @@
from django.conf.urls import url
from django.urls import path, re_path
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
url(r'^logout/$', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
url(r'^profile/$', views.profile, name='profile'), url(r'^$', views.accounts, name='accounts'),
url(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
path('profile/', views.profile, name='profile'), path('', views.accounts, name='accounts'),
re_path(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
]

View file

@ -151,10 +151,8 @@ class ComputeEditHostForm(forms.Form):
class ComputeAddSocketForm(forms.Form):
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
max_length=64)
details = forms.CharField(error_messages={'required': _('No details has been entred')},
max_length=50)
name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, max_length=64)
details = forms.CharField(error_messages={'required': _('No details has been entred')}, max_length=50)
def clean_name(self):
name = self.cleaned_data['name']

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
@ -13,15 +14,13 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Compute',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=20)),
('hostname', models.CharField(max_length=20)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=64)),
('hostname', models.CharField(max_length=64)),
('login', models.CharField(max_length=20)),
('password', models.CharField(max_length=14, null=True, blank=True)),
('password', models.CharField(blank=True, max_length=14, null=True)),
('details', models.CharField(blank=True, max_length=64, null=True)),
('type', models.IntegerField()),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('computes', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='Compute',
name='details',
field=models.CharField(max_length=50, null=True, blank=True),
),
]

View file

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.26 on 2020-01-21 12:23
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('computes', '0002_compute_details'),
]
operations = [
migrations.AlterField(
model_name='compute',
name='details',
field=models.CharField(blank=True, max_length=64, null=True),
),
migrations.AlterField(
model_name='compute',
name='hostname',
field=models.CharField(max_length=64),
),
migrations.AlterField(
model_name='compute',
name='name',
field=models.CharField(max_length=64),
),
]

View file

@ -1,13 +1,13 @@
from django.db import models
from django.db.models import Model, CharField, IntegerField
class Compute(models.Model):
name = models.CharField(max_length=64)
hostname = models.CharField(max_length=64)
login = models.CharField(max_length=20)
password = models.CharField(max_length=14, blank=True, null=True)
details = models.CharField(max_length=64, null=True, blank=True)
type = models.IntegerField()
class Compute(Model):
name = CharField(max_length=64)
hostname = CharField(max_length=64)
login = CharField(max_length=20)
password = CharField(max_length=14, blank=True, null=True)
details = CharField(max_length=64, null=True, blank=True)
type = IntegerField()
def __unicode__(self):
return self.hostname

View file

@ -1,4 +1,4 @@
from django.conf.urls import url
from django.urls import path, re_path
from storages.views import storages, storage, get_volumes
from networks.views import networks, network
from secrets.views import secrets
@ -9,23 +9,24 @@ from instances.views import instances
from nwfilters.views import nwfilter, nwfilters
urlpatterns = [
url(r'^$', computes, name='computes'),
url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/volumes$', get_volumes, name='volumes'),
url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
url(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
url(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
url(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
url(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
url(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
url(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance_select_type, name='create_instance_select_type'),
url(r'^(?P<compute_id>[0-9]+)/create/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)$', create_instance, name='create_instance'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines$', get_compute_machine_types, name='machines'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/disks/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'),
path('', computes, name='computes'),
re_path(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
re_path(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
re_path(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
re_path(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
re_path(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/volumes$', get_volumes, name='volumes'),
re_path(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
re_path(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
re_path(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
re_path(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
re_path(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
re_path(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
re_path(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
re_path(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
re_path(r'^(?P<compute_id>[0-9]+)/create/$', create_instance_select_type, name='create_instance_select_type'),
re_path(r'^(?P<compute_id>[0-9]+)/create/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)$', create_instance, name='create_instance'),
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines$', get_compute_machine_types, name='machines'),
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/disks/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'),
]

View file

@ -3,7 +3,7 @@
# gstfsd - WebVirtCloud daemon for managing VM's filesystem
#
import SocketServer
import socketserver
import json
import guestfs
import re
@ -13,11 +13,11 @@ PORT = 16510
ADDRESS = "0.0.0.0"
class MyTCPServer(SocketServer.ThreadingTCPServer):
class MyTCPServer(socketserver.ThreadingTCPServer):
allow_reuse_address = True
class MyTCPServerHandler(SocketServer.BaseRequestHandler):
class MyTCPServerHandler(socketserver.BaseRequestHandler):
def handle(self):
# recive data
data = json.loads(self.request.recv(1024).strip())
@ -42,9 +42,9 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
if data['action'] == 'publickey':
if not gfs.is_dir('/root/.ssh'):
gfs.mkdir('/root/.ssh')
gfs.chmod(0700, "/root/.ssh")
gfs.chmod(700, "/root/.ssh")
gfs.write('/root/.ssh/authorized_keys', data['key'])
gfs.chmod(0600, '/root/.ssh/authorized_keys')
gfs.chmod(600, '/root/.ssh/authorized_keys')
self.request.sendall(json.dumps({'return': 'success'}))
gfs.umount(part)
except RuntimeError:
@ -52,7 +52,7 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
gfs.shutdown()
gfs.close()
except RuntimeError as err:
self.request.sendall(json.dumps({'return': 'error', 'message': err.message}))
self.request.sendall(json.dumps({'return': 'error', 'message': err}))
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)

View file

@ -1,7 +1,8 @@
Django==1.11.26
Django==2.2.10
websockify==0.9.0
gunicorn==19.9.0
lxml==4.4.2
libvirt-python==5.10.0
gunicorn==20.0.4
lxml==4.5.0
libvirt-python==6.1.0
six
pytz
rwlock

View file

@ -1,4 +1,4 @@
import random, string
haystack = string.ascii_letters + string.digits + string.punctuation
print(''.join([random.SystemRandom().choice(haystack.replace('/','').replace('\'', '').replace('\"','')) for _ in range(50)]))
print(''.join([random.SystemRandom().choice(haystack.replace('/', '').replace('\'', '').replace('\"', '')) for _ in range(50)]))

View file

@ -1,5 +1,5 @@
[program:gstfsd]
command=/usr/bin/python /usr/local/bin/gstfsd
command=/srv/webvirtcloud/venv/bin/python3 /usr/local/bin/gstfsd
directory=/usr/local/bin
user=root
autostart=true

View file

@ -7,7 +7,7 @@ autorestart=true
redirect_stderr=true
[program:novncd]
command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd
command=/srv/webvirtcloud/venv/bin/python3 /srv/webvirtcloud/console/novncd
directory=/srv/webvirtcloud
user=www-data
autostart=true

View file

@ -7,7 +7,7 @@ import django
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
ROOT_PATH = os.path.abspath(os.path.join(DIR_PATH, '..', ''))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
os.environ["DJANGO_SETTINGS_MODULE"] = "webvirtcloud.settings"
CERT = DIR_PATH + '/cert.pem'
if ROOT_PATH not in sys.path:
@ -15,16 +15,16 @@ if ROOT_PATH not in sys.path:
django.setup()
# VENV_PATH = ROOT_PATH + '/venv/lib/python2.7/site-packages'
# VENV_PATH = ROOT_PATH + '/venv/lib/python3.6/site-packages'
# if VENV_PATH not in sys.path:
# sys.path.append(VENV_PATH)
import re
import Cookie
import socket
from six.moves import http_cookies as Cookie
from webvirtcloud.settings import WS_PORT, WS_HOST, WS_CERT
from vrtManager.connection import CONN_SSH, CONN_SOCKET
from tunnel import Tunnel
from console.tunnel import Tunnel
from optparse import OptionParser
parser = OptionParser()
@ -127,12 +127,30 @@ def get_connection_infos(token):
class CompatibilityMixIn(object):
def _new_client(self, daemon, socket_factory):
cookie = Cookie.SimpleCookie()
cookie.load(self.headers.getheader('cookie'))
if 'token' not in cookie:
self.msg('No token cookie found !')
return False
token = cookie['token'].value
# NoVNC uses it's own convention that forward token
# from the request to a cookie header, we should check
# also for this behavior
hcookie = self.headers.get('cookie')
if hcookie:
cookie = Cookie.SimpleCookie()
for hcookie_part in hcookie.split(';'):
hcookie_part = hcookie_part.lstrip()
try:
cookie.load(hcookie_part)
except Cookie.CookieError:
# NOTE(stgleb): Do not print out cookie content
# for security reasons.
self.msg('Found malformed cookie')
else:
if 'token' in cookie:
token = cookie['token'].value
# cookie = Cookie.SimpleCookie()
# cookie.load(self.headers.getheader('cookie'))
# if 'token' not in cookie:
# self.msg('No token cookie found !')
# return False
# token = cookie['token'].value
(connhost, connport, connuser, conntype, console_host, console_port,
console_socket) = get_connection_infos(token)

View file

@ -35,7 +35,6 @@
margin-left: auto;
margin-right: auto;
display: block;
margin: auto;
}
#status {

View file

@ -25,65 +25,28 @@
{% extends "console-base.html" %}
{% load i18n %}
{% load staticfiles %}
{% block head %}
<title>WebVirtCloud - Spice Client - Full</title>
<script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
<script src="{% static "js/spice-html5/enums.js" %}"></script>
<script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
<script src="{% static "js/spice-html5/utils.js" %}"></script>
<script src="{% static "js/spice-html5/png.js" %}"></script>
<script src="{% static "js/spice-html5/lz.js" %}"></script>
<script src="{% static "js/spice-html5/quic.js" %}"></script>
<script src="{% static "js/spice-html5/bitmap.js" %}"></script>
<script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
<script src="{% static "js/spice-html5/spicetype.js" %}"></script>
<script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
<script src="{% static "js/spice-html5/wire.js" %}"></script>
<script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
<script src="{% static "js/spice-html5/display.js" %}"></script>
<script src="{% static "js/spice-html5/port.js" %}"></script>
<script src="{% static "js/spice-html5/main.js" %}"></script>
<script src="{% static "js/spice-html5/inputs.js" %}"></script>
<script src="{% static "js/spice-html5/webm.js" %}"></script>
<script src="{% static "js/spice-html5/playback.js" %}"></script>
<script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
<script src="{% static "js/spice-html5/cursor.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
<script src="{% static "js/spice-html5/ticket.js" %}"></script>
<script src="{% static "js/spice-html5/resize.js" %}"></script>
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
{% endblock %}
{% block content %}
<div id="login" hidden>
<span class="logo">SPICE</span>
<label for="host">{% trans 'Host' %}:</label> <input type='text' id='host' value='{{ ws_host }}'> <!-- localhost -->
<label for="port">{% trans 'Port' %}:</label> <input type='text' id='port' value='{{ ws_port }}'>
<label for="password">{% trans 'Password' %}:</label> <input type='password' id='password' value='{{ console_passwd }}'>
<label for="show_console">{% trans 'Show console' %}</label><input type="checkbox" id="show_console" value="1" onchange="toggle_console()" checked>
<button id="connectButton" onclick="connect();">{% trans 'Start' %}</button>
</div>
<div id="spice-area">
<div id="spice-screen" class="spice-screen"></div>
<div id="message-div" class="spice-message"></div>
</div>
<div id="debug-div">
<!-- If DUMPXXX is turned on, dumped images will go here -->
</div>
{% endblock %}
{% block foot %}
<!-- ES2015/ES6 modules polyfill -->
<script type="module">
window._spice_has_module_support = true;
</script>
<script>
window.addEventListener("load", function() {
if (window._spice_has_module_support) return;
var loader = document.createElement("script");
loader.src = '{% static "thirdparty/browser-es-module-loader/dist/browser-es-module-loader.js" %}';
document.head.appendChild(loader);
});
</script>
<script type="module" crossorigin="anonymous">
import * as SpiceHtml5 from '{% static "js/spice-html5/main.js" %}';
var host = null, port = null;
var sc;
@ -96,9 +59,6 @@
host = document.getElementById("host").value;
port = document.getElementById("port").value;
if (window.location.protocol == 'https:') {
scheme = "wss://";
}
password = document.getElementById("password").value;
if ((!host) || (!port)) {
@ -116,11 +76,10 @@
document.getElementById('connectButton').onclick = disconnect;
try {
sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
sc = new SpiceHtml5.SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
}
catch (e)
{
catch (e) {
alert(e.toString());
disconnect();
}
@ -135,25 +94,27 @@
document.getElementById('connectButton').onclick = connect;
if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.getElementById('spice-xfer-area');
document.getElementById('spice-area').removeChild(spice_xfer_area);
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
if (spice_xfer_area != null) {
document.getElementById('spice-area').removeChild(spice_xfer_area);
}
document.getElementById('spice-area').removeEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').removeEventListener('drop', SpiceHtml5.handle_file_drop, false);
}
console.log("<< disconnect");
}
function agent_connected(sc) {
window.addEventListener('resize', handle_resize);
window.addEventListener('resize', SpiceHtml5.handle_resize);
window.spice_connection = this;
resize_helper(this);
SpiceHtml5.resize_helper(this);
if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.createElement("div");
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
document.getElementById('spice-area').appendChild(spice_xfer_area);
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
document.getElementById('spice-area').addEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
}
else {
console.log("File API is not supported");
@ -171,20 +132,9 @@
m.style.display = 'none';
}
window.addEventListener('resize', handle_resize);
resize_helper(sc);
}
function fullscreen() {
var screen=document.getElementById('spice-screen');
if(screen.requestFullscreen) {
screen.requestFullscreen();
} else if(screen.mozRequestFullScreen) {
screen.mozRequestFullScreen();
} else if(screen.webkitRequestFullscreen) {
screen.webkitRequestFullscreen();
} else if(screen.msRequestFullscreen) {
screen.msRequestFullscreen();
window.addEventListener('resize', SpiceHtml5.handle_resize);
if (sc) {
SpiceHtml5.resize_helper(sc);
}
}
/* SPICE port event listeners
@ -198,20 +148,72 @@
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent);
});
*/
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
document.getElementById('ctrlaltdel').addEventListener('click', sendCtrlAltDel);
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
document.getElementById('ctrlaltf4').addEventListener('click', function(){sendCtrlAltFN(3);});
document.getElementById('ctrlaltf5').addEventListener('click', function(){sendCtrlAltFN(4);});
document.getElementById('ctrlaltf6').addEventListener('click', function(){sendCtrlAltFN(5);});
document.getElementById('ctrlaltf7').addEventListener('click', function(){sendCtrlAltFN(6);});
document.getElementById('ctrlaltf8').addEventListener('click', function(){sendCtrlAltFN(7);});
document.getElementById('ctrlaltf9').addEventListener('click', function(){sendCtrlAltFN(8);});
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
connect();
</script>
function fullscreen() {
var screen=document.getElementById('spice-screen');
if(screen.requestFullscreen) {
screen.requestFullscreen();
} else if(screen.mozRequestFullScreen) {
screen.mozRequestFullScreen();
} else if(screen.webkitRequestFullscreen) {
screen.webkitRequestFullscreen();
} else if(screen.msRequestFullscreen) {
screen.msRequestFullscreen();
}
}
/* SPICE port event listeners
window.addEventListener('spice-port-data', function(event) {
// Here we convert data to text, but really we can obtain binary data also
var msg_text = arraybuffer_to_str(new Uint8Array(event.detail.data));
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'message text:', msg_text);
});
window.addEventListener('spice-port-event', function(event) {
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent);
});
*/
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
document.getElementById('ctrlaltdel').addEventListener('click', function(){sendCtrlAltDel(sc);});
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
document.getElementById('ctrlaltf4').addEventListener('click', function(){sendCtrlAltFN(3);});
document.getElementById('ctrlaltf5').addEventListener('click', function(){sendCtrlAltFN(4);});
document.getElementById('ctrlaltf6').addEventListener('click', function(){sendCtrlAltFN(5);});
document.getElementById('ctrlaltf7').addEventListener('click', function(){sendCtrlAltFN(6);});
document.getElementById('ctrlaltf8').addEventListener('click', function(){sendCtrlAltFN(7);});
document.getElementById('ctrlaltf9').addEventListener('click', function(){sendCtrlAltFN(8);});
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
document.getElementById('connectButton').onclick = connect;
document.getElementById('show_console').onchange = toggle_console;
</script>
{% endblock %}
{% block content %}
<div id="login">
<span class="logo">SPICE</span>
<label for="host">{% trans 'Host' %}:</label> <input type='text' id='host' value='{{ ws_host }}'> <!-- localhost -->
<label for="port">{% trans 'Port' %}:</label> <input type='text' id='port' value='{{ ws_port }}'>
<label for="password">{% trans 'Password' %}:</label> <input type='password' id='password' value='{{ console_passwd }}'>
<label for="show_console">{% trans 'Show console' %}</label><input type="checkbox" id="show_console" value="1" onchange="toggle_console()" checked>
<button id="connectButton">{% trans 'Start' %}</button>
</div>
<div id="spice-area">
<div id="spice-screen" class="spice-screen"></div>
</div>
<div id="message-div" class="spice-message"></div>
<div id="debug-div">
<!-- If DUMPXXX is turned on, dumped images will go here -->
</div>
{% endblock %}
{% block foot %}
{% endblock %}

View file

@ -25,56 +25,27 @@
{% extends "console-base.html" %}
{% load i18n %}
{% load staticfiles %}
{% block head %}
<title>WebVirtCloud - Spice - Lite</title>
<script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
<script src="{% static "js/spice-html5/enums.js" %}"></script>
<script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
<script src="{% static "js/spice-html5/utils.js" %}"></script>
<script src="{% static "js/spice-html5/png.js" %}"></script>
<script src="{% static "js/spice-html5/lz.js" %}"></script>
<script src="{% static "js/spice-html5/quic.js" %}"></script>
<script src="{% static "js/spice-html5/bitmap.js" %}"></script>
<script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
<script src="{% static "js/spice-html5/spicetype.js" %}"></script>
<script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
<script src="{% static "js/spice-html5/wire.js" %}"></script>
<script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
<script src="{% static "js/spice-html5/display.js" %}"></script>
<script src="{% static "js/spice-html5/main.js" %}"></script>
<script src="{% static "js/spice-html5/inputs.js" %}"></script>
<script src="{% static "js/spice-html5/webm.js" %}"></script>
<script src="{% static "js/spice-html5/playback.js" %}"></script>
<script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
<script src="{% static "js/spice-html5/cursor.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
<script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
<script src="{% static "js/spice-html5/ticket.js" %}"></script>
<script src="{% static "js/spice-html5/resize.js" %}"></script>
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
<script src="{% static "js/spice-html5/port.js" %}"></script>
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
{% endblock %}
{% block content %}
<div id="spice-area">
<div id="spice-screen" class="spice-screen"></div>
<div id="message-div" class="spice-message"></div>
</div>
<div id="debug-div">
<!-- If DUMPXXX is turned on, dumped images will go here -->
</div>
{% endblock %}
{% block foot %}
<!-- ES2015/ES6 modules polyfill -->
<script type="module">
window._spice_has_module_support = true;
</script>
<script>
window.addEventListener("load", function() {
if (window._spice_has_module_support) return;
var loader = document.createElement("script");
loader.src = '{% static "thirdparty/browser-es-module-loader/dist/browser-es-module-loader.js" %}';
document.head.appendChild(loader);
});
</script>
<script type="module" crossorigin="anonymous">
import * as SpiceHtml5 from '{% static "js/spice-html5/main.js" %}';
var host = null, port = null;
var sc;
@ -92,21 +63,18 @@
}
function spice_error(e) {
console.log(e);
disconnect();
if (e.message !== undefined) {
log_error(e.message);
}
else {
log_error('Unknown error');
if (e !== undefined && e.message === "Permission denied.") {
var pass = prompt("Password");
connect(pass);
}
}
function connect() {
var host, port, password, scheme = "ws://", uri;
console.log('>> connect');
function connect(password) {
var host, port, scheme = "ws://", uri;
// By default, use the host and port of server that served this file
//host = spice_query_var('host', window.location.hostname);
// host = spice_query_var('host', window.location.hostname);
host = '{{ ws_host| safe }}';
// Note that using the web server port only makes sense
@ -129,16 +97,18 @@
// If a token variable is passed in, set the parameter in a cookie.
// This is used by nova-spiceproxy.
token = spice_query_var('token', null);
var token = spice_query_var('token', null);
if (token) {
spice_set_cookie('token', token, 1)
}
//password = spice_query_var('password', '');
password = '{{ console_passwd | safe }}';
if (password === undefined) {
password = spice_query_var('password', '');
password = '{{ console_passwd | safe }}';
}
if (password === 'None') password = '';
path = spice_query_var('path', 'websockify');
var path = spice_query_var('path', 'websockify');
if ((!host) || (!port)) {
console.log("must specify host and port in URL");
@ -156,43 +126,44 @@
}
try {
sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
sc = new SpiceHtml5.SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
}
catch (e) {
alert(e.toString());
disconnect();
}
console.log('<< connect')
}
function disconnect()
{
function disconnect() {
console.log(">> disconnect");
if (sc) {
sc.stop();
}
if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.getElementById('spice-xfer-area');
document.getElementById('spice-area').removeChild(spice_xfer_area);
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
if (spice_xfer_area != null) {
document.getElementById('spice-area').removeChild(spice_xfer_area);
}
document.getElementById('spice-area').removeEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').removeEventListener('drop', SpiceHtml5.handle_file_drop, false);
}
console.log("<< disconnect");
}
function agent_connected(sc) {
window.addEventListener('resize', handle_resize);
window.addEventListener('resize', SpiceHtml5.handle_resize);
window.spice_connection = this;
resize_helper(this);
SpiceHtml5.resize_helper(this);
if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.createElement("div");
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
document.getElementById('spice-area').appendChild(spice_xfer_area);
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
document.getElementById('spice-area').addEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
}
else {
console.log("File API is not supported");
@ -224,7 +195,7 @@
});
*/
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
document.getElementById('ctrlaltdel').addEventListener('click', sendCtrlAltDel);
document.getElementById('ctrlaltdel').addEventListener('click', function(){sendCtrlAltDel(sc);});
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
@ -237,6 +208,22 @@
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
connect();
connect(undefined);
</script>
{% endblock %}
{% block content %}
<div id="login">
<span class="logo">SPICE</span>
</div>
<div id="spice-area">
<div id="spice-screen" class="spice-screen"></div>
</div>
<div id="message-div" class="spice-message"></div>
<div id="debug-div">
<!-- If DUMPXXX is turned on, dumped images will go here -->
</div>
{% endblock %}

View file

@ -27,6 +27,7 @@ import os
import socket
import signal
import logging
from functools import reduce
class Tunnel(object):
@ -103,7 +104,7 @@ class Tunnel(object):
logging.debug("Tunnel PID=%d OUTFD=%d ERRFD=%d",
pid, fds[0].fileno(), errorfds[0].fileno())
errorfds[0].setblocking(0)
errorfds[0].setblocking(False)
self.outfd = fds[0]
self.errfd = errorfds[0]

View file

@ -33,7 +33,7 @@ def console(request):
console_type = conn.get_console_type()
console_websocket_port = conn.get_console_websocket_port()
console_passwd = conn.get_console_passwd()
except libvirtError as lib_err:
except libvirtError:
console_type = None
console_websocket_port = None
console_passwd = None

View file

@ -62,4 +62,3 @@ class NewVMForm(forms.Form):
elif len(name) > 64:
raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters'))
return name

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
@ -13,14 +14,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Flavor',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('label', models.CharField(max_length=12)),
('memory', models.IntegerField()),
('vcpu', models.IntegerField()),
('disk', models.IntegerField()),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import migrations

View file

@ -1,11 +1,11 @@
from django.db import models
from django.db.models import Model, CharField, IntegerField
class Flavor(models.Model):
label = models.CharField(max_length=12)
memory = models.IntegerField()
vcpu = models.IntegerField()
disk = models.IntegerField()
class Flavor(Model):
label = CharField(max_length=12)
memory = IntegerField()
vcpu = IntegerField()
disk = IntegerField()
def __unicode__(self):
return self.name

View file

@ -94,7 +94,7 @@
</button>
</form>
</div>
<div class="clearfix"/>
<div class="clearfix"></div>
</div>
</div>
</div>

View file

@ -120,7 +120,7 @@
<div class="col-sm-6">
<select class="form-control" id="select_firmware" name="firmware">
{% for frm in firmwares %}
<option value="{{ frm }}" {% if frm == default_firmware %}selected{% endif %}>{{ frm }}</option>
<option value="{{ frm }}" {% if frm in default_firmware %}selected{% endif %}>{{ frm }}</option>
{% endfor %}
</select>
</div>
@ -792,7 +792,7 @@
$.each(input_value.split(','), function (index, value) {
let li = '<li>eth' + counter +
' -> ' + value + ' ' +
'<a class="btn-link pull-right" onclick="javascript:$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
'<a class="btn-link pull-right" onclick="$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
selected_list_html += li;
counter++;
});

View file

@ -19,9 +19,13 @@ from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_IO
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DISCARD
from webvirtcloud.settings import INSTANCE_ARCH_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_FIRMWARE_DEFAULT_TYPE
from django.contrib import messages
from logs.views import addlogmsg
@login_required
def create_instance_select_type(request, compute_id):
@ -45,8 +49,9 @@ def create_instance_select_type(request, compute_id):
all_hypervisors = conn.get_hypervisors_machines()
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"]
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch ]
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch]
default_machine = INSTANCE_MACHINE_DEFAULT_TYPE
default_arch = INSTANCE_ARCH_DEFAULT_TYPE
if request.method == 'POST':
if 'create_xml' in request.POST:
@ -63,18 +68,21 @@ def create_instance_select_type(request, compute_id):
conn._defineXML(xml)
return HttpResponseRedirect(reverse('instance', args=[compute_id, name]))
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
except libvirtError as lib_err:
error_messages.append(lib_err)
return render(request, 'create_instance_w1.html', locals())
@login_required
def create_instance(request, compute_id, arch, machine):
"""
:param request:
:param compute_id:
:param arch:
:param machine:
:return:
"""
if not request.user.is_superuser:
@ -96,6 +104,7 @@ def create_instance(request, compute_id, arch, machine):
compute.password,
compute.type)
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
instances = conn.get_instances()
videos = conn.get_video_models(arch, machine)
@ -189,7 +198,7 @@ def create_instance(request, compute_id, arch, machine):
volume_list.append(volume)
is_disk_created = True
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
elif data['template']:
templ_path = conn.get_volume_path(data['template'])
dest_vol = conn.get_volume_path(data["name"] + ".img", data['storage'])
@ -221,7 +230,7 @@ def create_instance(request, compute_id, arch, machine):
volume['bus'] = request.POST.get('bus' + str(idx), '')
volume_list.append(volume)
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if data['cache_mode'] not in conn.get_cache_modes():
error_msg = _("Invalid cache mode")
error_messages.append(error_msg)

View file

@ -1,4 +1,4 @@
-r ../conf/requirements.txt
pep8==1.7.1
pycodestyle
pyflakes==2.1.1
pylint==1.9.4
pylint==2.4.4

View file

@ -20,7 +20,7 @@ import os
# range.
#
#bind = 'unix:/srv/webvirtcloud/venv/wvcloud.socket'
# bind = 'unix:/srv/webvirtcloud/venv/wvcloud.socket'
bind = '127.0.0.1:8000'
backlog = 2048

View file

@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('computes', '0001_initial'),
]
@ -14,13 +16,12 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Instance',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=20)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=120)),
('uuid', models.CharField(max_length=36)),
('compute', models.ForeignKey(to='computes.Compute')),
('is_template', models.BooleanField(default=False)),
('created', models.DateField(auto_now_add=True)),
('compute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='computes.Compute')),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('instances', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='instance',
name='is_template',
field=models.BooleanField(default=False),
),
]

View file

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import datetime
class Migration(migrations.Migration):
dependencies = [
('instances', '0002_instance_is_template'),
]
operations = [
migrations.AddField(
model_name='instance',
name='created',
field=models.DateField(default=datetime.datetime(2017, 10, 26, 8, 5, 55, 797326), auto_now_add=True),
preserve_default=False,
),
]

View file

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.13 on 2018-07-24 11:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instances', '0003_instance_created'),
]
operations = [
migrations.AlterField(
model_name='instance',
name='name',
field=models.CharField(max_length=120),
),
]

View file

@ -1,13 +1,13 @@
from django.db import models
from django.db.models import Model, ForeignKey, CharField, BooleanField, DateField, CASCADE
from computes.models import Compute
class Instance(models.Model):
compute = models.ForeignKey(Compute, on_delete=models.CASCADE)
name = models.CharField(max_length=120)
uuid = models.CharField(max_length=36)
is_template = models.BooleanField(default=False)
created = models.DateField(auto_now_add=True)
class Instance(Model):
compute = ForeignKey(Compute, on_delete=CASCADE)
name = CharField(max_length=120)
uuid = CharField(max_length=36)
is_template = BooleanField(default=False)
created = DateField(auto_now_add=True)
def __unicode__(self):
return self.name

View file

@ -48,7 +48,7 @@
<div class="form-group">
<label class="col-sm-4 control-label">{% trans 'Bus' %}</label>
<div class="col-sm-8">
<select class="form-control" name="vol_bus">
<select class="form-control" name="vol_bus" {% if status != 5 %} disabled {% endif %}>
{% for bus in bus_host %}
<option value="{{ bus }}" {% if bus == disk.bus %}selected{% endif %}>{{ bus }}</option>
{% endfor %}
@ -110,6 +110,7 @@
</select>
</div>
</div>
<input class="form-control" name="vol_bus_old" value="{{ disk.bus }}"/>
</div><!-- /.tabpane-content -->
</div><!-- /.tab-content -->
</div> <!-- /.modal-body -->

View file

@ -510,7 +510,7 @@
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_disk">
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p style="font-weight:bold;">{% trans "Disk allocation (B):" %}</p>
<p style="font-weight:bold;">{% trans "Disk allocation (GB):" %}</p>
{% for disk in disks %}
<div class="form-group">
<label class="col-sm-4 control-label" style="font-weight:normal;">{% trans "Current allocation" %} ({{ disk.dev }})</label>
@ -704,25 +704,25 @@
</div>
</form>
<p style="font-weight:bold;">{% trans 'Boot Order' %}</p>
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<div class="col-sm-12 col-sm-offset-2">
{% ifequal status 5 %}
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
{% ifequal bootmenu 0 %}
<input type="submit" class="btn btn-success" name="set_bootmenu" title="Show boot menu" value="{% trans "Enable" %}">
{% else %}
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="Hide boot menu" value="{% trans "Disable" %}">
{% endifequal %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<div class="col-sm-12 col-sm-offset-2">
{% ifequal status 5 %}
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
{% ifequal bootmenu 0 %}
<input type="submit" class="btn btn-success" name="set_bootmenu" title="Show boot menu" value="{% trans "Enable" %}">
{% else %}
{% ifequal bootmenu 0 %}
<p>{% trans "**** Please shutdown instance to modify boot menu ****" %}</p>
{% endifequal %}
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="Hide boot menu" value="{% trans "Disable" %}">
{% endifequal %}
</div>
{% else %}
{% ifequal bootmenu 0 %}
<p>{% trans "**** Please shutdown instance to modify boot menu ****" %}</p>
{% endifequal %}
{% endifequal %}
</div>
</form>
</p>
</div>
</form>
{% ifequal bootmenu 1 %}
<div class="col-sm-6 col-sm-offset-2">
<div class="well">
@ -841,12 +841,14 @@
<div class="col-xs-12 col-sm-12">
<table class="table table-hover">
<thead>
<th>{% trans "Device" %}</th>
<th>{% trans "Used" %}</th>
<th>{% trans "Capacity" %}</th>
<th>{% trans "Storage" %}</th>
<th>{% trans "Source" %}</th>
<th>{% trans "Action" %}</th>
<tr>
<th>{% trans "Device" %}</th>
<th>{% trans "Used" %}</th>
<th>{% trans "Capacity" %}</th>
<th>{% trans "Storage" %}</th>
<th>{% trans "Source" %}</th>
<th>{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for disk in disks %}
@ -1169,7 +1171,7 @@
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
{% endif %}
</form>
<div class="clearfix"></div></p>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
@ -1354,7 +1356,7 @@
</div>
{% else %}
<div class="col-sm-4">
<select id="select_clone_name" class="form-control" name="name" size="1"/>
<select id="select_clone_name" class="form-control" name="name" size="1">
{% for name in clone_free_names %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
@ -1730,7 +1732,7 @@
$.getJSON('{% url 'random_mac_address' %}', function (data) {
$('input[name="' + net + '"]').val(data['mac']);
});
};
}
</script>
<script>
function show_console() {
@ -1765,13 +1767,13 @@
vname = '{{ vname }}';
{% for disk in disks %}
disk_name = '{{ disk.image }}';
disk_dot = disk_name.split('.')
disk_dot = disk_name.split('.');
disk_dot_suffix = disk_dot[disk_dot.length-1];
if (disk_name.startsWith(vname)) {
image = disk_name.replace(vname, new_vname);
} else if (disk_name.lastIndexOf('.') > -1 && disk_dot_suffix.length <= 7) {
disk_dot.pop();
disk_name_only = disk_dot.join('-')
disk_name_only = disk_dot.join('-');
image = new_vname + "-" + disk_name_only + "." + disk_dot_suffix
} else if (new_vname != disk_name) {
image = new_vname
@ -1811,28 +1813,28 @@
});
$(document).ready(function () {
// set current console keymap or fall back to default
var keymap = "{{ console_keymap }}"
var keymap = "{{ console_keymap }}";
if (keymap != '') {
$("#console_select_keymap option[value='" + keymap + "']").prop('selected', true);
}
});
$(document).ready(function () {
// set current console type or fall back to default
var console_type = "{{ console_type }}"
var console_type = "{{ console_type }}";
if (console_type != '') {
$("#console_select_type option[value='" + console_type + "']").prop('selected', true);
}
});
$(document).ready(function () {
// set current console listen address or fall back to default
var console_listen_address = "{{ console_listen_address }}"
var console_listen_address = "{{ console_listen_address }}";
if (console_listen_address != '') {
$("#console_select_listen_address option[value='" + console_listen_address + "']").prop('selected', true);
}
});
$(document).ready(function () {
// get video model or fall back to default
let video_model = "{{ video_model }}"
let video_model = "{{ video_model }}";
if (video_model != '') {
$("#video_model_select option[value='" + video_model + "']").prop('selected', true);
}
@ -2103,7 +2105,7 @@
var netChart = {};
{% for net in networks %}
var net_ctx_{{ forloop.counter0 }} = $("#netEth{{ forloop.counter0 }}Chart").get(0).getContext("2d");
netChart['{{ forloop.counter0 }}'] = new Chart(net_ctx_{{ forloop.counter0 }}, {
netChart[{{ forloop.counter0 }}] = new Chart(net_ctx_{{ forloop.counter0 }}, {
type: 'line',
data: {
datasets : [
@ -2182,8 +2184,8 @@
for (let j = 0; j < data.blkdata.length; j++) {
diskChart[data.blkdata[j].dev].data.labels.push(data.timeline);
diskChart[data.blkdata[j].dev].data.datasets[0].data.push(data.blkdata[0].data[0]);
diskChart[data.blkdata[j].dev].data.datasets[1].data.push(data.blkdata[0].data[1]);
diskChart[data.blkdata[j].dev].data.datasets[0].data.push(data.blkdata[j].data[0]);
diskChart[data.blkdata[j].dev].data.datasets[1].data.push(data.blkdata[j].data[1]);
if (diskChart[data.blkdata[j].dev].data.datasets[0].data.length > 10){
diskChart[data.blkdata[j].dev].data.labels.shift();
diskChart[data.blkdata[j].dev].data.datasets[0].data.shift();
@ -2195,8 +2197,8 @@
for (let j = 0; j < data.netdata.length; j++) {
netChart[data.netdata[j].dev].data.labels.push(data.timeline);
netChart[data.netdata[j].dev].data.datasets[0].data.push(data.netdata[0].data[0]);
netChart[data.netdata[j].dev].data.datasets[1].data.push(data.netdata[0].data[1]);
netChart[data.netdata[j].dev].data.datasets[0].data.push(data.netdata[j].data[0]);
netChart[data.netdata[j].dev].data.datasets[1].data.push(data.netdata[j].data[1]);
if (netChart[data.netdata[j].dev].data.datasets[0].data.length > 10){
netChart[data.netdata[j].dev].data.labels.shift();
netChart[data.netdata[j].dev].data.datasets[0].data.shift();
@ -2211,7 +2213,7 @@
<script>
backgroundJobRunning = false;
window.setInterval(function get_status() {
var status = {{ status }};
var status = {{ status|lower }};
$.getJSON('{% url 'inst_status' compute_id vname %}', function (data) {
if (data['status'] != status && !backgroundJobRunning) {
window.location.reload()

View file

@ -1,14 +1,14 @@
from django.conf.urls import url
from django.urls import path, re_path
from . import views
urlpatterns = [
url(r'^$', views.allinstances, name='allinstances'),
url(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'),
url(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'),
url(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'),
url(r'^guess_mac_address/(?P<vname>[\w\-\.]+)/$', views.guess_mac_address, name='guess_mac_address'),
url(r'^guess_clone_name/$', views.guess_clone_name, name='guess_clone_name'),
url(r'^random_mac_address/$', views.random_mac_address, name='random_mac_address'),
url(r'^check_instance/(?P<vname>[\w\-\.]+)/$', views.check_instance, name='check_instance'),
url(r'^sshkeys/(?P<vname>[\w\-\.]+)/$', views.sshkeys, name='sshkeys'),
path('', views.allinstances, name='allinstances'),
re_path(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'),
re_path(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'),
re_path(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'),
re_path(r'^guess_mac_address/(?P<vname>[\w\-\.]+)/$', views.guess_mac_address, name='guess_mac_address'),
re_path(r'^guess_clone_name/$', views.guess_clone_name, name='guess_clone_name'),
re_path(r'^random_mac_address/$', views.random_mac_address, name='random_mac_address'),
re_path(r'^check_instance/(?P<vname>[\w\-\.]+)/$', views.check_instance, name='check_instance'),
re_path(r'^sshkeys/(?P<vname>[\w\-\.]+)/$', views.sshkeys, name='sshkeys'),
]

View file

@ -63,7 +63,7 @@ def allinstances(request):
return instances_actions(request)
except libvirtError as lib_err:
error_messages.append(lib_err)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err)
view_style = settings.VIEW_INSTANCES_LIST_STYLE
@ -94,7 +94,7 @@ def instances(request, compute_id):
return instances_actions(request)
except libvirtError as lib_err:
error_messages.append(lib_err)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err)
return render(request, 'instances.html', locals())
@ -131,19 +131,19 @@ def instance(request, compute_id, vname):
def filesizefstr(size_str):
if size_str == '':
return 0
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
size_str = size_str.upper().replace("B", "")
if 'K' == size_str[-1]:
return long(float(size_str[:-1])) << 10
return int(float(size_str[:-1])) << 10
elif 'M' == size_str[-1]:
return long(float(size_str[:-1])) << 20
return int(float(size_str[:-1])) << 20
elif 'G' == size_str[-1]:
return long(float(size_str[:-1])) << 30
return int(float(size_str[:-1])) << 30
elif 'T' == size_str[-1]:
return long(float(size_str[:-1])) << 40
return int(float(size_str[:-1])) << 40
elif 'P' == size_str[-1]:
return long(float(size_str[:-1])) << 50
return int(float(size_str[:-1])) << 50
else:
return long(float(size_str))
return int(float(size_str))
def get_clone_free_names(size=10):
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
@ -217,8 +217,8 @@ def instance(request, compute_id, vname):
if media:
existing_media_devs = [m['dev'] for m in media]
for l in string.lowercase:
dev = dev_base + l
for al in string.ascii_lowercase:
dev = dev_base + al
if dev not in existing_disk_devs and dev not in existing_media_devs:
return dev
raise Exception(_('None available device name'))
@ -576,7 +576,7 @@ def instance(request, compute_id, vname):
target_dev = get_new_disk_dev(media, disks, bus)
source = conn_create.create_volume(storage, name, size, format, meta_prealloc, default_owner)
conn.attach_disk(source, target_dev, target_bus=bus, driver_type=format, cache_mode=cache)
conn.attach_disk(target_dev, source, target_bus=bus, driver_type=format, cache_mode=cache)
msg = _('Attach new disk {} ({})'.format(name, format))
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
@ -598,7 +598,7 @@ def instance(request, compute_id, vname):
target_dev = get_new_disk_dev(media, disks, bus)
source = path + "/" + name
conn.attach_disk(source, target_dev, target_bus=bus, driver_type=driver_type, cache_mode=cache)
conn.attach_disk(target_dev, source, target_bus=bus, driver_type=driver_type, cache_mode=cache)
msg = _('Attach Existing disk: ' + target_dev)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
@ -609,16 +609,26 @@ def instance(request, compute_id, vname):
new_path = request.POST.get('vol_path', '')
shareable = bool(request.POST.get('vol_shareable', False))
readonly = bool(request.POST.get('vol_readonly', False))
bus = request.POST.get('vol_bus', '')
disk_type = request.POST.get('vol_type', '')
new_bus = request.POST.get('vol_bus', '')
bus = request.POST.get('vol_bus_old', '')
serial = request.POST.get('vol_serial', '')
format = request.POST.get('vol_format', '')
cache = request.POST.get('vol_cache', default_cache)
io = request.POST.get('vol_io_mode', default_io)
discard = request.POST.get('vol_discard_mode', default_discard)
zeroes = request.POST.get('vol_detect_zeroes', default_zeroes)
new_target_dev = get_new_disk_dev(media, disks, new_bus)
conn.edit_disk(target_dev, new_path, readonly, shareable, bus, serial, format,
cache, io, discard, zeroes)
if new_bus != bus:
conn.detach_disk(target_dev)
conn.attach_disk(new_target_dev, new_path, target_bus=new_bus,
driver_type=format, cache_mode=cache,
readonly=readonly, shareable=shareable, serial=serial,
io_mode=io, discard_mode=discard, detect_zeroes_mode=zeroes)
else:
conn.edit_disk(target_dev, new_path, readonly, shareable, new_bus, serial, format,
cache, io, discard, zeroes)
if not conn.get_status() == 5:
messages.success(request, _("Disk changes changes are applied. " +
@ -646,7 +656,7 @@ def instance(request, compute_id, vname):
try:
conn_delete.del_volume(name)
except libvirtError as err:
msg = _('The disk: ' + dev + ' is detached but not deleted. ' + err.message)
msg = _('The disk: ' + dev + ' is detached but not deleted. ' + err)
messages.warning(request, msg)
addlogmsg(request.user.username, instance.name, msg)
@ -663,7 +673,7 @@ def instance(request, compute_id, vname):
if 'add_cdrom' in request.POST and allow_admin_or_not_template:
bus = request.POST.get('bus', 'ide' if machine == 'pc' else 'sata')
target = get_new_disk_dev(media, disks, bus)
conn.attach_disk("", target, disk_device='cdrom', cache_mode='none', target_bus=bus, readonly=True)
conn.attach_disk(target, "", disk_device='cdrom', cache_mode='none', target_bus=bus, readonly=True)
msg = _('Add CD-ROM: ' + target)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
@ -746,7 +756,7 @@ def instance(request, compute_id, vname):
try:
conn.set_vcpu_hotplug(eval(status))
except libvirtError as lib_err:
messages.error(request, lib_err.message)
messages.error(request, lib_err)
messages.success(request, msg)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#resize')
@ -951,7 +961,7 @@ def instance(request, compute_id, vname):
"Stop and start network to activate new config")
except libvirtError as le:
messages.error(request, le.message)
messages.error(request, le)
return HttpResponseRedirect(request.get_full_path() + '#network')
if 'unset_qos' in request.POST:
qos_dir = request.POST.get('qos_direction', '')
@ -1028,13 +1038,13 @@ def instance(request, compute_id, vname):
error_messages.append(msg)
else:
new_instance = Instance(compute_id=compute_id, name=clone_data['name'])
#new_instance.save()
# new_instance.save()
try:
new_uuid = conn.clone_instance(clone_data)
new_instance.uuid = new_uuid
new_instance.save()
except Exception as e:
#new_instance.delete()
# new_instance.delete()
raise e
user_instance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True)
@ -1064,8 +1074,8 @@ def instance(request, compute_id, vname):
conn.close()
except libvirtError as lib_err:
error_messages.append(lib_err.message)
addlogmsg(request.user.username, vname, lib_err.message)
error_messages.append(lib_err)
addlogmsg(request.user.username, vname, lib_err)
return render(request, 'instance.html', locals())
@ -1152,7 +1162,7 @@ def get_host_instances(request, comp):
status = connection_manager.host_is_up(comp.type, comp.hostname)
if status is True:
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
conn = wvmHostDetails(comp.hostname, comp.login, comp.password, comp.type)
comp_node_info = conn.get_node_info()
comp_mem = conn.get_memory_usage()
comp_instances = conn.get_host_instances(True)

View file

@ -1,29 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# Generated by Django 2.2.10 on 2020-01-28 07:01
from django.db import models, migrations
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instances', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Logs',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user', models.CharField(max_length=50)),
('instance', models.CharField(max_length=50)),
('message', models.CharField(max_length=255)),
('date', models.DateTimeField(auto_now=True)),
('instance', models.ForeignKey(to='instances.Instance')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
},
bases=(models.Model,),
),
]

View file

@ -1,28 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('logs', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='logs',
name='user',
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.AlterField(
model_name='logs',
name='date',
field=models.DateTimeField(auto_now=True),
preserve_default=True,
),
]

View file

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('logs', '0002_auto_20150316_1420'),
]
operations = [
migrations.AlterField(
model_name='logs',
name='instance',
field=models.CharField(max_length=50),
),
migrations.AlterField(
model_name='logs',
name='user',
field=models.CharField(max_length=50),
),
]

View file

@ -1,11 +1,11 @@
from django.db import models
from django.db.models import Model, CharField, DateTimeField
class Logs(models.Model):
user = models.CharField(max_length=50)
instance = models.CharField(max_length=50)
message = models.CharField(max_length=255)
date = models.DateTimeField(auto_now=True)
class Logs(Model):
user = CharField(max_length=50)
instance = CharField(max_length=50)
message = CharField(max_length=255)
date = DateTimeField(auto_now=True)
def __unicode__(self):
return self.instance

View file

@ -1,4 +1,4 @@
<center>
<div class="center-block">
{% if page > 1 %}
<a href="{% url 'showlogspage' page|add:"-1" %}">&larr;</a>
{% else %}
@ -9,4 +9,4 @@
{% else %}
&nbsp;
{% endif %}
</center>
</div>

View file

@ -1,8 +1,8 @@
from django.conf.urls import url
from django.urls import path, re_path
from . import views
urlpatterns = [
url(r'^$', views.showlogs, name='showlogs'),
url(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
url(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
path('', views.showlogs, name='showlogs'),
re_path(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
re_path(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
]

View file

@ -9,7 +9,7 @@ class AddNetPool(forms.Form):
subnet = forms.CharField(error_messages={'required': _('No IPv4 subnet has been entered')},
max_length=20, required=False)
subnet6 = forms.CharField(error_messages={'required': _('No IPv6 subnet has been entered')},
max_length=42, required=False)
max_length=42, required=False)
forward = forms.CharField(max_length=100)
dhcp4 = forms.BooleanField(required=False)
dhcp6 = forms.BooleanField(required=False)

View file

@ -191,6 +191,7 @@
<tbody style="text-align: center">
{% for fix4 in ipv4_fixed_address %}
<tr>
<td>
<form method="post" role="form">{% csrf_token %}
<td><input class="form-control" value="{{ fix4.mac }}" name="mac" readonly/></td>
<td><input class="form-control" value="{{ fix4.ip }}" name="address" /></td>
@ -209,6 +210,7 @@
</button>
</td>
</form>
</td>
</tr>
{% endfor %}
</tbody>

View file

@ -126,31 +126,31 @@ def network(request, compute_id, pool):
conn.start()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'stop' in request.POST:
try:
conn.stop()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'delete' in request.POST:
try:
conn.delete()
return HttpResponseRedirect(reverse('networks', args=[compute_id]))
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'set_autostart' in request.POST:
try:
conn.set_autostart(1)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'unset_autostart' in request.POST:
try:
conn.set_autostart(0)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'modify_fixed_address' in request.POST:
name = request.POST.get('name', '')
address = request.POST.get('address', '')
@ -166,9 +166,9 @@ def network(request, compute_id, pool):
messages.success(request, _("{} Fixed Address Operation Completed.").format(family.upper()))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
except ValueError as val_err:
error_messages.append(val_err.message)
error_messages.append(val_err)
if 'delete_fixed_address' in request.POST:
ip = request.POST.get('address', '')
family = request.POST.get('family', 'ipv4')
@ -184,7 +184,7 @@ def network(request, compute_id, pool):
messages.success(request, _("{} DHCP Range is Changed.").format(family.upper()))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'edit_network' in request.POST:
edit_xml = request.POST.get('edit_xml', '')
if edit_xml:
@ -208,8 +208,8 @@ def network(request, compute_id, pool):
_("Stop and start network to activate new config"))
else:
messages.success(request, _("{} Qos is set").format(qos_dir.capitalize()))
except libvirtError as le:
messages.error(request, le.message)
except libvirtError as lib_err:
messages.error(request, lib_err)
return HttpResponseRedirect(request.get_full_path())
if 'unset_qos' in request.POST:
qos_dir = request.POST.get('qos_direction', '')

View file

@ -95,9 +95,11 @@
<div class="col-xs-12 col-sm-12">
<table class="table table-hover">
<thead>
<th style="width: 45px;">#</th>
<th>{% trans "Reference" %}</th>
<th>{% trans "Action" %}</th>
<tr>
<th style="width: 45px;">#</th>
<th>{% trans "Reference" %}</th>
<th>{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for ref in refs %}
@ -125,6 +127,7 @@
<div class="col-xs-12 col-sm-12">
<table class="table table-hover">
<thead>
<tr>
<th style="width:45px;">{% trans "Rule" %}</th>
<th>{% trans "ActionType" %}</th>
<th>{% trans "Direction" %}</th>
@ -132,6 +135,7 @@
<th>{% trans "Statematch" %}</th>
<th>{% trans "Directives" %}</th>
<th style="width:100px;">{% trans "Action" %}</th>
</tr>
</thead>
<tbody>
{% for rule in rules %}

View file

@ -59,8 +59,8 @@ def nwfilters(request, compute_id):
conn.create_nwfilter(xml)
addlogmsg(request.user.username, compute.hostname, msg)
except libvirtError as lib_err:
error_messages.append(lib_err.message)
addlogmsg(request.user.username, compute.hostname, lib_err.message)
error_messages.append(lib_err)
addlogmsg(request.user.username, compute.hostname, lib_err)
if 'del_nwfilter' in request.POST:
name = request.POST.get('nwfiltername', '')

View file

@ -22,13 +22,13 @@ def secrets(request, compute_id):
secrets_all = []
error_messages = []
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmSecrets(compute.hostname,
compute.login,
compute.password,
compute.type)
secrets = conn.get_secrets()
for uuid in secrets:
secrt = conn.get_secret(uuid)
try:

View file

@ -813,7 +813,7 @@
}
$options.push(this.getOptionByValue(value));
}, this))
}, this));
// Cannot use select or deselect here because it would call updateOptGroups again.
@ -1608,7 +1608,7 @@
* Update opt groups.
*/
updateOptGroups: function() {
var $groups = $('li.multiselect-group', this.$ul)
var $groups = $('li.multiselect-group', this.$ul);
var selectedClass = this.options.selectedClass;
$groups.each(function() {

View file

@ -22,7 +22,7 @@
body {
margin:0;
padding:0;
font-family: Helvetica;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
/*Background image with light grey curve.*/
background-color:#494949;
background-repeat:no-repeat;

File diff suppressed because it is too large Load diff

View file

@ -35,7 +35,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
// throw so it still shows up in the console
throw err;
}
};
var ready = function() {
document.removeEventListener('DOMContentLoaded', ready, false );
@ -63,7 +63,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
}
}
}
}
};
// simple DOM ready
if (document.readyState !== 'loading')
@ -105,10 +105,10 @@ function xhrFetch(url, resolve, reject) {
var xhr = new XMLHttpRequest();
var load = function(source) {
resolve(xhr.responseText);
}
};
var error = function() {
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
}
};
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
@ -235,7 +235,7 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
return new Promise(function(resolve, reject) {
// anonymous module
if (anonSources[key]) {
resolve(anonSources[key])
resolve(anonSources[key]);
anonSources[key] = undefined;
}
// otherwise we fetch

View file

@ -4,7 +4,7 @@ export function shrinkBuf (buf, size) {
if (buf.subarray) { return buf.subarray(0, size); }
buf.length = size;
return buf;
};
}
export function arraySet (dest, src, src_offs, len, dest_offs) {

View file

@ -277,7 +277,7 @@ export default function inflate_fast(strm, start) {
}
else if ((op & 64) === 0) { /* 2nd level distance code */
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist;
continue;
}
else {
strm.msg = 'invalid distance code';
@ -290,7 +290,7 @@ export default function inflate_fast(strm, start) {
}
else if ((op & 64) === 0) { /* 2nd level length code */
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen;
continue;
}
else if (op & 32) { /* end-of-block */
//Tracevv((stderr, "inflate: end of block\n"));
@ -320,5 +320,5 @@ export default function inflate_fast(strm, start) {
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
state.hold = hold;
state.bits = bits;
return;
};

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,750 +0,0 @@
/*
Flot plugin for rendering pie charts. The plugin assumes the data is
coming is as a single data value for each series, and each of those
values is a positive value or zero (negative numbers don't make
any sense and will cause strange effects). The data values do
NOT need to be passed in as percentage values because it
internally calculates the total and percentages.
* Created by Brian Medendorp, June 2009
* Updated November 2009 with contributions from: btburnett3, Anthony Aragues and Xavi Ivars
* Changes:
2009-10-22: lineJoin set to round
2009-10-23: IE full circle fix, donut
2009-11-11: Added basic hover from btburnett3 - does not work in IE, and center is off in Chrome and Opera
2009-11-17: Added IE hover capability submitted by Anthony Aragues
2009-11-18: Added bug fix submitted by Xavi Ivars (issues with arrays when other JS libraries are included as well)
Available options are:
series: {
pie: {
show: true/false
radius: 0-1 for percentage of fullsize, or a specified pixel length, or 'auto'
innerRadius: 0-1 for percentage of fullsize or a specified pixel length, for creating a donut effect
startAngle: 0-2 factor of PI used for starting angle (in radians) i.e 3/2 starts at the top, 0 and 2 have the same result
tilt: 0-1 for percentage to tilt the pie, where 1 is no tilt, and 0 is completely flat (nothing will show)
offset: {
top: integer value to move the pie up or down
left: integer value to move the pie left or right, or 'auto'
},
stroke: {
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#FFF')
width: integer pixel width of the stroke
},
label: {
show: true/false, or 'auto'
formatter: a user-defined function that modifies the text/style of the label text
radius: 0-1 for percentage of fullsize, or a specified pixel length
background: {
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#000')
opacity: 0-1
},
threshold: 0-1 for the percentage value at which to hide labels (if they're too small)
},
combine: {
threshold: 0-1 for the percentage value at which to combine slices (if they're too small)
color: any hexidecimal color value (other formats may or may not work, so best to stick with something like '#CCC'), if null, the plugin will automatically use the color of the first slice to be combined
label: any text value of what the combined slice should be labeled
}
highlight: {
opacity: 0-1
}
}
}
More detail and specific examples can be found in the included HTML file.
*/
(function ($)
{
function init(plot) // this is the "body" of the plugin
{
var canvas = null;
var target = null;
var maxRadius = null;
var centerLeft = null;
var centerTop = null;
var total = 0;
var redraw = true;
var redrawAttempts = 10;
var shrink = 0.95;
var legendWidth = 0;
var processed = false;
var raw = false;
// interactive variables
var highlights = [];
// add hook to determine if pie plugin in enabled, and then perform necessary operations
plot.hooks.processOptions.push(checkPieEnabled);
plot.hooks.bindEvents.push(bindEvents);
// check to see if the pie plugin is enabled
function checkPieEnabled(plot, options)
{
if (options.series.pie.show)
{
//disable grid
options.grid.show = false;
// set labels.show
if (options.series.pie.label.show=='auto')
if (options.legend.show)
options.series.pie.label.show = false;
else
options.series.pie.label.show = true;
// set radius
if (options.series.pie.radius=='auto')
if (options.series.pie.label.show)
options.series.pie.radius = 3/4;
else
options.series.pie.radius = 1;
// ensure sane tilt
if (options.series.pie.tilt>1)
options.series.pie.tilt=1;
if (options.series.pie.tilt<0)
options.series.pie.tilt=0;
// add processData hook to do transformations on the data
plot.hooks.processDatapoints.push(processDatapoints);
plot.hooks.drawOverlay.push(drawOverlay);
// add draw hook
plot.hooks.draw.push(draw);
}
}
// bind hoverable events
function bindEvents(plot, eventHolder)
{
var options = plot.getOptions();
if (options.series.pie.show && options.grid.hoverable)
eventHolder.unbind('mousemove').mousemove(onMouseMove);
if (options.series.pie.show && options.grid.clickable)
eventHolder.unbind('click').click(onClick);
}
// debugging function that prints out an object
function alertObject(obj)
{
var msg = '';
function traverse(obj, depth)
{
if (!depth)
depth = 0;
for (var i = 0; i < obj.length; ++i)
{
for (var j=0; j<depth; j++)
msg += '\t';
if( typeof obj[i] == "object")
{ // its an object
msg += ''+i+':\n';
traverse(obj[i], depth+1);
}
else
{ // its a value
msg += ''+i+': '+obj[i]+'\n';
}
}
}
traverse(obj);
alert(msg);
}
function calcTotal(data)
{
for (var i = 0; i < data.length; ++i)
{
var item = parseFloat(data[i].data[0][1]);
if (item)
total += item;
}
}
function processDatapoints(plot, series, data, datapoints)
{
if (!processed)
{
processed = true;
canvas = plot.getCanvas();
target = $(canvas).parent();
options = plot.getOptions();
plot.setData(combine(plot.getData()));
}
}
function setupPie()
{
legendWidth = target.children().filter('.legend').children().width();
// calculate maximum radius and center point
maxRadius = Math.min(canvas.width,(canvas.height/options.series.pie.tilt))/2;
centerTop = (canvas.height/2)+options.series.pie.offset.top;
centerLeft = (canvas.width/2);
if (options.series.pie.offset.left=='auto')
if (options.legend.position.match('w'))
centerLeft += legendWidth/2;
else
centerLeft -= legendWidth/2;
else
centerLeft += options.series.pie.offset.left;
if (centerLeft<maxRadius)
centerLeft = maxRadius;
else if (centerLeft>canvas.width-maxRadius)
centerLeft = canvas.width-maxRadius;
}
function fixData(data)
{
for (var i = 0; i < data.length; ++i)
{
if (typeof(data[i].data)=='number')
data[i].data = [[1,data[i].data]];
else if (typeof(data[i].data)=='undefined' || typeof(data[i].data[0])=='undefined')
{
if (typeof(data[i].data)!='undefined' && typeof(data[i].data.label)!='undefined')
data[i].label = data[i].data.label; // fix weirdness coming from flot
data[i].data = [[1,0]];
}
}
return data;
}
function combine(data)
{
data = fixData(data);
calcTotal(data);
var combined = 0;
var numCombined = 0;
var color = options.series.pie.combine.color;
var newdata = [];
for (var i = 0; i < data.length; ++i)
{
// make sure its a number
data[i].data[0][1] = parseFloat(data[i].data[0][1]);
if (!data[i].data[0][1])
data[i].data[0][1] = 0;
if (data[i].data[0][1]/total<=options.series.pie.combine.threshold)
{
combined += data[i].data[0][1];
numCombined++;
if (!color)
color = data[i].color;
}
else
{
newdata.push({
data: [[1,data[i].data[0][1]]],
color: data[i].color,
label: data[i].label,
angle: (data[i].data[0][1]*(Math.PI*2))/total,
percent: (data[i].data[0][1]/total*100)
});
}
}
if (numCombined>0)
newdata.push({
data: [[1,combined]],
color: color,
label: options.series.pie.combine.label,
angle: (combined*(Math.PI*2))/total,
percent: (combined/total*100)
});
return newdata;
}
function draw(plot, newCtx)
{
if (!target) return; // if no series were passed
ctx = newCtx;
setupPie();
var slices = plot.getData();
var attempts = 0;
while (redraw && attempts<redrawAttempts)
{
redraw = false;
if (attempts>0)
maxRadius *= shrink;
attempts += 1;
clear();
if (options.series.pie.tilt<=0.8)
drawShadow();
drawPie();
}
if (attempts >= redrawAttempts) {
clear();
target.prepend('<div class="error">Could not draw pie with labels contained inside canvas</div>');
}
if ( plot.setSeries && plot.insertLegend )
{
plot.setSeries(slices);
plot.insertLegend();
}
// we're actually done at this point, just defining internal functions at this point
function clear()
{
ctx.clearRect(0,0,canvas.width,canvas.height);
target.children().filter('.pieLabel, .pieLabelBackground').remove();
}
function drawShadow()
{
var shadowLeft = 5;
var shadowTop = 15;
var edge = 10;
var alpha = 0.02;
// set radius
if (options.series.pie.radius>1)
var radius = options.series.pie.radius;
else
var radius = maxRadius * options.series.pie.radius;
if (radius>=(canvas.width/2)-shadowLeft || radius*options.series.pie.tilt>=(canvas.height/2)-shadowTop || radius<=edge)
return; // shadow would be outside canvas, so don't draw it
ctx.save();
ctx.translate(shadowLeft,shadowTop);
ctx.globalAlpha = alpha;
ctx.fillStyle = '#000';
// center and rotate to starting position
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//radius -= edge;
for (var i=1; i<=edge; i++)
{
ctx.beginPath();
ctx.arc(0,0,radius,0,Math.PI*2,false);
ctx.fill();
radius -= i;
}
ctx.restore();
}
function drawPie()
{
startAngle = Math.PI*options.series.pie.startAngle;
// set radius
if (options.series.pie.radius>1)
var radius = options.series.pie.radius;
else
var radius = maxRadius * options.series.pie.radius;
// center and rotate to starting position
ctx.save();
ctx.translate(centerLeft,centerTop);
ctx.scale(1, options.series.pie.tilt);
//ctx.rotate(startAngle); // start at top; -- This doesn't work properly in Opera
// draw slices
ctx.save();
var currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
{
slices[i].startAngle = currentAngle;
drawSlice(slices[i].angle, slices[i].color, true);
}
ctx.restore();
// draw slice outlines
ctx.save();
ctx.lineWidth = options.series.pie.stroke.width;
currentAngle = startAngle;
for (var i = 0; i < slices.length; ++i)
drawSlice(slices[i].angle, options.series.pie.stroke.color, false);
ctx.restore();
// draw donut hole
drawDonutHole(ctx);
// draw labels
if (options.series.pie.label.show)
drawLabels();
// restore to original state
ctx.restore();
function drawSlice(angle, color, fill)
{
if (angle<=0)
return;
if (fill)
ctx.fillStyle = color;
else
{
ctx.strokeStyle = color;
ctx.lineJoin = 'round';
}
ctx.beginPath();
if (Math.abs(angle - Math.PI*2) > 0.000000001)
ctx.moveTo(0,0); // Center of the pie
else if ($.browser.msie)
angle -= 0.0001;
//ctx.arc(0,0,radius,0,angle,false); // This doesn't work properly in Opera
ctx.arc(0,0,radius,currentAngle,currentAngle+angle,false);
ctx.closePath();
//ctx.rotate(angle); // This doesn't work properly in Opera
currentAngle += angle;
if (fill)
ctx.fill();
else
ctx.stroke();
}
function drawLabels()
{
var currentAngle = startAngle;
// set radius
if (options.series.pie.label.radius>1)
var radius = options.series.pie.label.radius;
else
var radius = maxRadius * options.series.pie.label.radius;
for (var i = 0; i < slices.length; ++i)
{
if (slices[i].percent >= options.series.pie.label.threshold*100)
drawLabel(slices[i], currentAngle, i);
currentAngle += slices[i].angle;
}
function drawLabel(slice, startAngle, index)
{
if (slice.data[0][1]==0)
return;
// format label text
var lf = options.legend.labelFormatter, text, plf = options.series.pie.label.formatter;
if (lf)
text = lf(slice.label, slice);
else
text = slice.label;
if (plf)
text = plf(text, slice);
var halfAngle = ((startAngle+slice.angle) + startAngle)/2;
var x = centerLeft + Math.round(Math.cos(halfAngle) * radius);
var y = centerTop + Math.round(Math.sin(halfAngle) * radius) * options.series.pie.tilt;
var html = '<span class="pieLabel" id="pieLabel'+index+'" style="position:absolute;top:' + y + 'px;left:' + x + 'px;">' + text + "</span>";
target.append(html);
var label = target.children('#pieLabel'+index);
var labelTop = (y - label.height()/2);
var labelLeft = (x - label.width()/2);
label.css('top', labelTop);
label.css('left', labelLeft);
// check to make sure that the label is not outside the canvas
if (0-labelTop>0 || 0-labelLeft>0 || canvas.height-(labelTop+label.height())<0 || canvas.width-(labelLeft+label.width())<0)
redraw = true;
if (options.series.pie.label.background.opacity != 0) {
// put in the transparent background separately to avoid blended labels and label boxes
var c = options.series.pie.label.background.color;
if (c == null) {
c = slice.color;
}
var pos = 'top:'+labelTop+'px;left:'+labelLeft+'px;';
$('<div class="pieLabelBackground" style="position:absolute;width:' + label.width() + 'px;height:' + label.height() + 'px;' + pos +'background-color:' + c + ';"> </div>').insertBefore(label).css('opacity', options.series.pie.label.background.opacity);
}
} // end individual label function
} // end drawLabels function
} // end drawPie function
} // end draw function
// Placed here because it needs to be accessed from multiple locations
function drawDonutHole(layer)
{
// draw donut hole
if(options.series.pie.innerRadius > 0)
{
// subtract the center
layer.save();
innerRadius = options.series.pie.innerRadius > 1 ? options.series.pie.innerRadius : maxRadius * options.series.pie.innerRadius;
layer.globalCompositeOperation = 'destination-out'; // this does not work with excanvas, but it will fall back to using the stroke color
layer.beginPath();
layer.fillStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.fill();
layer.closePath();
layer.restore();
// add inner stroke
layer.save();
layer.beginPath();
layer.strokeStyle = options.series.pie.stroke.color;
layer.arc(0,0,innerRadius,0,Math.PI*2,false);
layer.stroke();
layer.closePath();
layer.restore();
// TODO: add extra shadow inside hole (with a mask) if the pie is tilted.
}
}
//-- Additional Interactive related functions --
function isPointInPoly(poly, pt)
{
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
((poly[i][1] <= pt[1] && pt[1] < poly[j][1]) || (poly[j][1] <= pt[1] && pt[1]< poly[i][1]))
&& (pt[0] < (poly[j][0] - poly[i][0]) * (pt[1] - poly[i][1]) / (poly[j][1] - poly[i][1]) + poly[i][0])
&& (c = !c);
return c;
}
function findNearbySlice(mouseX, mouseY)
{
var slices = plot.getData(),
options = plot.getOptions(),
radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
for (var i = 0; i < slices.length; ++i)
{
var s = slices[i];
if(s.pie.show)
{
ctx.save();
ctx.beginPath();
ctx.moveTo(0,0); // Center of the pie
//ctx.scale(1, options.series.pie.tilt); // this actually seems to break everything when here.
ctx.arc(0,0,radius,s.startAngle,s.startAngle+s.angle,false);
ctx.closePath();
x = mouseX-centerLeft;
y = mouseY-centerTop;
if(ctx.isPointInPath)
{
if (ctx.isPointInPath(mouseX-centerLeft, mouseY-centerTop))
{
//alert('found slice!');
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
}
}
else
{
// excanvas for IE doesn;t support isPointInPath, this is a workaround.
p1X = (radius * Math.cos(s.startAngle));
p1Y = (radius * Math.sin(s.startAngle));
p2X = (radius * Math.cos(s.startAngle+(s.angle/4)));
p2Y = (radius * Math.sin(s.startAngle+(s.angle/4)));
p3X = (radius * Math.cos(s.startAngle+(s.angle/2)));
p3Y = (radius * Math.sin(s.startAngle+(s.angle/2)));
p4X = (radius * Math.cos(s.startAngle+(s.angle/1.5)));
p4Y = (radius * Math.sin(s.startAngle+(s.angle/1.5)));
p5X = (radius * Math.cos(s.startAngle+s.angle));
p5Y = (radius * Math.sin(s.startAngle+s.angle));
arrPoly = [[0,0],[p1X,p1Y],[p2X,p2Y],[p3X,p3Y],[p4X,p4Y],[p5X,p5Y]];
arrPoint = [x,y];
// TODO: perhaps do some mathmatical trickery here with the Y-coordinate to compensate for pie tilt?
if(isPointInPoly(arrPoly, arrPoint))
{
ctx.restore();
return {datapoint: [s.percent, s.data], dataIndex: 0, series: s, seriesIndex: i};
}
}
ctx.restore();
}
}
return null;
}
function onMouseMove(e)
{
triggerClickHoverEvent('plothover', e);
}
function onClick(e)
{
triggerClickHoverEvent('plotclick', e);
}
// trigger click or hover event (they send the same parameters so we share their code)
function triggerClickHoverEvent(eventname, e)
{
var offset = plot.offset(),
canvasX = parseInt(e.pageX - offset.left),
canvasY = parseInt(e.pageY - offset.top),
item = findNearbySlice(canvasX, canvasY);
if (options.grid.autoHighlight)
{
// clear auto-highlights
for (var i = 0; i < highlights.length; ++i)
{
var h = highlights[i];
if (h.auto == eventname && !(item && h.series == item.series))
unhighlight(h.series);
}
}
// highlight the slice
if (item)
highlight(item.series, eventname);
// trigger any hover bind events
var pos = { pageX: e.pageX, pageY: e.pageY };
target.trigger(eventname, [ pos, item ]);
}
function highlight(s, auto)
{
if (typeof s == "number")
s = series[s];
var i = indexOfHighlight(s);
if (i == -1)
{
highlights.push({ series: s, auto: auto });
plot.triggerRedrawOverlay();
}
else if (!auto)
highlights[i].auto = false;
}
function unhighlight(s)
{
if (s == null)
{
highlights = [];
plot.triggerRedrawOverlay();
}
if (typeof s == "number")
s = series[s];
var i = indexOfHighlight(s);
if (i != -1)
{
highlights.splice(i, 1);
plot.triggerRedrawOverlay();
}
}
function indexOfHighlight(s)
{
for (var i = 0; i < highlights.length; ++i)
{
var h = highlights[i];
if (h.series == s)
return i;
}
return -1;
}
function drawOverlay(plot, octx)
{
//alert(options.series.pie.radius);
var options = plot.getOptions();
//alert(options.series.pie.radius);
var radius = options.series.pie.radius > 1 ? options.series.pie.radius : maxRadius * options.series.pie.radius;
octx.save();
octx.translate(centerLeft, centerTop);
octx.scale(1, options.series.pie.tilt);
for (i = 0; i < highlights.length; ++i)
drawHighlight(highlights[i].series);
drawDonutHole(octx);
octx.restore();
function drawHighlight(series)
{
if (series.angle < 0) return;
//octx.fillStyle = parseColor(options.series.pie.highlight.color).scale(null, null, null, options.series.pie.highlight.opacity).toString();
octx.fillStyle = "rgba(255, 255, 255, "+options.series.pie.highlight.opacity+")"; // this is temporary until we have access to parseColor
octx.beginPath();
if (Math.abs(series.angle - Math.PI*2) > 0.000000001)
octx.moveTo(0,0); // Center of the pie
octx.arc(0,0,radius,series.startAngle,series.startAngle+series.angle,false);
octx.closePath();
octx.fill();
}
}
} // end init (plugin body)
// define pie specific options and their default values
var options = {
series: {
pie: {
show: false,
radius: 'auto', // actual radius of the visible pie (based on full calculated radius if <=1, or hard pixel value)
innerRadius:0, /* for donut */
startAngle: 3/2,
tilt: 1,
offset: {
top: 0,
left: 'auto'
},
stroke: {
color: '#FFF',
width: 1
},
label: {
show: 'auto',
formatter: function(label, slice){
return '<div style="font-size:x-small;text-align:center;padding:2px;color:'+slice.color+';">'+label+'<br/>'+Math.round(slice.percent)+'%</div>';
}, // formatter function
radius: 1, // radius at which to place the labels (based on full calculated radius if <=1, or hard pixel value)
background: {
color: null,
opacity: 0
},
threshold: 0 // percentage at which to hide the label (i.e. the slice is too narrow)
},
combine: {
threshold: -1, // percentage at which to combine little slices into one larger slice
color: null, // color to give the new slice (auto-generated if null)
label: 'Other' // label to give the new slice
},
highlight: {
//color: '#FFF', // will add this functionality once parseColor is available
opacity: 0.5
}
}
}
};
$.plot.plugins.push({
init: init,
options: options,
name: "pie",
version: "1.0"
});
})(jQuery);

View file

@ -1,60 +0,0 @@
/* Flot plugin for automatically redrawing plots as the placeholder resizes.
Copyright (c) 2007-2013 IOLA and Ole Laursen.
Licensed under the MIT license.
It works by listening for changes on the placeholder div (through the jQuery
resize event plugin) - if the size changes, it will redraw the plot.
There are no options. If you need to disable the plugin for some plots, you
can just fix the size of their placeholders.
*/
/* Inline dependency:
* jQuery resize event - v1.1 - 3/14/2010
* http://benalman.com/projects/jquery-resize-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,h,c){var a=$([]),e=$.resize=$.extend($.resize,{}),i,k="setTimeout",j="resize",d=j+"-special-event",b="delay",f="throttleWindow";e[b]=250;e[f]=true;$.event.special[j]={setup:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.add(l);$.data(this,d,{w:l.width(),h:l.height()});if(a.length===1){g()}},teardown:function(){if(!e[f]&&this[k]){return false}var l=$(this);a=a.not(l);l.removeData(d);if(!a.length){clearTimeout(i)}},add:function(l){if(!e[f]&&this[k]){return false}var n;function m(s,o,p){var q=$(this),r=$.data(this,d);r.w=o!==c?o:q.width();r.h=p!==c?p:q.height();n.apply(this,arguments)}if($.isFunction(l)){n=l;return m}else{n=l.handler;l.handler=m}}};function g(){i=h[k](function(){a.each(function(){var n=$(this),m=n.width(),l=n.height(),o=$.data(this,d);if(m!==o.w||l!==o.h){n.trigger(j,[o.w=m,o.h=l])}});g()},e[b])}})(jQuery,this);
(function ($) {
var options = { }; // no options
function init(plot) {
function onResize() {
var placeholder = plot.getPlaceholder();
// somebody might have hidden us and we can't plot
// when we don't have the dimensions
if (placeholder.width() == 0 || placeholder.height() == 0)
return;
plot.resize();
plot.setupGrid();
plot.draw();
}
function bindEvents(plot, eventHolder) {
plot.getPlaceholder().resize(onResize);
}
function shutdown(plot, eventHolder) {
plot.getPlaceholder().unbind("resize", onResize);
}
plot.hooks.bindEvents.push(bindEvents);
plot.hooks.shutdown.push(shutdown);
}
$.plot.plugins.push({
init: init,
options: options,
name: 'resize',
version: '1.0'
});
})(jQuery);

View file

@ -1,12 +0,0 @@
/*
* jquery.flot.tooltip
*
* description: easy-to-use tooltips for Flot charts
* version: 0.6.2
* author: Krzysztof Urbas @krzysu [myviews.pl]
* website: https://github.com/krzysu/flot.tooltip
*
* build on 2013-09-30
* released under MIT License, 2012
*/
(function(t){var o={tooltip:!1,tooltipOpts:{content:"%s | X: %x | Y: %y",xDateFormat:null,yDateFormat:null,shifts:{x:10,y:20},defaultTheme:!0,onHover:function(){}}},i=function(t){this.tipPosition={x:0,y:0},this.init(t)};i.prototype.init=function(o){function i(t){var o={};o.x=t.pageX,o.y=t.pageY,s.updateTooltipPosition(o)}function e(t,o,i){var e=s.getDomElement();if(i){var n;n=s.stringFormat(s.tooltipOptions.content,i),e.html(n),s.updateTooltipPosition({x:o.pageX,y:o.pageY}),e.css({left:s.tipPosition.x+s.tooltipOptions.shifts.x,top:s.tipPosition.y+s.tooltipOptions.shifts.y}).show(),"function"==typeof s.tooltipOptions.onHover&&s.tooltipOptions.onHover(i,e)}else e.hide().html("")}var s=this;o.hooks.bindEvents.push(function(o,n){s.plotOptions=o.getOptions(),s.plotOptions.tooltip!==!1&&void 0!==s.plotOptions.tooltip&&(s.tooltipOptions=s.plotOptions.tooltipOpts,s.getDomElement(),t(o.getPlaceholder()).bind("plothover",e),t(n).bind("mousemove",i))}),o.hooks.shutdown.push(function(o,s){t(o.getPlaceholder()).unbind("plothover",e),t(s).unbind("mousemove",i)})},i.prototype.getDomElement=function(){var o;return t("#flotTip").length>0?o=t("#flotTip"):(o=t("<div />").attr("id","flotTip"),o.appendTo("body").hide().css({position:"absolute"}),this.tooltipOptions.defaultTheme&&o.css({background:"#fff","z-index":"100",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111",display:"none","white-space":"nowrap"})),o},i.prototype.updateTooltipPosition=function(o){var i=t("#flotTip").outerWidth()+this.tooltipOptions.shifts.x,e=t("#flotTip").outerHeight()+this.tooltipOptions.shifts.y;o.x-t(window).scrollLeft()>t(window).innerWidth()-i&&(o.x-=i),o.y-t(window).scrollTop()>t(window).innerHeight()-e&&(o.y-=e),this.tipPosition.x=o.x,this.tipPosition.y=o.y},i.prototype.stringFormat=function(t,o){var i=/%p\.{0,1}(\d{0,})/,e=/%s/,s=/%x\.{0,1}(?:\d{0,})/,n=/%y\.{0,1}(?:\d{0,})/;return"function"==typeof t&&(t=t(o.series.label,o.series.data[o.dataIndex][0],o.series.data[o.dataIndex][1],o)),o.series.percent!==void 0&&(t=this.adjustValPrecision(i,t,o.series.percent)),o.series.label!==void 0&&(t=t.replace(e,o.series.label)),this.isTimeMode("xaxis",o)&&this.isXDateFormat(o)&&(t=t.replace(s,this.timestampToDate(o.series.data[o.dataIndex][0],this.tooltipOptions.xDateFormat))),this.isTimeMode("yaxis",o)&&this.isYDateFormat(o)&&(t=t.replace(n,this.timestampToDate(o.series.data[o.dataIndex][1],this.tooltipOptions.yDateFormat))),"number"==typeof o.series.data[o.dataIndex][0]&&(t=this.adjustValPrecision(s,t,o.series.data[o.dataIndex][0])),"number"==typeof o.series.data[o.dataIndex][1]&&(t=this.adjustValPrecision(n,t,o.series.data[o.dataIndex][1])),o.series.xaxis.tickFormatter!==void 0&&(t=t.replace(s,o.series.xaxis.tickFormatter(o.series.data[o.dataIndex][0],o.series.xaxis))),o.series.yaxis.tickFormatter!==void 0&&(t=t.replace(n,o.series.yaxis.tickFormatter(o.series.data[o.dataIndex][1],o.series.yaxis))),t},i.prototype.isTimeMode=function(t,o){return o.series[t].options.mode!==void 0&&"time"===o.series[t].options.mode},i.prototype.isXDateFormat=function(){return this.tooltipOptions.xDateFormat!==void 0&&null!==this.tooltipOptions.xDateFormat},i.prototype.isYDateFormat=function(){return this.tooltipOptions.yDateFormat!==void 0&&null!==this.tooltipOptions.yDateFormat},i.prototype.timestampToDate=function(o,i){var e=new Date(o);return t.plot.formatDate(e,i)},i.prototype.adjustValPrecision=function(t,o,i){var e,s=o.match(t);return null!==s&&""!==RegExp.$1&&(e=RegExp.$1,i=i.toFixed(e),o=o.replace(t,i)),o};var e=function(t){new i(t)};t.plot.plugins.push({init:e,options:o,name:"tooltip",version:"0.6.1"})})(jQuery);

View file

@ -36,7 +36,6 @@ body
#login input
{
padding: 5px;
background-color: #fAfAfA;
border: 1px inset #999999;
outline: none;
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
@ -81,7 +80,7 @@ body
{
min-height: 600px;
height: 100%;
margin: 62px 10px 10px 10px;
margin: 10px;
padding: 0;
background-color: #333333;
}
@ -104,6 +103,8 @@ body
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
/* We default the message box to hidden. */
display: none;
}
.spice-message p {
margin-bottom: 0em;

View file

@ -105,7 +105,7 @@ def storage(request, compute_id, pool):
size, free = conn.get_size()
used = (size - free)
if state:
percent = (used * 100) / size
percent = (used * 100) // size
else:
percent = 0
status = conn.get_status()
@ -127,31 +127,31 @@ def storage(request, compute_id, pool):
conn.start()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'stop' in request.POST:
try:
conn.stop()
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'delete' in request.POST:
try:
conn.delete()
return HttpResponseRedirect(reverse('storages', args=[compute_id]))
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'set_autostart' in request.POST:
try:
conn.set_autostart(1)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'unset_autostart' in request.POST:
try:
conn.set_autostart(0)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'add_volume' in request.POST:
form = AddImage(request.POST)
if form.is_valid():
@ -175,7 +175,7 @@ def storage(request, compute_id, pool):
messages.success(request, _('Volume: {} is deleted.'.format(volname)))
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err:
error_messages.append(lib_err.message)
error_messages.append(lib_err)
if 'iso_upload' in request.POST:
if str(request.FILES['file']) in conn.update_volumes():
error_msg = _("ISO image already exist")

View file

@ -80,7 +80,7 @@ class wvmConnection(object):
elif self.type == CONN_SOCKET:
self.__connect_socket()
else:
raise ValueError('"{type}" is not a valid connection type'.format(type=self.type))
raise ValueError(f'"{self.type}" is not a valid connection type')
if self.connected:
# do some preprocessing of the connection:
@ -135,14 +135,14 @@ class wvmConnection(object):
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
uri = f'qemu+tcp://{self.host}/system'
try:
self.connection = libvirt.openAuth(uri, auth, 0)
self.last_error = None
except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e)
self.last_error = f'Connection Failed: {str(e)}'
self.connection = None
def __connect_ssh(self):
@ -153,7 +153,7 @@ class wvmConnection(object):
self.last_error = None
except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e) + ' --- ' + repr(libvirt.virGetLastError())
self.last_error = f'Connection Failed: {str(e)} --- ' + repr(libvirt.virGetLastError())
self.connection = None
def __connect_tls(self):
@ -166,7 +166,7 @@ class wvmConnection(object):
self.last_error = None
except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e)
self.last_error = f'Connection Failed: {str(e)}'
self.connection = None
def __connect_socket(self):
@ -177,7 +177,7 @@ class wvmConnection(object):
self.last_error = None
except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e)
self.last_error = f'Connection Failed: {str(e)}'
self.connection = None
def close(self):
@ -208,18 +208,18 @@ class wvmConnection(object):
def __unicode__(self):
if self.type == CONN_TCP:
type_str = u'tcp'
type_str = 'tcp'
elif self.type == CONN_SSH:
type_str = u'ssh'
type_str = 'ssh'
elif self.type == CONN_TLS:
type_str = u'tls'
type_str = 'tls'
else:
type_str = u'invalid_type'
type_str = 'invalid_type'
return u'qemu+{type}://{user}@{host}/system'.format(type=type_str, user=self.login, host=self.host)
return f'qemu+{type_str}://{self.login}@{self.host}/system'
def __repr__(self):
return '<wvmConnection {connection_str}>'.format(connection_str=unicode(self))
return f'<wvmConnection {str(self)}>'
class wvmConnectionManager(object):
@ -264,9 +264,9 @@ class wvmConnectionManager(object):
raises libvirtError if (re)connecting fails
"""
# force all string values to unicode
host = unicode(host)
login = unicode(login)
passwd = unicode(passwd) if passwd is not None else None
host = str(host)
login = str(login)
passwd = str(passwd) if passwd is not None else None
connection = self._search_connection(host, login, passwd, conn)
@ -432,21 +432,21 @@ class wvmConnect(object):
def get_version(self):
ver = self.wvm.getVersion()
major = ver / 1000000
major = ver // 1000000
ver = ver % 1000000
minor = ver / 1000
minor = ver // 1000
ver = ver % 1000
release = ver
return "%s.%s.%s" % (major, minor, release)
return f"{major}.{minor}.{release}"
def get_lib_version(self):
ver = self.wvm.getLibVersion()
major = ver / 1000000
major = ver // 1000000
ver %= 1000000
minor = ver / 1000
minor = ver // 1000
ver %= 1000
release = ver
return "%s.%s.%s" % (major,minor,release)
return f"{major}.{minor}.{release}"
def is_kvm_supported(self):
"""Return KVM capabilities."""

View file

@ -6,9 +6,6 @@ from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
def get_rbd_storage_data(stg):
xml = stg.XMLDesc(0)
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
@ -236,10 +233,10 @@ class wvmCreate(wvmConnect):
"""
xml += """<devices>"""
vd_disk_letters = list(string.lowercase)
fd_disk_letters = list(string.lowercase)
hd_disk_letters = list(string.lowercase)
sd_disk_letters = list(string.lowercase)
vd_disk_letters = list(string.ascii_lowercase)
fd_disk_letters = list(string.ascii_lowercase)
hd_disk_letters = list(string.ascii_lowercase)
sd_disk_letters = list(string.ascii_lowercase)
add_cd = True
disk_opts = ''

View file

@ -19,10 +19,10 @@ class wvmHostDetails(wvmConnect):
all_mem = self.wvm.getInfo()[1] * 1048576
freemem = self.wvm.getMemoryStats(-1, 0)
if type(freemem) == dict:
free = (freemem.values()[0] +
freemem.values()[2] +
freemem.values()[3]) * 1024
percent = (100 - ((free * 100) / all_mem))
free = (freemem['buffers'] +
freemem['free'] +
freemem['cached']) * 1024
percent = abs(100 - ((free * 100) // all_mem))
usage = (all_mem - free)
mem_usage = {'total': all_mem, 'usage': usage, 'percent': percent}
else:
@ -38,7 +38,7 @@ class wvmHostDetails(wvmConnect):
cpu = self.wvm.getCPUStats(-1, 0)
if type(cpu) == dict:
for num in range(2):
idle = self.wvm.getCPUStats(-1, 0).values()[1]
idle = self.wvm.getCPUStats(-1, 0)['idle']
total = sum(self.wvm.getCPUStats(-1, 0).values())
diff_idle = idle - prev_idle
diff_total = total - prev_total

View file

@ -275,7 +275,7 @@ class wvmInstance(wvmConnect):
"""Get number of physical CPUs."""
hostinfo = self.wvm.getInfo()
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
range_pcpus = xrange(1, int(pcpus + 1))
range_pcpus = range(1, int(pcpus + 1))
return range_pcpus
def get_interface_addresses(self, iface_mac):
@ -322,11 +322,11 @@ class wvmInstance(wvmConnect):
return None, None
def _get_interface_addresses(self, source):
#("Calling interfaceAddresses source=%s", source)
# ("Calling interfaceAddresses source=%s", source)
try:
return self.instance.interfaceAddresses(source)
except Exception as e:
#log.debug("interfaceAddresses failed: %s", str(e))
# log.debug("interfaceAddresses failed: %s", str(e))
pass
return {}
@ -340,7 +340,7 @@ class wvmInstance(wvmConnect):
self._ip_cache["qemuga"] = self._get_interface_addresses(
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
arp_flag = 3 # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
arp_flag = 3 # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
self._ip_cache["arp"] = self._get_interface_addresses(arp_flag)
def get_net_devices(self):
@ -501,7 +501,7 @@ class wvmInstance(wvmConnect):
else:
raise Exception('Unknown boot menu option, please choose one of 0:disable, 1:enable, -1:remove')
xmldom = ElementTree.tostring(tree)
xmldom = ElementTree.tostring(tree).decode()
self._defineXML(xmldom)
def get_bootorder(self):
@ -592,7 +592,7 @@ class wvmInstance(wvmConnect):
d.append(order)
else:
raise Exception('Invalid Device Type for boot order')
self._defineXML(ElementTree.tostring(tree))
self._defineXML(ElementTree.tostring(tree).decode())
def mount_iso(self, dev, image):
def attach_iso(dev, disk, vol):
@ -618,11 +618,11 @@ class wvmInstance(wvmConnect):
if attach_iso(dev, disk, vol):
break
if self.get_status() == 1:
xml = ElementTree.tostring(disk)
xml = ElementTree.tostring(disk).decode()
self.instance.attachDevice(xml)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree)
xmldom = ElementTree.tostring(tree).decode()
self._defineXML(xmldom)
def umount_iso(self, dev, image):
@ -637,14 +637,14 @@ class wvmInstance(wvmConnect):
if elm.get('dev') == dev:
disk.remove(src_media)
if self.get_status() == 1:
xml_disk = ElementTree.tostring(disk)
xml_disk = ElementTree.tostring(disk).decode()
self.instance.attachDevice(xml_disk)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree)
xmldom = ElementTree.tostring(tree).decode()
self._defineXML(xmldom)
def attach_disk(self, source, target_dev, target_bus='ide', disk_type='file',
def attach_disk(self, target_dev, source, target_bus='ide', disk_type='file',
disk_device='disk', driver_name='qemu', driver_type='raw',
readonly=False, shareable=False, serial=None,
cache_mode=None, io_mode=None, discard_mode=None, detect_zeroes_mode=None):
@ -683,7 +683,7 @@ class wvmInstance(wvmConnect):
tree = etree.fromstring(self._XMLDesc(0))
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[0].getparent()
xml_disk = etree.tostring(disk_el)
xml_disk = etree.tostring(disk_el).decode()
devices = tree.find('devices')
devices.remove(disk_el)
@ -699,6 +699,7 @@ class wvmInstance(wvmConnect):
old_disk_type = disk_el.get('type')
old_disk_device = disk_el.get('device')
old_driver_name = disk_el.xpath('driver/@name')[0]
old_target_bus = disk_el.xpath('target/@bus')[0]
additionals = ''
if cache_mode is not None and cache_mode != 'default':
@ -715,6 +716,7 @@ class wvmInstance(wvmConnect):
xml_disk += "<driver name='%s' type='%s'/>" % (old_driver_name, format)
elif old_disk_device == 'disk':
xml_disk += "<driver name='%s' type='%s' %s/>" % (old_driver_name, format, additionals)
xml_disk += """<source file='%s'/>
<target dev='%s' bus='%s'/>""" % (source, target_dev, target_bus)
if readonly:
@ -735,7 +737,7 @@ class wvmInstance(wvmConnect):
time.sleep(1)
cpu_use_now = self.instance.info()[4]
diff_usage = cpu_use_now - cpu_use_ago
cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10 ** 9L)
cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10 ** 9)
else:
cpu_usage['cpu'] = 0
return cpu_usage
@ -746,7 +748,7 @@ class wvmInstance(wvmConnect):
def set_vcpu_hotplug(self, status, vcpus_hotplug=0):
""" vcpus_hotplug = 0 make all vpus hotpluggable """
vcpus_hotplug = int(self.get_vcpu()) if vcpus_hotplug == 0 else vcpus_hotplug
if self.get_status() == 5: # shutoff
if self.get_status() == 5: # shutoff
if status:
xml = """ <vcpus>"""
xml += """<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>"""
@ -758,14 +760,14 @@ class wvmInstance(wvmConnect):
vcpus = tree.xpath("/domain/vcpus")
if not vcpus:
tree.append(etree.fromstring(xml))
self._defineXML(etree.tostring(tree))
self._defineXML(etree.tostring(tree).decode())
else:
tree = etree.fromstring(self._XMLDesc(0))
vcpus = tree.xpath("/domain/vcpus")
for vcpu in vcpus:
parent = vcpu.getparent()
parent.remove(vcpu)
self._defineXML(etree.tostring(tree))
self._defineXML(etree.tostring(tree).decode())
else:
raise libvirtError("Please shutdown the instance then try to enable vCPU hotplug")
@ -892,7 +894,7 @@ class wvmInstance(wvmConnect):
listen.attrib.pop("address")
except:
pass
newxml = ElementTree.tostring(root)
newxml = ElementTree.tostring(root).decode()
return self._defineXML(newxml)
def get_console_socket(self):
@ -917,7 +919,7 @@ class wvmInstance(wvmConnect):
# Little fix for old version ElementTree
graphic = root.find("devices/graphics")
graphic.set('type', console_type)
newxml = ElementTree.tostring(root)
newxml = ElementTree.tostring(root).decode()
self._defineXML(newxml)
def get_console_port(self, console_type=None):
@ -953,7 +955,7 @@ class wvmInstance(wvmConnect):
graphic.attrib.pop('passwd')
except:
pass
newxml = ElementTree.tostring(root)
newxml = ElementTree.tostring(root).decode()
return self._defineXML(newxml)
def set_console_keymap(self, keymap):
@ -972,7 +974,7 @@ class wvmInstance(wvmConnect):
graphic.attrib.pop('keymap')
except:
pass
newxml = ElementTree.tostring(root)
newxml = ElementTree.tostring(root).decode()
self._defineXML(newxml)
def get_console_keymap(self):
@ -998,7 +1000,7 @@ class wvmInstance(wvmConnect):
parent = model.getparent()
parent.remove(model)
parent.append(etree.fromstring(video_xml))
self._defineXML(etree.tostring(tree))
self._defineXML(etree.tostring(tree).decode())
def resize_cpu(self, cur_vcpu, vcpu):
"""
@ -1011,11 +1013,11 @@ class wvmInstance(wvmConnect):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = etree.fromstring(xml)
set_vcpu = tree.find('vcpu')
set_vcpu.text = vcpu
set_vcpu.set('current', cur_vcpu)
vcpu_elem = tree.find('vcpu')
vcpu_elem.text = vcpu
vcpu_elem.set('current', cur_vcpu)
new_xml = etree.tostring(tree)
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
if is_vcpus_enabled:
@ -1034,14 +1036,14 @@ class wvmInstance(wvmConnect):
return
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
tree = etree.fromstring(xml)
set_mem = tree.find('memory')
set_mem.text = str(memory)
set_cur_mem = tree.find('currentMemory')
set_cur_mem.text = str(cur_memory)
mem_elem = tree.find('memory')
mem_elem.text = str(memory)
cur_mem_elem = tree.find('currentMemory')
cur_mem_elem.text = str(cur_memory)
new_xml = ElementTree.tostring(tree)
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
def resize_disk(self, disks):
@ -1049,14 +1051,14 @@ class wvmInstance(wvmConnect):
Function change disks on vds.
"""
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
tree = etree.fromstring(xml)
for disk in disks:
source_dev = disk['path']
vol = self.get_volume_by_path(source_dev)
vol.resize(disk['size_new'])
new_xml = ElementTree.tostring(tree)
new_xml = etree.tostring(tree).decode()
self._defineXML(new_xml)
def get_iso_media(self):
@ -1250,7 +1252,7 @@ class wvmInstance(wvmConnect):
'description': clone_data.get('clone-description', ''),
}
self._set_options(tree, options)
self._defineXML(ElementTree.tostring(tree))
self._defineXML(ElementTree.tostring(tree).decode())
return self.get_instance(clone_data['name']).UUIDString()
@ -1297,7 +1299,7 @@ class wvmInstance(wvmConnect):
for interface in tree.findall('devices/interface'):
source = interface.find('mac')
if source.get('address', '') == mac_address:
new_xml = ElementTree.tostring(interface)
new_xml = ElementTree.tostring(interface).decode()
if self.get_status() == 1:
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
@ -1359,7 +1361,7 @@ class wvmInstance(wvmConnect):
else:
if source is not None: interface.remove(source)
new_xml = ElementTree.tostring(tree)
new_xml = ElementTree.tostring(tree).decode()
self._defineXML(new_xml)
def set_link_state(self, mac_address, state):
@ -1373,7 +1375,7 @@ class wvmInstance(wvmConnect):
link_el = etree.Element("link")
link_el.attrib["state"] = state
interface.append(link_el)
new_xml = etree.tostring(interface)
new_xml = etree.tostring(interface).decode()
if self.get_status() == 1:
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
@ -1400,7 +1402,7 @@ class wvmInstance(wvmConnect):
tree = ElementTree.fromstring(xml)
self._set_options(tree, options)
new_xml = ElementTree.tostring(tree)
new_xml = ElementTree.tostring(tree).decode()
self._defineXML(new_xml)
def set_memory(self, size, flags=0):
@ -1460,7 +1462,7 @@ class wvmInstance(wvmConnect):
parent.append(etree.fromstring(xml))
else:
band.append(etree.fromstring(xml))
new_xml = etree.tostring(tree)
new_xml = etree.tostring(tree).decode()
self.wvm.defineXML(new_xml)
def unset_qos(self, mac, direction):
@ -1472,7 +1474,7 @@ class wvmInstance(wvmConnect):
if parent_mac[0] == mac:
band_el.remove(direct)
self.wvm.defineXML(etree.tostring(tree))
self.wvm.defineXML(etree.tostring(tree).decode())
def add_guest_agent(self):
channel_xml = """
@ -1490,7 +1492,7 @@ class wvmInstance(wvmConnect):
tree = etree.fromstring(self._XMLDesc(0))
for target in tree.xpath("/domain/devices/channel[@type='unix']/target[@name='org.qemu.guest_agent.0']"):
parent = target.getparent()
channel_xml = etree.tostring(parent)
channel_xml = etree.tostring(parent).decode()
if self.get_status() == 1:
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_LIVE)
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)

View file

@ -213,7 +213,7 @@ class wvmNetwork(wvmConnect):
for host in hosts:
ip = host.get('ip')
mac = host.get('mac')
name = host.get('name','')
name = host.get('name', '')
result.append({'ip': ip, 'mac': mac, 'name': name})
return result
else:
@ -223,7 +223,7 @@ class wvmNetwork(wvmConnect):
for host in hosts:
ip = host.get('ip')
id = host.get('id')
name = host.get('name','')
name = host.get('name', '')
result.append({'ip': ip, 'id': id, 'name': name})
return result
@ -236,7 +236,7 @@ class wvmNetwork(wvmConnect):
range = tree.xpath("./ip[@family='ipv6']/dhcp/range")
range[0].set('start', range_start)
range[0].set('end', range_end)
self.wvm.networkDefineXML(etree.tostring(tree))
self.wvm.networkDefineXML(etree.tostring(tree).decode())
def delete_fixed_address(self, ip, family='ipv4'):
tree = etree.fromstring(self._XMLDesc(0))
@ -335,7 +335,7 @@ class wvmNetwork(wvmConnect):
parent.append(etree.fromstring(xml))
else:
band[0].append(etree.fromstring(xml))
new_xml = etree.tostring(tree)
new_xml = etree.tostring(tree).decode()
self.wvm.networkDefineXML(new_xml)
def unset_qos(self, direction):
@ -344,7 +344,7 @@ class wvmNetwork(wvmConnect):
parent = direct.getparent()
parent.remove(direct)
self.wvm.networkDefineXML(etree.tostring(tree))
self.wvm.networkDefineXML(etree.tostring(tree).decode())
def edit_network(self, new_xml):
self.wvm.networkDefineXML(new_xml)
@ -354,7 +354,7 @@ class wvmNetwork(wvmConnect):
self.leases = self.net.DHCPLeases()
except Exception as e:
self.leases = []
raise "Error getting %s DHCP leases: %s" % self, str(e)
raise "Error getting %s DHCP leases: %s" % (self, e)
def get_dhcp_leases(self):
if self.leases is None:

View file

@ -19,7 +19,7 @@ class wvmNWFilters(wvmConnect):
tree.set('name', cln_name)
uuid = tree.find('uuid')
tree.remove(uuid)
self.create_nwfilter(ElementTree.tostring(tree))
self.create_nwfilter(ElementTree.tostring(tree).decode())
class wvmNWFilter(wvmConnect):
@ -43,7 +43,7 @@ class wvmNWFilter(wvmConnect):
tree = ElementTree.fromstring(self._XMLDesc(0))
uuid = tree.find('uuid')
tree.remove(uuid)
return ElementTree.tostring(tree)
return ElementTree.tostring(tree).decode()
def get_filter_refs(self):
refs = []
@ -64,7 +64,7 @@ class wvmNWFilter(wvmConnect):
rule_directives = r.find("./")
if rule_directives is not None:
rule_directives = ElementTree.tostring(rule_directives)
rule_directives = ElementTree.tostring(rule_directives).decode()
rule_info = {
"action": rule_action,
@ -84,7 +84,7 @@ class wvmNWFilter(wvmConnect):
if name == ref.get('filter'):
tree.remove(ref)
break
return ElementTree.tostring(tree)
return ElementTree.tostring(tree).decode()
def delete_rule(self, action, direction, priority):
tree = ElementTree.fromstring(self._XMLDesc(0))
@ -93,14 +93,14 @@ class wvmNWFilter(wvmConnect):
if rule_tree:
tree.remove(rule_tree[0])
return ElementTree.tostring(tree)
return ElementTree.tostring(tree).decode()
def add_ref(self, name):
tree = ElementTree.fromstring(self._XMLDesc(0))
element = ElementTree.Element("filterref")
element.attrib['filter'] = name
tree.append(element)
return ElementTree.tostring(tree)
return ElementTree.tostring(tree).decode()
def add_rule(self, xml):
tree = ElementTree.fromstring(self._XMLDesc(0))
@ -122,4 +122,4 @@ class wvmNWFilter(wvmConnect):
element.append(rule_directives)
tree.append(element)
return ElementTree.tostring(tree)
return ElementTree.tostring(tree).decode()

View file

@ -147,13 +147,13 @@ class wvmStorage(wvmConnect):
return util.get_xml_path(self._XMLDesc(0), "/pool/target/path")
def get_allocation(self):
return long(util.get_xml_path(self._XMLDesc(0), "/pool/allocation"))
return int(util.get_xml_path(self._XMLDesc(0), "/pool/allocation"))
def get_available(self):
return long(util.get_xml_path(self._XMLDesc(0), "/pool/available"))
return int(util.get_xml_path(self._XMLDesc(0), "/pool/available"))
def get_capacity(self):
return long(util.get_xml_path(self._XMLDesc(0), "/pool/capacity"))
return int(util.get_xml_path(self._XMLDesc(0), "/pool/capacity"))
def get_pretty_allocation(self):
return util.pretty_bytes(self.get_allocation())

View file

@ -34,9 +34,9 @@ def randomUUID():
"%02x" * 6]) % tuple(u)
def randomPasswd(length=12, alphabet=string.letters + string.digits):
def randomPasswd(length=12, alphabet=string.ascii_letters + string.digits):
"""Generate a random password"""
return ''.join([random.choice(alphabet) for i in xrange(length)])
return ''.join([random.choice(alphabet) for i in range(length)])
def get_max_vcpus(conn, type=None):
@ -75,7 +75,7 @@ def compareMAC(p, q):
else:
return -1
for i in xrange(len(pa)):
for i in range(len(pa)):
n = int(pa[i], 0x10) - int(qa[i], 0x10)
if n > 0:
return 1
@ -160,12 +160,12 @@ def validate_macaddr(val):
if val is None:
return
if not (isinstance(val, str) or isinstance(val, basestring)):
if not (isinstance(val, str) or isinstance(val, str)):
raise ValueError("MAC address must be a string.")
form = re.match("^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$", val)
if form is None:
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was '%s'" % val)
raise ValueError(f"MAC address must be of the format AA:BB:CC:DD:EE:FF, was {val}")
# Mapping of UEFI binary names to their associated architectures. We

View file

@ -4,7 +4,9 @@ Django settings for webvirtcloud project.
"""
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SECRET_KEY = ''
@ -12,73 +14,44 @@ DEBUG = True
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = (
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'accounts',
'computes',
'console',
'networks',
'storages',
'interfaces',
'nwfilters',
'instances',
'secrets',
'logs',
'accounts',
'create',
'datasource',
)
'networks',
'instances',
'interfaces',
'nwfilters',
'storages',
'secrets',
'logs',
]
MIDDLEWARE_CLASSES = (
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.auth.middleware.RemoteUserMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
#'django.contrib.auth.backends.RemoteUserBackend',
#'accounts.backends.MyRemoteUserBackend',
)
LOGIN_URL = '/accounts/login'
'django.middleware.locale.LocaleMiddleware',
]
ROOT_URLCONF = 'webvirtcloud.urls'
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@ -95,9 +68,57 @@ TEMPLATES = [
},
]
## WebVirtCloud settings
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
# Wobsock port
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
#'django.contrib.auth.backends.RemoteUserBackend',
#'accounts.backends.MyRemoteUserBackend',
]
LOGIN_URL = '/accounts/login'
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static"),
]
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"handlers": {"mail_admins": {"level": "ERROR", "class": "django.utils.log.AdminEmailHandler"}},
"loggers": {
"django.request": {"handlers": ["mail_admins"], "level": "ERROR", "propagate": True}
},
}
#
## WebVirtCloud settings
#
# Websock port
WS_PORT = 6080
# Websock host
@ -112,19 +133,19 @@ WS_PUBLIC_HOST = None
# Websock SSL connection
WS_CERT = None
# list of console types
# List of console types
QEMU_CONSOLE_TYPES = ['vnc', 'spice']
# default console type
# Default console type
QEMU_CONSOLE_DEFAULT_TYPE = 'vnc'
# list of console listen addresses
# List of console listen addresses
QEMU_CONSOLE_LISTEN_ADDRESSES = (
('127.0.0.1', 'Localhost'),
('0.0.0.0', 'All interfaces'),
)
# list taken from http://qemu.weilnetz.de/qemu-doc.html#sec_005finvocation
# List taken from http://qemu.weilnetz.de/qemu-doc.html#sec_005finvocation
QEMU_KEYMAPS = ['ar', 'da', 'de', 'de-ch', 'en-gb', 'en-us', 'es', 'et', 'fi',
'fo', 'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it',
'ja', 'lt', 'lv', 'mk', 'nl', 'nl-be', 'no', 'pl', 'pt',
@ -181,3 +202,9 @@ INSTANCE_CPU_DEFAULT_MODE = 'host-model'
# Chipset/Machine: pc or q35 for x86_64
INSTANCE_MACHINE_DEFAULT_TYPE = 'q35'
# Firmware: BIOS or UEFI for x86_64
INSTANCE_FIRMWARE_DEFAULT_TYPE = 'BIOS'
# Architecture: x86_64, i686, etc
INSTANCE_ARCH_DEFAULT_TYPE = 'x86_64'

View file

@ -1,17 +1,17 @@
from django.conf.urls import include, url
from django.urls import include, path
from instances.views import index
from console.views import console
# from django.contrib import admin
urlpatterns = [
url(r'^$', index, name='index'),
path('', index, name='index'),
url(r'^instances/', include('instances.urls')),
url(r'^accounts/', include('accounts.urls')),
url(r'^computes/', include('computes.urls')),
url(r'^logs/', include('logs.urls')),
url(r'^datasource/', include('datasource.urls')),
url(r'^console/$', console, name='console'),
#url(r'^admin/', include(admin.site.urls)),
path('instances/', include('instances.urls')),
path('accounts/', include('accounts.urls')),
path('computes/', include('computes.urls')),
path('logs/', include('logs.urls')),
path('datasource/', include('datasource.urls')),
path('console/', console, name='console'),
# url(r'^admin/', include(admin.site.urls)),
]

View file

@ -4,11 +4,11 @@ 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/1.11/howto/deployment/wsgi/
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
os.environ["DJANGO_SETTINGS_MODULE"] = "webvirtcloud.settings"
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

View file

@ -4,14 +4,17 @@ 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/1.7/howto/deployment/wsgi/
https://docs.djangoproject.com/en/2.0/howto/deployment/wsgi/
"""
execfile('/srv/webvirtcloud/venv/bin/activate_this.py', dict(__file__='/srv/webvirtcloud/venv/bin/activate_this.py'))
import os, sys
sys.path.append('/srv/webvirtcloud')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
from django.core.wsgi import get_wsgi_application
# execfile('/srv/webvirtcloud/venv/bin/activate_this.py', dict(__file__='/srv/webvvirtcloud/venv/bin/activate_this.py'))
exec(compile(open('/srv/webvirtcloud/venv/bin/activate_this.py', "rb").read(),
'/srv/webvirtcloud/venv/bin/activate_this.py', 'exec'))
sys.path.append('/srv/webvirtcloud')
os.environ["DJANGO_SETTINGS_MODULE"] = "webvirtcloud.settings"
application = get_wsgi_application()