1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 23:25:24 +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 .vagrant
venv venv
venv2
.vscode .vscode
.idea .idea
.DS_* .DS_*

View file

@ -1,8 +1,8 @@
language: python language: python
python: python:
- "2.7" - "3.6"
env: env:
- DJANGO=1.11.26 - DJANGO=2.2.10
install: install:
- pip install -r dev/requirements.txt - pip install -r dev/requirements.txt
script: 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 apt-get update -qqy
RUN DEBIAN_FRONTEND=noninteractive apt-get -qyy install \ RUN DEBIAN_FRONTEND=noninteractive apt-get -qyy install \
-o APT::Install-Suggests=false \ -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 ADD . /srv/webvirtcloud
RUN chown -R www-data:www-data /srv/webvirtcloud RUN chown -R www-data:www-data /srv/webvirtcloud
# Setup webvirtcloud # Setup webvirtcloud
RUN cd /srv/webvirtcloud && \ RUN cd /srv/webvirtcloud && \
virtualenv venv && \ virtualenv --python=python3 venv && \
. venv/bin/activate && \ . venv/bin/activate && \
pip install -U pip && \ pip3 install -U pip && \
pip install -r conf/requirements.txt && \ pip3 install -r conf/requirements.txt && \
chown -R www-data:www-data /srv/webvirtcloud chown -R www-data:www-data /srv/webvirtcloud
RUN cd /srv/webvirtcloud && . venv/bin/activate && \ RUN cd /srv/webvirtcloud && . venv/bin/activate && \
python manage.py migrate && \ python3 manage.py migrate && \
chown -R www-data:www-data /srv/webvirtcloud chown -R www-data:www-data /srv/webvirtcloud
# Setup Nginx # Setup Nginx

View file

@ -1,5 +1,5 @@
## WebVirtCloud ## WebVirtCloud
###### Python3 & Django 2.11
## Features ## Features
* QEMU/KVM Hypervisor Management * 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)])) print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)]))
``` ```
### Install WebVirtCloud panel (Ubuntu) ### Install WebVirtCloud panel (Ubuntu 18.04+ LTS)
```bash ```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 git clone https://github.com/retspen/webvirtcloud
cd webvirtcloud cd webvirtcloud
cp webvirtcloud/settings.py.template webvirtcloud/settings.py cp webvirtcloud/settings.py.template webvirtcloud/settings.py
@ -52,7 +52,7 @@ cd ..
sudo mv webvirtcloud /srv sudo mv webvirtcloud /srv
sudo chown -R www-data:www-data /srv/webvirtcloud sudo chown -R www-data:www-data /srv/webvirtcloud
cd /srv/webvirtcloud cd /srv/webvirtcloud
virtualenv venv virtualenv -p python3 venv
source venv/bin/activate source venv/bin/activate
pip install -r conf/requirements.txt pip install -r conf/requirements.txt
python manage.py migrate python manage.py migrate
@ -76,11 +76,11 @@ Done!!
Go to http://serverip and you should see the login screen. Go to http://serverip and you should see the login screen.
### Install WebVirtCloud panel (CentOS8/OEL8)
### Install WebVirtCloud panel (CentOS)
```bash ```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 #### Creating directories and cloning repo
@ -90,15 +90,17 @@ sudo mkdir /srv && cd /srv
sudo git clone https://github.com/retspen/webvirtcloud && cd webvirtcloud sudo git clone https://github.com/retspen/webvirtcloud && cd webvirtcloud
cp webvirtcloud/settings.py.template webvirtcloud/settings.py cp webvirtcloud/settings.py.template webvirtcloud/settings.py
# now put secret key to 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 #### Start installation webvirtcloud
``` ```
sudo virtualenv venv virtualenv-3 venv
sudo source venv/bin/activate source venv/bin/activate
sudo venv/bin/pip install -r conf/requirements.txt pip3 install -r conf/requirements.txt
sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/ cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
sudo venv/bin/python manage.py migrate python3 manage.py migrate
``` ```
#### Configure the supervisor for CentOS #### Configure the supervisor for CentOS
@ -115,7 +117,7 @@ autorestart=true
redirect_stderr=true redirect_stderr=true
[program:novncd] [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 directory=/srv/webvirtcloud
user=nginx user=nginx
autostart=true autostart=true
@ -191,11 +193,20 @@ Change permission for selinux:
```bash ```bash
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webvirtcloud(/.*)" 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 ```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: Let's restart nginx and the supervisord services:
@ -226,7 +237,7 @@ Done!!
Go to http://serverip and you should see the login screen. 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. Alternative to running nonvcd via supervisor is runit.
On Debian systems install runit and configure novncd service On Debian systems install runit and configure novncd service
@ -272,11 +283,12 @@ datasource:
### How To Update ### How To Update
```bash ```bash
cd <installation-directory> # Go to Installation Directory
sudo source venv/bin/activate cd /srv/webvirtcloud
source venv/bin/activate
git pull git pull
pip install -U -r conf/requirements.txt pip3 install -U -r conf/requirements.txt
python manage.py migrate python3 manage.py migrate
sudo service supervisor restart sudo service supervisor restart
``` ```
### Screenshots ### 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 sed -i 's/auth_tcp = \"sasl\"/auth_tcp = \"none\"/g' /etc/libvirt/libvirtd.conf
sudo service libvirt-bin restart sudo service libvirt-bin restart
sudo adduser vagrant libvirtd sudo adduser vagrant libvirtd
sudo apt-get -y install python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev sudo apt-get -y install python3-virtualenv virtualenv python3-pip python3-dev python3-lxml libvirt-dev zlib1g-dev python3-guestfs
virtualenv /vagrant/venv virtualenv -p python3 /vagrant/venv
source /vagrant/venv/bin/activate source /vagrant/venv/bin/activate
pip install -r /vagrant/dev/requirements.txt pip3 install -r /vagrant/dev/requirements.txt
SHELL SHELL
end end
# To start this machine run "vagrant up prod" # To start this machine run "vagrant up prod"
@ -24,6 +24,7 @@ Vagrant.configure(2) do |config|
prod.vm.box = "ubuntu/bionic64" prod.vm.box = "ubuntu/bionic64"
prod.vm.hostname = "webvirtcloud" prod.vm.hostname = "webvirtcloud"
prod.vm.network "private_network", ip: "192.168.33.11" 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.synced_folder ".", "/srv/webvirtcloud"
prod.vm.provision "shell", inline: <<-SHELL prod.vm.provision "shell", inline: <<-SHELL
sudo mkdir /srv/webvirtcloud sudo mkdir /srv/webvirtcloud
@ -33,15 +34,15 @@ Vagrant.configure(2) do |config|
sudo service libvirt-bin restart sudo service libvirt-bin restart
sudo adduser vagrant libvirtd sudo adduser vagrant libvirtd
sudo chown -R vagrant:vagrant /srv/webvirtcloud 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 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 /srv/webvirtcloud/venv virtualenv -p python3 /srv/webvirtcloud/venv
source /srv/webvirtcloud/venv/bin/activate 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/supervisor/webvirtcloud.conf /etc/supervisor/conf.d
sudo cp /srv/webvirtcloud/conf/nginx/webvirtcloud.conf /etc/nginx/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 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 sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
python /srv/webvirtcloud/manage.py migrate python3 /srv/webvirtcloud/manage.py migrate
sudo rm /etc/nginx/sites-enabled/default sudo rm /etc/nginx/sites-enabled/default
sudo chown -R www-data:www-data /srv/webvirtcloud sudo chown -R www-data:www-data /srv/webvirtcloud
sudo service nginx restart sudo service nginx restart

View file

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

View file

@ -1,29 +1,51 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.10 on 2020-01-28 07:01
from __future__ import unicode_literals
from django.db import models, migrations
from django.conf import settings from django.conf import settings
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instances', '0001_initial'), ('instances', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
] ]
operations = [ 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( migrations.CreateModel(
name='UserInstance', name='UserInstance',
fields=[ 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_change', models.BooleanField(default=False)),
('is_delete', models.BooleanField(default=False)), ('is_delete', models.BooleanField(default=False)),
('instance', models.ForeignKey(to='instances.Instance')), ('is_vnc', models.BooleanField(default=False)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)), ('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.contrib.auth.models import User
from django.conf import settings from django.conf import settings
from instances.models import Instance from instances.models import Instance
from django.core.validators import MinValueValidator from django.core.validators import MinValueValidator
class UserInstance(models.Model): class UserInstance(Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) user = ForeignKey(User, on_delete=CASCADE)
instance = models.ForeignKey(Instance, on_delete=models.CASCADE) instance = ForeignKey(Instance, on_delete=CASCADE)
is_change = models.BooleanField(default=False) is_change = BooleanField(default=False)
is_delete = models.BooleanField(default=False) is_delete = BooleanField(default=False)
is_vnc = models.BooleanField(default=False) is_vnc = BooleanField(default=False)
def __unicode__(self): def __unicode__(self):
return self.instance.name return self.instance.name
class UserSSHKey(models.Model): class UserSSHKey(Model):
user = models.ForeignKey(User, on_delete=models.DO_NOTHING) user = ForeignKey(User, on_delete=DO_NOTHING)
keyname = models.CharField(max_length=25) keyname = CharField(max_length=25)
keypublic = models.CharField(max_length=500) keypublic = CharField(max_length=500)
def __unicode__(self): def __unicode__(self):
return self.keyname return self.keyname
class UserAttributes(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE) class UserAttributes(Model):
can_clone_instances = models.BooleanField(default=True) user = OneToOneField(User, on_delete=CASCADE)
max_instances = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1),]) can_clone_instances = BooleanField(default=True)
max_cpus = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)]) max_instances = 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_cpus = IntegerField(default=1, 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)]) 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 @staticmethod
def create_missing_userattributes(user): def create_missing_userattributes(user):

View file

@ -87,8 +87,8 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label">{% trans "VNC" %}</label> <label class="col-sm-4 control-label">{% trans "VNC" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select type="text" class="form-control" name="inst_vnc"> <select class="form-control" name="inst_vnc">
<option value="">False</option> <option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_vnc %}selected{% endif %}>True</option> <option value="1" {% if inst.is_vnc %}selected{% endif %}>True</option>
</select> </select>
</div> </div>
@ -96,8 +96,8 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label">{% trans "Resize" %}</label> <label class="col-sm-4 control-label">{% trans "Resize" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select type="text" class="form-control" name="inst_change"> <select class="form-control" name="inst_change">
<option value="">False</option> <option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_change %}selected{% endif %}>True</option> <option value="1" {% if inst.is_change %}selected{% endif %}>True</option>
</select> </select>
</div> </div>
@ -105,8 +105,8 @@
<div class="form-group"> <div class="form-group">
<label class="col-sm-4 control-label">{% trans "Delete" %}</label> <label class="col-sm-4 control-label">{% trans "Delete" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
<select type="text" class="form-control" name="inst_delete"> <select class="form-control" name="inst_delete">
<option value="">False</option> <option value="">{% trans 'False' %}</option>
<option value="1" {% if inst.is_delete %}selected{% endif %}>True</option> <option value="1" {% if inst.is_delete %}selected{% endif %}>True</option>
</select> </select>
</div> </div>
@ -124,7 +124,7 @@
<td style="width:5px;"> <td style="width:5px;">
<form action="" method="post" role="form">{% csrf_token %} <form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="user_inst" value="{{ inst.id }}"> <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> <span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
</button> </button>
</form> </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 django.contrib.auth import views as auth_views
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'), path('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'), path('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'), path('profile/', views.profile, name='profile'), path('', views.accounts, name='accounts'),
url(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'), 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): class ComputeAddSocketForm(forms.Form):
name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, max_length=64)
max_length=64) details = forms.CharField(error_messages={'required': _('No details has been entred')}, max_length=50)
details = forms.CharField(error_messages={'required': _('No details has been entred')},
max_length=50)
def clean_name(self): def clean_name(self):
name = self.cleaned_data['name'] name = self.cleaned_data['name']

View file

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.10 on 2020-01-28 07:01
from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
] ]
@ -13,15 +14,13 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Compute', name='Compute',
fields=[ 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')),
('name', models.CharField(max_length=20)), ('name', models.CharField(max_length=64)),
('hostname', models.CharField(max_length=20)), ('hostname', models.CharField(max_length=64)),
('login', models.CharField(max_length=20)), ('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()), ('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): class Compute(Model):
name = models.CharField(max_length=64) name = CharField(max_length=64)
hostname = models.CharField(max_length=64) hostname = CharField(max_length=64)
login = models.CharField(max_length=20) login = CharField(max_length=20)
password = models.CharField(max_length=14, blank=True, null=True) password = CharField(max_length=14, blank=True, null=True)
details = models.CharField(max_length=64, null=True, blank=True) details = CharField(max_length=64, null=True, blank=True)
type = models.IntegerField() type = IntegerField()
def __unicode__(self): def __unicode__(self):
return self.hostname 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 storages.views import storages, storage, get_volumes
from networks.views import networks, network from networks.views import networks, network
from secrets.views import secrets from secrets.views import secrets
@ -9,23 +9,24 @@ from instances.views import instances
from nwfilters.views import nwfilter, nwfilters from nwfilters.views import nwfilter, nwfilters
urlpatterns = [ urlpatterns = [
url(r'^$', computes, name='computes'), path('', computes, name='computes'),
url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'), re_path(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'), re_path(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'), re_path(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'), re_path(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'), re_path(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'), re_path(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
url(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'), re_path(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
url(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'), re_path(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
url(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'), re_path(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
url(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'), re_path(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
url(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'), re_path(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
url(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'), re_path(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'), re_path(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'), re_path(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'), re_path(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'), re_path(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'), 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'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'), 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 # gstfsd - WebVirtCloud daemon for managing VM's filesystem
# #
import SocketServer import socketserver
import json import json
import guestfs import guestfs
import re import re
@ -13,11 +13,11 @@ PORT = 16510
ADDRESS = "0.0.0.0" ADDRESS = "0.0.0.0"
class MyTCPServer(SocketServer.ThreadingTCPServer): class MyTCPServer(socketserver.ThreadingTCPServer):
allow_reuse_address = True allow_reuse_address = True
class MyTCPServerHandler(SocketServer.BaseRequestHandler): class MyTCPServerHandler(socketserver.BaseRequestHandler):
def handle(self): def handle(self):
# recive data # recive data
data = json.loads(self.request.recv(1024).strip()) data = json.loads(self.request.recv(1024).strip())
@ -42,9 +42,9 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
if data['action'] == 'publickey': if data['action'] == 'publickey':
if not gfs.is_dir('/root/.ssh'): if not gfs.is_dir('/root/.ssh'):
gfs.mkdir('/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.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'})) self.request.sendall(json.dumps({'return': 'success'}))
gfs.umount(part) gfs.umount(part)
except RuntimeError: except RuntimeError:
@ -52,7 +52,7 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
gfs.shutdown() gfs.shutdown()
gfs.close() gfs.close()
except RuntimeError as err: 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) server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)

View file

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

View file

@ -1,4 +1,4 @@
import random, string import random, string
haystack = string.ascii_letters + string.digits + string.punctuation 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] [program:gstfsd]
command=/usr/bin/python /usr/local/bin/gstfsd command=/srv/webvirtcloud/venv/bin/python3 /usr/local/bin/gstfsd
directory=/usr/local/bin directory=/usr/local/bin
user=root user=root
autostart=true autostart=true

View file

@ -7,7 +7,7 @@ autorestart=true
redirect_stderr=true redirect_stderr=true
[program:novncd] [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 directory=/srv/webvirtcloud
user=www-data user=www-data
autostart=true autostart=true

View file

@ -7,7 +7,7 @@ import django
DIR_PATH = os.path.dirname(os.path.abspath(__file__)) DIR_PATH = os.path.dirname(os.path.abspath(__file__))
ROOT_PATH = os.path.abspath(os.path.join(DIR_PATH, '..', '')) 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' CERT = DIR_PATH + '/cert.pem'
if ROOT_PATH not in sys.path: if ROOT_PATH not in sys.path:
@ -15,16 +15,16 @@ if ROOT_PATH not in sys.path:
django.setup() 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: # if VENV_PATH not in sys.path:
# sys.path.append(VENV_PATH) # sys.path.append(VENV_PATH)
import re import re
import Cookie
import socket import socket
from six.moves import http_cookies as Cookie
from webvirtcloud.settings import WS_PORT, WS_HOST, WS_CERT from webvirtcloud.settings import WS_PORT, WS_HOST, WS_CERT
from vrtManager.connection import CONN_SSH, CONN_SOCKET from vrtManager.connection import CONN_SSH, CONN_SOCKET
from tunnel import Tunnel from console.tunnel import Tunnel
from optparse import OptionParser from optparse import OptionParser
parser = OptionParser() parser = OptionParser()
@ -127,12 +127,30 @@ def get_connection_infos(token):
class CompatibilityMixIn(object): class CompatibilityMixIn(object):
def _new_client(self, daemon, socket_factory): def _new_client(self, daemon, socket_factory):
cookie = Cookie.SimpleCookie() # NoVNC uses it's own convention that forward token
cookie.load(self.headers.getheader('cookie')) # from the request to a cookie header, we should check
if 'token' not in cookie: # also for this behavior
self.msg('No token cookie found !') hcookie = self.headers.get('cookie')
return False if hcookie:
token = cookie['token'].value 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, (connhost, connport, connuser, conntype, console_host, console_port,
console_socket) = get_connection_infos(token) console_socket) = get_connection_infos(token)

View file

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

View file

@ -25,65 +25,28 @@
{% extends "console-base.html" %} {% extends "console-base.html" %}
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block head %} {% block head %}
<title>WebVirtCloud - Spice Client - Full</title> <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" %}" /> <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> <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 host = null, port = null;
var sc; var sc;
@ -96,9 +59,6 @@
host = document.getElementById("host").value; host = document.getElementById("host").value;
port = document.getElementById("port").value; port = document.getElementById("port").value;
if (window.location.protocol == 'https:') {
scheme = "wss://";
}
password = document.getElementById("password").value; password = document.getElementById("password").value;
if ((!host) || (!port)) { if ((!host) || (!port)) {
@ -116,11 +76,10 @@
document.getElementById('connectButton').onclick = disconnect; document.getElementById('connectButton').onclick = disconnect;
try { 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 }); message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
} }
catch (e) catch (e) {
{
alert(e.toString()); alert(e.toString());
disconnect(); disconnect();
} }
@ -135,25 +94,27 @@
document.getElementById('connectButton').onclick = connect; document.getElementById('connectButton').onclick = connect;
if (window.File && window.FileReader && window.FileList && window.Blob) { if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.getElementById('spice-xfer-area'); var spice_xfer_area = document.getElementById('spice-xfer-area');
document.getElementById('spice-area').removeChild(spice_xfer_area); if (spice_xfer_area != null) {
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false); document.getElementById('spice-area').removeChild(spice_xfer_area);
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false); }
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"); console.log("<< disconnect");
} }
function agent_connected(sc) { function agent_connected(sc) {
window.addEventListener('resize', handle_resize); window.addEventListener('resize', SpiceHtml5.handle_resize);
window.spice_connection = this; window.spice_connection = this;
resize_helper(this); SpiceHtml5.resize_helper(this);
if (window.File && window.FileReader && window.FileList && window.Blob) { if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.createElement("div"); var spice_xfer_area = document.createElement("div");
spice_xfer_area.setAttribute('id', 'spice-xfer-area'); spice_xfer_area.setAttribute('id', 'spice-xfer-area');
document.getElementById('spice-area').appendChild(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('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false); document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
} }
else { else {
console.log("File API is not supported"); console.log("File API is not supported");
@ -171,20 +132,9 @@
m.style.display = 'none'; m.style.display = 'none';
} }
window.addEventListener('resize', handle_resize); window.addEventListener('resize', SpiceHtml5.handle_resize);
resize_helper(sc); if (sc) {
} SpiceHtml5.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();
} }
} }
/* SPICE port event listeners /* SPICE port event listeners
@ -198,20 +148,72 @@
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent); 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); function fullscreen() {
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);}); var screen=document.getElementById('spice-screen');
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);}); if(screen.requestFullscreen) {
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);}); screen.requestFullscreen();
document.getElementById('ctrlaltf4').addEventListener('click', function(){sendCtrlAltFN(3);}); } else if(screen.mozRequestFullScreen) {
document.getElementById('ctrlaltf5').addEventListener('click', function(){sendCtrlAltFN(4);}); screen.mozRequestFullScreen();
document.getElementById('ctrlaltf6').addEventListener('click', function(){sendCtrlAltFN(5);}); } else if(screen.webkitRequestFullscreen) {
document.getElementById('ctrlaltf7').addEventListener('click', function(){sendCtrlAltFN(6);}); screen.webkitRequestFullscreen();
document.getElementById('ctrlaltf8').addEventListener('click', function(){sendCtrlAltFN(7);}); } else if(screen.msRequestFullscreen) {
document.getElementById('ctrlaltf9').addEventListener('click', function(){sendCtrlAltFN(8);}); screen.msRequestFullscreen();
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);}); }
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);}); }
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);}); /* SPICE port event listeners
connect(); window.addEventListener('spice-port-data', function(event) {
</script> // 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 %} {% endblock %}

View file

@ -25,56 +25,27 @@
{% extends "console-base.html" %} {% extends "console-base.html" %}
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block head %} {% block head %}
<title>WebVirtCloud - Spice - Lite</title> <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" %}" /> <link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
{% endblock %} <!-- ES2015/ES6 modules polyfill -->
<script type="module">
{% block content %} window._spice_has_module_support = true;
<div id="spice-area"> </script>
<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 %}
<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 host = null, port = null;
var sc; var sc;
@ -92,21 +63,18 @@
} }
function spice_error(e) { function spice_error(e) {
console.log(e);
disconnect(); disconnect();
if (e.message !== undefined) { if (e !== undefined && e.message === "Permission denied.") {
log_error(e.message); var pass = prompt("Password");
} connect(pass);
else {
log_error('Unknown error');
} }
} }
function connect() { function connect(password) {
var host, port, password, scheme = "ws://", uri; var host, port, scheme = "ws://", uri;
console.log('>> connect');
// By default, use the host and port of server that served this file // 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 }}'; host = '{{ ws_host| safe }}';
// Note that using the web server port only makes sense // 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. // If a token variable is passed in, set the parameter in a cookie.
// This is used by nova-spiceproxy. // This is used by nova-spiceproxy.
token = spice_query_var('token', null); var token = spice_query_var('token', null);
if (token) { if (token) {
spice_set_cookie('token', token, 1) spice_set_cookie('token', token, 1)
} }
//password = spice_query_var('password', ''); if (password === undefined) {
password = '{{ console_passwd | safe }}'; password = spice_query_var('password', '');
password = '{{ console_passwd | safe }}';
}
if (password === 'None') password = ''; if (password === 'None') password = '';
path = spice_query_var('path', 'websockify'); var path = spice_query_var('path', 'websockify');
if ((!host) || (!port)) { if ((!host) || (!port)) {
console.log("must specify host and port in URL"); console.log("must specify host and port in URL");
@ -156,43 +126,44 @@
} }
try { 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 }); message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
} }
catch (e) { catch (e) {
alert(e.toString()); alert(e.toString());
disconnect(); disconnect();
} }
console.log('<< connect')
} }
function disconnect() function disconnect() {
{
console.log(">> disconnect"); console.log(">> disconnect");
if (sc) { if (sc) {
sc.stop(); sc.stop();
} }
if (window.File && window.FileReader && window.FileList && window.Blob) { if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.getElementById('spice-xfer-area'); var spice_xfer_area = document.getElementById('spice-xfer-area');
document.getElementById('spice-area').removeChild(spice_xfer_area); if (spice_xfer_area != null) {
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false); document.getElementById('spice-area').removeChild(spice_xfer_area);
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false); }
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"); console.log("<< disconnect");
} }
function agent_connected(sc) { function agent_connected(sc) {
window.addEventListener('resize', handle_resize); window.addEventListener('resize', SpiceHtml5.handle_resize);
window.spice_connection = this; window.spice_connection = this;
resize_helper(this); SpiceHtml5.resize_helper(this);
if (window.File && window.FileReader && window.FileList && window.Blob) { if (window.File && window.FileReader && window.FileList && window.Blob) {
var spice_xfer_area = document.createElement("div"); var spice_xfer_area = document.createElement("div");
spice_xfer_area.setAttribute('id', 'spice-xfer-area'); spice_xfer_area.setAttribute('id', 'spice-xfer-area');
document.getElementById('spice-area').appendChild(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('dragover', SpiceHtml5.handle_file_dragover, false);
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false); document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
} }
else { else {
console.log("File API is not supported"); console.log("File API is not supported");
@ -224,7 +195,7 @@
}); });
*/ */
document.getElementById("fullscreen_button").addEventListener('click', fullscreen); 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('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);}); document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);}); document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
@ -237,6 +208,22 @@
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);}); document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);}); document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);}); document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
connect(); connect(undefined);
</script> </script>
{% endblock %} {% 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 socket
import signal import signal
import logging import logging
from functools import reduce
class Tunnel(object): class Tunnel(object):
@ -103,7 +104,7 @@ class Tunnel(object):
logging.debug("Tunnel PID=%d OUTFD=%d ERRFD=%d", logging.debug("Tunnel PID=%d OUTFD=%d ERRFD=%d",
pid, fds[0].fileno(), errorfds[0].fileno()) pid, fds[0].fileno(), errorfds[0].fileno())
errorfds[0].setblocking(0) errorfds[0].setblocking(False)
self.outfd = fds[0] self.outfd = fds[0]
self.errfd = errorfds[0] self.errfd = errorfds[0]

View file

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

View file

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

View file

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

View file

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.10 on 2020-01-28 07:01
from __future__ import unicode_literals
from django.db import migrations 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): class Flavor(Model):
label = models.CharField(max_length=12) label = CharField(max_length=12)
memory = models.IntegerField() memory = IntegerField()
vcpu = models.IntegerField() vcpu = IntegerField()
disk = models.IntegerField() disk = IntegerField()
def __unicode__(self): def __unicode__(self):
return self.name return self.name

View file

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

View file

@ -120,7 +120,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<select class="form-control" id="select_firmware" name="firmware"> <select class="form-control" id="select_firmware" name="firmware">
{% for frm in firmwares %} {% 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 %} {% endfor %}
</select> </select>
</div> </div>
@ -792,7 +792,7 @@
$.each(input_value.split(','), function (index, value) { $.each(input_value.split(','), function (index, value) {
let li = '<li>eth' + counter + let li = '<li>eth' + counter +
' -> ' + value + ' ' + ' -> ' + 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; selected_list_html += li;
counter++; 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_IO
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DISCARD 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 django.contrib import messages
from logs.views import addlogmsg from logs.views import addlogmsg
@login_required @login_required
def create_instance_select_type(request, compute_id): 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() all_hypervisors = conn.get_hypervisors_machines()
# Supported hypervisors by webvirtcloud: i686, x86_64(for now) # Supported hypervisors by webvirtcloud: i686, x86_64(for now)
supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"] 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_machine = INSTANCE_MACHINE_DEFAULT_TYPE
default_arch = INSTANCE_ARCH_DEFAULT_TYPE
if request.method == 'POST': if request.method == 'POST':
if 'create_xml' in request.POST: if 'create_xml' in request.POST:
@ -63,18 +68,21 @@ def create_instance_select_type(request, compute_id):
conn._defineXML(xml) conn._defineXML(xml)
return HttpResponseRedirect(reverse('instance', args=[compute_id, name])) return HttpResponseRedirect(reverse('instance', args=[compute_id, name]))
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err.message) error_messages.append(lib_err)
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)
return render(request, 'create_instance_w1.html', locals()) return render(request, 'create_instance_w1.html', locals())
@login_required @login_required
def create_instance(request, compute_id, arch, machine): def create_instance(request, compute_id, arch, machine):
""" """
:param request: :param request:
:param compute_id: :param compute_id:
:param arch:
:param machine:
:return: :return:
""" """
if not request.user.is_superuser: if not request.user.is_superuser:
@ -96,6 +104,7 @@ def create_instance(request, compute_id, arch, machine):
compute.password, compute.password,
compute.type) compute.type)
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
instances = conn.get_instances() instances = conn.get_instances()
videos = conn.get_video_models(arch, machine) videos = conn.get_video_models(arch, machine)
@ -189,7 +198,7 @@ def create_instance(request, compute_id, arch, machine):
volume_list.append(volume) volume_list.append(volume)
is_disk_created = True is_disk_created = True
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err.message) error_messages.append(lib_err)
elif data['template']: elif data['template']:
templ_path = conn.get_volume_path(data['template']) templ_path = conn.get_volume_path(data['template'])
dest_vol = conn.get_volume_path(data["name"] + ".img", data['storage']) 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['bus'] = request.POST.get('bus' + str(idx), '')
volume_list.append(volume) volume_list.append(volume)
except libvirtError as lib_err: 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(): if data['cache_mode'] not in conn.get_cache_modes():
error_msg = _("Invalid cache mode") error_msg = _("Invalid cache mode")
error_messages.append(error_msg) error_messages.append(error_msg)

View file

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

View file

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

View file

@ -1,11 +1,13 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.10 on 2020-01-28 07:01
from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
('computes', '0001_initial'), ('computes', '0001_initial'),
] ]
@ -14,13 +16,12 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Instance', name='Instance',
fields=[ 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')),
('name', models.CharField(max_length=20)), ('name', models.CharField(max_length=120)),
('uuid', models.CharField(max_length=36)), ('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 from computes.models import Compute
class Instance(models.Model): class Instance(Model):
compute = models.ForeignKey(Compute, on_delete=models.CASCADE) compute = ForeignKey(Compute, on_delete=CASCADE)
name = models.CharField(max_length=120) name = CharField(max_length=120)
uuid = models.CharField(max_length=36) uuid = CharField(max_length=36)
is_template = models.BooleanField(default=False) is_template = BooleanField(default=False)
created = models.DateField(auto_now_add=True) created = DateField(auto_now_add=True)
def __unicode__(self): def __unicode__(self):
return self.name return self.name

View file

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

View file

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

View file

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

View file

@ -1,29 +1,24 @@
# -*- coding: utf-8 -*- # Generated by Django 2.2.10 on 2020-01-28 07:01
from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('instances', '0001_initial'),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Logs', name='Logs',
fields=[ 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)), ('message', models.CharField(max_length=255)),
('date', models.DateTimeField(auto_now=True)), ('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): class Logs(Model):
user = models.CharField(max_length=50) user = CharField(max_length=50)
instance = models.CharField(max_length=50) instance = CharField(max_length=50)
message = models.CharField(max_length=255) message = CharField(max_length=255)
date = models.DateTimeField(auto_now=True) date = DateTimeField(auto_now=True)
def __unicode__(self): def __unicode__(self):
return self.instance return self.instance

View file

@ -1,4 +1,4 @@
<center> <div class="center-block">
{% if page > 1 %} {% if page > 1 %}
<a href="{% url 'showlogspage' page|add:"-1" %}">&larr;</a> <a href="{% url 'showlogspage' page|add:"-1" %}">&larr;</a>
{% else %} {% else %}
@ -9,4 +9,4 @@
{% else %} {% else %}
&nbsp; &nbsp;
{% endif %} {% 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 from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.showlogs, name='showlogs'), path('', views.showlogs, name='showlogs'),
url(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'), re_path(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
url(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'), 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')}, subnet = forms.CharField(error_messages={'required': _('No IPv4 subnet has been entered')},
max_length=20, required=False) max_length=20, required=False)
subnet6 = forms.CharField(error_messages={'required': _('No IPv6 subnet has been entered')}, 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) forward = forms.CharField(max_length=100)
dhcp4 = forms.BooleanField(required=False) dhcp4 = forms.BooleanField(required=False)
dhcp6 = forms.BooleanField(required=False) dhcp6 = forms.BooleanField(required=False)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,7 +22,7 @@
body { body {
margin:0; margin:0;
padding:0; padding:0;
font-family: Helvetica; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
/*Background image with light grey curve.*/ /*Background image with light grey curve.*/
background-color:#494949; background-color:#494949;
background-repeat:no-repeat; 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 so it still shows up in the console
throw err; throw err;
} };
var ready = function() { var ready = function() {
document.removeEventListener('DOMContentLoaded', ready, false ); document.removeEventListener('DOMContentLoaded', ready, false );
@ -63,7 +63,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
} }
} }
} }
} };
// simple DOM ready // simple DOM ready
if (document.readyState !== 'loading') if (document.readyState !== 'loading')
@ -105,10 +105,10 @@ function xhrFetch(url, resolve, reject) {
var xhr = new XMLHttpRequest(); var xhr = new XMLHttpRequest();
var load = function(source) { var load = function(source) {
resolve(xhr.responseText); resolve(xhr.responseText);
} };
var error = function() { var error = function() {
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url)); reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
} };
xhr.onreadystatechange = function () { xhr.onreadystatechange = function () {
if (xhr.readyState === 4) { if (xhr.readyState === 4) {
@ -235,7 +235,7 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
// anonymous module // anonymous module
if (anonSources[key]) { if (anonSources[key]) {
resolve(anonSources[key]) resolve(anonSources[key]);
anonSources[key] = undefined; anonSources[key] = undefined;
} }
// otherwise we fetch // otherwise we fetch

View file

@ -4,7 +4,7 @@ export function shrinkBuf (buf, size) {
if (buf.subarray) { return buf.subarray(0, size); } if (buf.subarray) { return buf.subarray(0, size); }
buf.length = size; buf.length = size;
return buf; return buf;
}; }
export function arraySet (dest, src, src_offs, len, dest_offs) { 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 */ else if ((op & 64) === 0) { /* 2nd level distance code */
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist; continue;
} }
else { else {
strm.msg = 'invalid distance code'; 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 */ else if ((op & 64) === 0) { /* 2nd level length code */
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))]; here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen; continue;
} }
else if (op & 32) { /* end-of-block */ else if (op & 32) { /* end-of-block */
//Tracevv((stderr, "inflate: end of block\n")); //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)); strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
state.hold = hold; state.hold = hold;
state.bits = bits; 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 #login input
{ {
padding: 5px; padding: 5px;
background-color: #fAfAfA;
border: 1px inset #999999; border: 1px inset #999999;
outline: none; outline: none;
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
@ -81,7 +80,7 @@ body
{ {
min-height: 600px; min-height: 600px;
height: 100%; height: 100%;
margin: 62px 10px 10px 10px; margin: 10px;
padding: 0; padding: 0;
background-color: #333333; background-color: #333333;
} }
@ -104,6 +103,8 @@ body
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
-moz-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); box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
/* We default the message box to hidden. */
display: none;
} }
.spice-message p { .spice-message p {
margin-bottom: 0em; margin-bottom: 0em;

View file

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

View file

@ -80,7 +80,7 @@ class wvmConnection(object):
elif self.type == CONN_SOCKET: elif self.type == CONN_SOCKET:
self.__connect_socket() self.__connect_socket()
else: 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: if self.connected:
# do some preprocessing of the connection: # do some preprocessing of the connection:
@ -135,14 +135,14 @@ class wvmConnection(object):
def __connect_tcp(self): def __connect_tcp(self):
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE] flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
auth = [flags, self.__libvirt_auth_credentials_callback, None] auth = [flags, self.__libvirt_auth_credentials_callback, None]
uri = 'qemu+tcp://%s/system' % self.host uri = f'qemu+tcp://{self.host}/system'
try: try:
self.connection = libvirt.openAuth(uri, auth, 0) self.connection = libvirt.openAuth(uri, auth, 0)
self.last_error = None self.last_error = None
except libvirtError as e: except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e) self.last_error = f'Connection Failed: {str(e)}'
self.connection = None self.connection = None
def __connect_ssh(self): def __connect_ssh(self):
@ -153,7 +153,7 @@ class wvmConnection(object):
self.last_error = None self.last_error = None
except libvirtError as e: 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 self.connection = None
def __connect_tls(self): def __connect_tls(self):
@ -166,7 +166,7 @@ class wvmConnection(object):
self.last_error = None self.last_error = None
except libvirtError as e: except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e) self.last_error = f'Connection Failed: {str(e)}'
self.connection = None self.connection = None
def __connect_socket(self): def __connect_socket(self):
@ -177,7 +177,7 @@ class wvmConnection(object):
self.last_error = None self.last_error = None
except libvirtError as e: except libvirtError as e:
self.last_error = 'Connection Failed: ' + str(e) self.last_error = f'Connection Failed: {str(e)}'
self.connection = None self.connection = None
def close(self): def close(self):
@ -208,18 +208,18 @@ class wvmConnection(object):
def __unicode__(self): def __unicode__(self):
if self.type == CONN_TCP: if self.type == CONN_TCP:
type_str = u'tcp' type_str = 'tcp'
elif self.type == CONN_SSH: elif self.type == CONN_SSH:
type_str = u'ssh' type_str = 'ssh'
elif self.type == CONN_TLS: elif self.type == CONN_TLS:
type_str = u'tls' type_str = 'tls'
else: 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): def __repr__(self):
return '<wvmConnection {connection_str}>'.format(connection_str=unicode(self)) return f'<wvmConnection {str(self)}>'
class wvmConnectionManager(object): class wvmConnectionManager(object):
@ -264,9 +264,9 @@ class wvmConnectionManager(object):
raises libvirtError if (re)connecting fails raises libvirtError if (re)connecting fails
""" """
# force all string values to unicode # force all string values to unicode
host = unicode(host) host = str(host)
login = unicode(login) login = str(login)
passwd = unicode(passwd) if passwd is not None else None passwd = str(passwd) if passwd is not None else None
connection = self._search_connection(host, login, passwd, conn) connection = self._search_connection(host, login, passwd, conn)
@ -432,21 +432,21 @@ class wvmConnect(object):
def get_version(self): def get_version(self):
ver = self.wvm.getVersion() ver = self.wvm.getVersion()
major = ver / 1000000 major = ver // 1000000
ver = ver % 1000000 ver = ver % 1000000
minor = ver / 1000 minor = ver // 1000
ver = ver % 1000 ver = ver % 1000
release = ver release = ver
return "%s.%s.%s" % (major, minor, release) return f"{major}.{minor}.{release}"
def get_lib_version(self): def get_lib_version(self):
ver = self.wvm.getLibVersion() ver = self.wvm.getLibVersion()
major = ver / 1000000 major = ver // 1000000
ver %= 1000000 ver %= 1000000
minor = ver / 1000 minor = ver // 1000
ver %= 1000 ver %= 1000
release = ver release = ver
return "%s.%s.%s" % (major,minor,release) return f"{major}.{minor}.{release}"
def is_kvm_supported(self): def is_kvm_supported(self):
"""Return KVM capabilities.""" """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 from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
def get_rbd_storage_data(stg): def get_rbd_storage_data(stg):
xml = stg.XMLDesc(0) xml = stg.XMLDesc(0)
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username") ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
@ -236,10 +233,10 @@ class wvmCreate(wvmConnect):
""" """
xml += """<devices>""" xml += """<devices>"""
vd_disk_letters = list(string.lowercase) vd_disk_letters = list(string.ascii_lowercase)
fd_disk_letters = list(string.lowercase) fd_disk_letters = list(string.ascii_lowercase)
hd_disk_letters = list(string.lowercase) hd_disk_letters = list(string.ascii_lowercase)
sd_disk_letters = list(string.lowercase) sd_disk_letters = list(string.ascii_lowercase)
add_cd = True add_cd = True
disk_opts = '' disk_opts = ''

View file

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

View file

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

View file

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

View file

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

View file

@ -34,9 +34,9 @@ def randomUUID():
"%02x" * 6]) % tuple(u) "%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""" """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): def get_max_vcpus(conn, type=None):
@ -75,7 +75,7 @@ def compareMAC(p, q):
else: else:
return -1 return -1
for i in xrange(len(pa)): for i in range(len(pa)):
n = int(pa[i], 0x10) - int(qa[i], 0x10) n = int(pa[i], 0x10) - int(qa[i], 0x10)
if n > 0: if n > 0:
return 1 return 1
@ -160,12 +160,12 @@ def validate_macaddr(val):
if val is None: if val is None:
return 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.") 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) form = re.match("^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$", val)
if form is None: 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 # Mapping of UEFI binary names to their associated architectures. We

View file

@ -4,7 +4,9 @@ Django settings for webvirtcloud project.
""" """
import os 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 = '' SECRET_KEY = ''
@ -12,73 +14,44 @@ DEBUG = True
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
INSTALLED_APPS = (
# Application definition
INSTALLED_APPS = [
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'accounts',
'computes', 'computes',
'console', 'console',
'networks',
'storages',
'interfaces',
'nwfilters',
'instances',
'secrets',
'logs',
'accounts',
'create', 'create',
'datasource', 'datasource',
) 'networks',
'instances',
'interfaces',
'nwfilters',
'storages',
'secrets',
'logs',
]
MIDDLEWARE_CLASSES = ( MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware', 'django.contrib.auth.middleware.RemoteUserMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
) 'django.middleware.locale.LocaleMiddleware',
]
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
#'django.contrib.auth.backends.RemoteUserBackend',
#'accounts.backends.MyRemoteUserBackend',
)
LOGIN_URL = '/accounts/login'
ROOT_URLCONF = 'webvirtcloud.urls' 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 = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', '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 WS_PORT = 6080
# Websock host # Websock host
@ -112,19 +133,19 @@ WS_PUBLIC_HOST = None
# Websock SSL connection # Websock SSL connection
WS_CERT = None WS_CERT = None
# list of console types # List of console types
QEMU_CONSOLE_TYPES = ['vnc', 'spice'] QEMU_CONSOLE_TYPES = ['vnc', 'spice']
# default console type # Default console type
QEMU_CONSOLE_DEFAULT_TYPE = 'vnc' QEMU_CONSOLE_DEFAULT_TYPE = 'vnc'
# list of console listen addresses # List of console listen addresses
QEMU_CONSOLE_LISTEN_ADDRESSES = ( QEMU_CONSOLE_LISTEN_ADDRESSES = (
('127.0.0.1', 'Localhost'), ('127.0.0.1', 'Localhost'),
('0.0.0.0', 'All interfaces'), ('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', 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', 'fo', 'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it',
'ja', 'lt', 'lv', 'mk', 'nl', 'nl-be', 'no', 'pl', 'pt', '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 # Chipset/Machine: pc or q35 for x86_64
INSTANCE_MACHINE_DEFAULT_TYPE = 'q35' 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 instances.views import index
from console.views import console from console.views import console
# from django.contrib import admin # from django.contrib import admin
urlpatterns = [ urlpatterns = [
url(r'^$', index, name='index'), path('', index, name='index'),
url(r'^instances/', include('instances.urls')), path('instances/', include('instances.urls')),
url(r'^accounts/', include('accounts.urls')), path('accounts/', include('accounts.urls')),
url(r'^computes/', include('computes.urls')), path('computes/', include('computes.urls')),
url(r'^logs/', include('logs.urls')), path('logs/', include('logs.urls')),
url(r'^datasource/', include('datasource.urls')), path('datasource/', include('datasource.urls')),
url(r'^console/$', console, name='console'), path('console/', console, name='console'),
#url(r'^admin/', include(admin.site.urls)), # 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``. It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see 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 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 from django.core.wsgi import get_wsgi_application
application = 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``. It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see 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 import os, sys
sys.path.append('/srv/webvirtcloud')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
from django.core.wsgi import get_wsgi_application 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() application = get_wsgi_application()