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:
parent
fc8612c604
commit
4d40de1b55
98 changed files with 1525 additions and 6658 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
||||||
.vagrant
|
.vagrant
|
||||||
venv
|
venv
|
||||||
|
venv2
|
||||||
.vscode
|
.vscode
|
||||||
.idea
|
.idea
|
||||||
.DS_*
|
.DS_*
|
||||||
|
|
|
@ -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:
|
||||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -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
|
||||||
|
|
52
README.md
52
README.md
|
@ -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
17
Vagrantfile
vendored
|
@ -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
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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,),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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),
|
|
||||||
]
|
|
|
@ -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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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 = [
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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)]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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.')]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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)]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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)]),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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):
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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,),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,13 +127,31 @@ 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):
|
||||||
|
# NoVNC uses it's own convention that forward token
|
||||||
|
# from the request to a cookie header, we should check
|
||||||
|
# also for this behavior
|
||||||
|
hcookie = self.headers.get('cookie')
|
||||||
|
if hcookie:
|
||||||
cookie = Cookie.SimpleCookie()
|
cookie = Cookie.SimpleCookie()
|
||||||
cookie.load(self.headers.getheader('cookie'))
|
for hcookie_part in hcookie.split(';'):
|
||||||
if 'token' not in cookie:
|
hcookie_part = hcookie_part.lstrip()
|
||||||
self.msg('No token cookie found !')
|
try:
|
||||||
return False
|
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
|
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)
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
display: block;
|
display: block;
|
||||||
margin: auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#status {
|
#status {
|
||||||
|
|
|
@ -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');
|
||||||
|
if (spice_xfer_area != null) {
|
||||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
}
|
||||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
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,9 +132,22 @@
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
/* SPICE port event listeners
|
||||||
|
window.addEventListener('spice-port-data', function(event) {
|
||||||
|
// Here we convert data to text, but really we can obtain binary data also
|
||||||
|
var msg_text = arraybuffer_to_str(new Uint8Array(event.detail.data));
|
||||||
|
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'message text:', msg_text);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('spice-port-event', function(event) {
|
||||||
|
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
function fullscreen() {
|
function fullscreen() {
|
||||||
var screen=document.getElementById('spice-screen');
|
var screen=document.getElementById('spice-screen');
|
||||||
|
@ -199,7 +173,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);});
|
||||||
|
@ -212,6 +186,34 @@
|
||||||
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();
|
|
||||||
|
document.getElementById('connectButton').onclick = connect;
|
||||||
|
document.getElementById('show_console').onchange = toggle_console;
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% 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 %}
|
||||||
|
|
|
@ -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,19 +63,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
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 }}';
|
||||||
|
@ -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 = spice_query_var('password', '');
|
||||||
password = '{{ console_passwd | safe }}';
|
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');
|
||||||
|
if (spice_xfer_area != null) {
|
||||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
}
|
||||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
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 %}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -94,7 +94,7 @@
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"/>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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++;
|
||||||
});
|
});
|
||||||
|
|
|
@ -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):
|
||||||
|
|
||||||
|
@ -47,6 +51,7 @@ def create_instance_select_type(request, compute_id):
|
||||||
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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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,
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -722,7 +722,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</p>
|
|
||||||
{% 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>
|
||||||
|
<tr>
|
||||||
<th>{% trans "Device" %}</th>
|
<th>{% trans "Device" %}</th>
|
||||||
<th>{% trans "Used" %}</th>
|
<th>{% trans "Used" %}</th>
|
||||||
<th>{% trans "Capacity" %}</th>
|
<th>{% trans "Capacity" %}</th>
|
||||||
<th>{% trans "Storage" %}</th>
|
<th>{% trans "Storage" %}</th>
|
||||||
<th>{% trans "Source" %}</th>
|
<th>{% trans "Source" %}</th>
|
||||||
<th>{% trans "Action" %}</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()
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,15 +609,25 @@ 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:
|
||||||
|
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)
|
cache, io, discard, zeroes)
|
||||||
|
|
||||||
if not conn.get_status() == 5:
|
if not conn.get_status() == 5:
|
||||||
|
@ -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', '')
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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,),
|
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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,
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -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
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<center>
|
<div class="center-block">
|
||||||
{% if page > 1 %}
|
{% if page > 1 %}
|
||||||
<a href="{% url 'showlogspage' page|add:"-1" %}">←</a>
|
<a href="{% url 'showlogspage' page|add:"-1" %}">←</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -9,4 +9,4 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</center>
|
</div>
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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', '')
|
||||||
|
|
|
@ -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>
|
||||||
|
<tr>
|
||||||
<th style="width: 45px;">#</th>
|
<th style="width: 45px;">#</th>
|
||||||
<th>{% trans "Reference" %}</th>
|
<th>{% trans "Reference" %}</th>
|
||||||
<th>{% trans "Action" %}</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 %}
|
||||||
|
|
|
@ -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', '')
|
||||||
|
|
|
@ -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:
|
||||||
|
|
4
static/js/bootstrap-multiselect.js
vendored
4
static/js/bootstrap-multiselect.js
vendored
|
@ -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() {
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
1
static/js/plugins/flot/excanvas.min.js
vendored
1
static/js/plugins/flot/excanvas.min.js
vendored
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
|
@ -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);
|
|
|
@ -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);
|
|
|
@ -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);
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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."""
|
||||||
|
|
|
@ -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 = ''
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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'
|
|
@ -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)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue