mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +00:00
Merge pull request #308 from catborise/master
Django2 & Python3 Migrate
This commit is contained in:
commit
d9fa43463b
102 changed files with 1579 additions and 6684 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,5 +1,6 @@
|
|||
.vagrant
|
||||
venv
|
||||
venv2
|
||||
.vscode
|
||||
.idea
|
||||
.DS_*
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
- "3.6"
|
||||
env:
|
||||
- DJANGO=1.11.26
|
||||
- DJANGO=2.2.12
|
||||
install:
|
||||
- pip install -r dev/requirements.txt
|
||||
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 DEBIAN_FRONTEND=noninteractive apt-get -qyy install \
|
||||
-o APT::Install-Suggests=false \
|
||||
git python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev nginx libsasl2-modules
|
||||
git python3-virtualenv python3-dev python3-lxml virtualenv libvirt-dev zlib1g-dev nginx libsasl2-modules
|
||||
|
||||
ADD . /srv/webvirtcloud
|
||||
RUN chown -R www-data:www-data /srv/webvirtcloud
|
||||
|
||||
# Setup webvirtcloud
|
||||
RUN cd /srv/webvirtcloud && \
|
||||
virtualenv venv && \
|
||||
virtualenv --python=python3 venv && \
|
||||
. venv/bin/activate && \
|
||||
pip install -U pip && \
|
||||
pip install -r conf/requirements.txt && \
|
||||
pip3 install -U pip && \
|
||||
pip3 install -r conf/requirements.txt && \
|
||||
chown -R www-data:www-data /srv/webvirtcloud
|
||||
|
||||
RUN cd /srv/webvirtcloud && . venv/bin/activate && \
|
||||
python manage.py migrate && \
|
||||
python3 manage.py migrate && \
|
||||
chown -R www-data:www-data /srv/webvirtcloud
|
||||
|
||||
# Setup Nginx
|
||||
|
|
56
README.md
56
README.md
|
@ -1,5 +1,5 @@
|
|||
## WebVirtCloud
|
||||
|
||||
###### Python3 & Django 2.2.12
|
||||
|
||||
## Features
|
||||
* QEMU/KVM Hypervisor Management
|
||||
|
@ -32,16 +32,16 @@ WebVirtCloud is a virtualization web interface for admins and users. It can dele
|
|||
### Generate secret key
|
||||
You should generate SECRET_KEY after cloning repo. Then put it into webvirtcloud/settings.py.
|
||||
|
||||
```python
|
||||
```python3
|
||||
import random, string
|
||||
haystack = string.ascii_letters + string.digits + string.punctuation
|
||||
print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)]))
|
||||
```
|
||||
|
||||
### Install WebVirtCloud panel (Ubuntu)
|
||||
### Install WebVirtCloud panel (Ubuntu 18.04+ LTS)
|
||||
|
||||
```bash
|
||||
sudo apt-get -y install git virtualenv python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python-guestfs
|
||||
sudo apt-get -y install git virtualenv python3-virtualenv python3-dev python3-lxml libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs
|
||||
git clone https://github.com/retspen/webvirtcloud
|
||||
cd webvirtcloud
|
||||
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
|
||||
|
@ -52,10 +52,10 @@ cd ..
|
|||
sudo mv webvirtcloud /srv
|
||||
sudo chown -R www-data:www-data /srv/webvirtcloud
|
||||
cd /srv/webvirtcloud
|
||||
virtualenv venv
|
||||
virtualenv -p python3 venv
|
||||
source venv/bin/activate
|
||||
pip install -r conf/requirements.txt
|
||||
python manage.py migrate
|
||||
python3 manage.py migrate
|
||||
sudo chown -R www-data:www-data /srv/webvirtcloud
|
||||
sudo rm /etc/nginx/sites-enabled/default
|
||||
```
|
||||
|
@ -76,11 +76,11 @@ Done!!
|
|||
|
||||
Go to http://serverip and you should see the login screen.
|
||||
|
||||
|
||||
### Install WebVirtCloud panel (CentOS)
|
||||
### Install WebVirtCloud panel (CentOS8/OEL8)
|
||||
|
||||
```bash
|
||||
sudo yum -y install python-virtualenv python-devel libvirt-devel glibc gcc nginx supervisor python-lxml git python-libguestfs iproute-tc
|
||||
sudo yum -y install epel-release
|
||||
sudo yum -y install python3-virtualenv python3-devel libvirt-devel glibc gcc nginx supervisor python3-lxml git python3-libguestfs iproute-tc cyrus-sasl-md5 python3-libguestfs
|
||||
```
|
||||
|
||||
#### Creating directories and cloning repo
|
||||
|
@ -90,15 +90,17 @@ sudo mkdir /srv && cd /srv
|
|||
sudo git clone https://github.com/retspen/webvirtcloud && cd webvirtcloud
|
||||
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
|
||||
# now put secret key to webvirtcloud/settings.py
|
||||
# create secret key manually or use that command
|
||||
sudo sed -r "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
|
||||
```
|
||||
|
||||
#### Start installation webvirtcloud
|
||||
```
|
||||
sudo virtualenv venv
|
||||
sudo source venv/bin/activate
|
||||
sudo venv/bin/pip install -r conf/requirements.txt
|
||||
sudo cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
|
||||
sudo venv/bin/python manage.py migrate
|
||||
virtualenv-3 venv
|
||||
source venv/bin/activate
|
||||
pip3 install -r conf/requirements.txt
|
||||
cp conf/nginx/webvirtcloud.conf /etc/nginx/conf.d/
|
||||
python3 manage.py migrate
|
||||
```
|
||||
|
||||
#### Configure the supervisor for CentOS
|
||||
|
@ -115,7 +117,7 @@ autorestart=true
|
|||
redirect_stderr=true
|
||||
|
||||
[program:novncd]
|
||||
command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd
|
||||
command=/srv/webvirtcloud/venv/bin/python3 /srv/webvirtcloud/console/novncd
|
||||
directory=/srv/webvirtcloud
|
||||
user=nginx
|
||||
autostart=true
|
||||
|
@ -191,11 +193,20 @@ Change permission for selinux:
|
|||
|
||||
```bash
|
||||
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webvirtcloud(/.*)"
|
||||
sudo setsebool -P httpd_can_network_connect on -P
|
||||
```
|
||||
|
||||
Add required user to the kvm group:
|
||||
Add required user to the kvm group(if you not install with root):
|
||||
```bash
|
||||
sudo usermod -G kvm -a webvirtmgr
|
||||
sudo usermod -G kvm -a <username>
|
||||
```
|
||||
|
||||
Allow http ports on firewall:
|
||||
```bash
|
||||
sudo firewall-cmd --add-service=http
|
||||
sudo firewall-cmd --add-service=http --permanent
|
||||
sudo firewall-cmd --add-port=6080/tcp
|
||||
sudo firewall-cmd --add-port=6080/tcp --permanent
|
||||
```
|
||||
|
||||
Let's restart nginx and the supervisord services:
|
||||
|
@ -226,7 +237,7 @@ Done!!
|
|||
|
||||
Go to http://serverip and you should see the login screen.
|
||||
|
||||
### Alternative running novncd via runit
|
||||
### Alternative running novncd via runit(Debian)
|
||||
Alternative to running nonvcd via supervisor is runit.
|
||||
|
||||
On Debian systems install runit and configure novncd service
|
||||
|
@ -272,11 +283,12 @@ datasource:
|
|||
|
||||
### How To Update
|
||||
```bash
|
||||
cd <installation-directory>
|
||||
sudo source venv/bin/activate
|
||||
# Go to Installation Directory
|
||||
cd /srv/webvirtcloud
|
||||
source venv/bin/activate
|
||||
git pull
|
||||
pip install -U -r conf/requirements.txt
|
||||
python manage.py migrate
|
||||
pip3 install -U -r conf/requirements.txt
|
||||
python3 manage.py migrate
|
||||
sudo service supervisor restart
|
||||
```
|
||||
### Screenshots
|
||||
|
|
17
Vagrantfile
vendored
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 service libvirt-bin restart
|
||||
sudo adduser vagrant libvirtd
|
||||
sudo apt-get -y install python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev
|
||||
virtualenv /vagrant/venv
|
||||
sudo apt-get -y install python3-virtualenv virtualenv python3-pip python3-dev python3-lxml libvirt-dev zlib1g-dev python3-guestfs
|
||||
virtualenv -p python3 /vagrant/venv
|
||||
source /vagrant/venv/bin/activate
|
||||
pip install -r /vagrant/dev/requirements.txt
|
||||
pip3 install -r /vagrant/dev/requirements.txt
|
||||
SHELL
|
||||
end
|
||||
# To start this machine run "vagrant up prod"
|
||||
|
@ -24,6 +24,7 @@ Vagrant.configure(2) do |config|
|
|||
prod.vm.box = "ubuntu/bionic64"
|
||||
prod.vm.hostname = "webvirtcloud"
|
||||
prod.vm.network "private_network", ip: "192.168.33.11"
|
||||
prod.vm.network "forwarded_port", guest: 80, host: 8081
|
||||
#prod.vm.synced_folder ".", "/srv/webvirtcloud"
|
||||
prod.vm.provision "shell", inline: <<-SHELL
|
||||
sudo mkdir /srv/webvirtcloud
|
||||
|
@ -33,15 +34,15 @@ Vagrant.configure(2) do |config|
|
|||
sudo service libvirt-bin restart
|
||||
sudo adduser vagrant libvirtd
|
||||
sudo chown -R vagrant:vagrant /srv/webvirtcloud
|
||||
sudo apt-get -y install python-virtualenv python-dev python-lxml python-pip libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python-guestfs
|
||||
virtualenv /srv/webvirtcloud/venv
|
||||
sudo apt-get -y install python3-virtualenv python3-dev python3-lxml python3-pip virtualenv libvirt-dev zlib1g-dev libxslt1-dev nginx supervisor libsasl2-modules gcc pkg-config python3-guestfs
|
||||
virtualenv -p python3 /srv/webvirtcloud/venv
|
||||
source /srv/webvirtcloud/venv/bin/activate
|
||||
pip install -r /srv/webvirtcloud/dev/requirements.txt
|
||||
pip3 install -r /srv/webvirtcloud/requirements.txt
|
||||
sudo cp /srv/webvirtcloud/conf/supervisor/webvirtcloud.conf /etc/supervisor/conf.d
|
||||
sudo cp /srv/webvirtcloud/conf/nginx/webvirtcloud.conf /etc/nginx/conf.d
|
||||
sudo cp /srv/webvirtcloud/webvirtcloud/settings.py.template /srv/webvirtcloud/webvirtcloud/settings.py
|
||||
sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
|
||||
python /srv/webvirtcloud/manage.py migrate
|
||||
sudo sed "s/SECRET_KEY = ''/SECRET_KEY = '"`python3 /srv/webvirtcloud/conf/runit/secret_generator.py`"'/" -i /srv/webvirtcloud/webvirtcloud/settings.py
|
||||
python3 /srv/webvirtcloud/manage.py migrate
|
||||
sudo rm /etc/nginx/sites-enabled/default
|
||||
sudo chown -R www-data:www-data /srv/webvirtcloud
|
||||
sudo service nginx restart
|
||||
|
|
|
@ -9,7 +9,8 @@ class UserAddForm(forms.Form):
|
|||
name = forms.CharField(label="Name",
|
||||
error_messages={'required': _('No User name has been entered')},
|
||||
max_length=20)
|
||||
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD, error_messages={'required': _('No password has been entered')},)
|
||||
password = forms.CharField(required=not settings.ALLOW_EMPTY_PASSWORD,
|
||||
error_messages={'required': _('No password has been entered')},)
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
|
|
|
@ -1,29 +1,51 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('instances', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UserSSHKey',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('keyname', models.CharField(max_length=25)),
|
||||
('keypublic', models.CharField(max_length=500)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserInstance',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('is_change', models.BooleanField(default=False)),
|
||||
('is_delete', models.BooleanField(default=False)),
|
||||
('instance', models.ForeignKey(to='instances.Instance')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
('is_vnc', models.BooleanField(default=False)),
|
||||
('instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='instances.Instance')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserAttributes',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('can_clone_instances', models.BooleanField(default=True)),
|
||||
('max_instances', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
|
||||
('max_cpus', models.IntegerField(default=1, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
|
||||
('max_memory', models.IntegerField(default=2048, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
|
||||
('max_disk_size', models.IntegerField(default=20, help_text='-1 for unlimited. Any integer value', validators=[django.core.validators.MinValueValidator(-1)])),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
@ -8,9 +7,7 @@ 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()
|
||||
)
|
||||
User.objects.create_superuser('admin', None, 'admin', last_login=timezone.now())
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
|
@ -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.conf import settings
|
||||
from instances.models import Instance
|
||||
from django.core.validators import MinValueValidator
|
||||
|
||||
|
||||
class UserInstance(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
instance = models.ForeignKey(Instance, on_delete=models.CASCADE)
|
||||
is_change = models.BooleanField(default=False)
|
||||
is_delete = models.BooleanField(default=False)
|
||||
is_vnc = models.BooleanField(default=False)
|
||||
class UserInstance(Model):
|
||||
user = ForeignKey(User, on_delete=CASCADE)
|
||||
instance = ForeignKey(Instance, on_delete=CASCADE)
|
||||
is_change = BooleanField(default=False)
|
||||
is_delete = BooleanField(default=False)
|
||||
is_vnc = BooleanField(default=False)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.instance.name
|
||||
|
||||
|
||||
class UserSSHKey(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)
|
||||
keyname = models.CharField(max_length=25)
|
||||
keypublic = models.CharField(max_length=500)
|
||||
class UserSSHKey(Model):
|
||||
user = ForeignKey(User, on_delete=DO_NOTHING)
|
||||
keyname = CharField(max_length=25)
|
||||
keypublic = CharField(max_length=500)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.keyname
|
||||
|
||||
class UserAttributes(models.Model):
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||
can_clone_instances = models.BooleanField(default=True)
|
||||
max_instances = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1),])
|
||||
max_cpus = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
max_memory = models.IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
max_disk_size = models.IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
|
||||
class UserAttributes(Model):
|
||||
user = OneToOneField(User, on_delete=CASCADE)
|
||||
can_clone_instances = BooleanField(default=True)
|
||||
max_instances = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1), ])
|
||||
max_cpus = IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
max_memory = IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
max_disk_size = IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[MinValueValidator(-1)])
|
||||
|
||||
@staticmethod
|
||||
def create_missing_userattributes(user):
|
||||
|
|
|
@ -87,8 +87,8 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans "VNC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select type="text" class="form-control" name="inst_vnc">
|
||||
<option value="">False</option>
|
||||
<select class="form-control" name="inst_vnc">
|
||||
<option value="">{% trans 'False' %}</option>
|
||||
<option value="1" {% if inst.is_vnc %}selected{% endif %}>True</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -96,8 +96,8 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans "Resize" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select type="text" class="form-control" name="inst_change">
|
||||
<option value="">False</option>
|
||||
<select class="form-control" name="inst_change">
|
||||
<option value="">{% trans 'False' %}</option>
|
||||
<option value="1" {% if inst.is_change %}selected{% endif %}>True</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -105,8 +105,8 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans "Delete" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select type="text" class="form-control" name="inst_delete">
|
||||
<option value="">False</option>
|
||||
<select class="form-control" name="inst_delete">
|
||||
<option value="">{% trans 'False' %}</option>
|
||||
<option value="1" {% if inst.is_delete %}selected{% endif %}>True</option>
|
||||
</select>
|
||||
</div>
|
||||
|
@ -124,7 +124,7 @@
|
|||
<td style="width:5px;">
|
||||
<form action="" method="post" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="user_inst" value="{{ inst.id }}">
|
||||
<button type="submit" class="btn btn-xs btn-default" name="delete" tittle="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<button type="submit" class="btn btn-xs btn-default" name="delete" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from django.contrib.auth import views as auth_views
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
|
||||
url(r'^logout/$', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
|
||||
url(r'^profile/$', views.profile, name='profile'), url(r'^$', views.accounts, name='accounts'),
|
||||
url(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
|
||||
path('login/', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
|
||||
path('logout/', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
|
||||
path('profile/', views.profile, name='profile'), path('', views.accounts, name='accounts'),
|
||||
re_path(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
|
||||
]
|
||||
|
|
|
@ -151,10 +151,8 @@ class ComputeEditHostForm(forms.Form):
|
|||
|
||||
|
||||
class ComputeAddSocketForm(forms.Form):
|
||||
name = forms.CharField(error_messages={'required': _('No hostname has been entered')},
|
||||
max_length=64)
|
||||
details = forms.CharField(error_messages={'required': _('No details has been entred')},
|
||||
max_length=50)
|
||||
name = forms.CharField(error_messages={'required': _('No hostname has been entered')}, max_length=64)
|
||||
details = forms.CharField(error_messages={'required': _('No details has been entred')}, max_length=50)
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
|
@ -13,15 +14,13 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Compute',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=20)),
|
||||
('hostname', models.CharField(max_length=20)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64)),
|
||||
('hostname', models.CharField(max_length=64)),
|
||||
('login', models.CharField(max_length=20)),
|
||||
('password', models.CharField(max_length=14, null=True, blank=True)),
|
||||
('password', models.CharField(blank=True, max_length=14, null=True)),
|
||||
('details', models.CharField(blank=True, max_length=64, null=True)),
|
||||
('type', models.IntegerField()),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
name = models.CharField(max_length=64)
|
||||
hostname = models.CharField(max_length=64)
|
||||
login = models.CharField(max_length=20)
|
||||
password = models.CharField(max_length=14, blank=True, null=True)
|
||||
details = models.CharField(max_length=64, null=True, blank=True)
|
||||
type = models.IntegerField()
|
||||
class Compute(Model):
|
||||
name = CharField(max_length=64)
|
||||
hostname = CharField(max_length=64)
|
||||
login = CharField(max_length=20)
|
||||
password = CharField(max_length=14, blank=True, null=True)
|
||||
details = CharField(max_length=64, null=True, blank=True)
|
||||
type = IntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.hostname
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from storages.views import storages, storage, get_volumes
|
||||
from networks.views import networks, network
|
||||
from secrets.views import secrets
|
||||
|
@ -9,23 +9,24 @@ from instances.views import instances
|
|||
from nwfilters.views import nwfilter, nwfilters
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', computes, name='computes'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/volumes$', get_volumes, name='volumes'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance_select_type, name='create_instance_select_type'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/create/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)$', create_instance, name='create_instance'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines$', get_compute_machine_types, name='machines'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/disks/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'),
|
||||
path('', computes, name='computes'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/volumes$', get_volumes, name='volumes'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/create/$', create_instance_select_type, name='create_instance_select_type'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/create/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)$', create_instance, name='create_instance'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines$', get_compute_machine_types, name='machines'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/disks/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'),
|
||||
]
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# gstfsd - WebVirtCloud daemon for managing VM's filesystem
|
||||
#
|
||||
|
||||
import SocketServer
|
||||
import socketserver
|
||||
import json
|
||||
import guestfs
|
||||
import re
|
||||
|
@ -13,11 +13,11 @@ PORT = 16510
|
|||
ADDRESS = "0.0.0.0"
|
||||
|
||||
|
||||
class MyTCPServer(SocketServer.ThreadingTCPServer):
|
||||
class MyTCPServer(socketserver.ThreadingTCPServer):
|
||||
allow_reuse_address = True
|
||||
|
||||
|
||||
class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
||||
class MyTCPServerHandler(socketserver.BaseRequestHandler):
|
||||
def handle(self):
|
||||
# recive data
|
||||
data = json.loads(self.request.recv(1024).strip())
|
||||
|
@ -42,9 +42,9 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
|||
if data['action'] == 'publickey':
|
||||
if not gfs.is_dir('/root/.ssh'):
|
||||
gfs.mkdir('/root/.ssh')
|
||||
gfs.chmod(0700, "/root/.ssh")
|
||||
gfs.chmod(700, "/root/.ssh")
|
||||
gfs.write('/root/.ssh/authorized_keys', data['key'])
|
||||
gfs.chmod(0600, '/root/.ssh/authorized_keys')
|
||||
gfs.chmod(600, '/root/.ssh/authorized_keys')
|
||||
self.request.sendall(json.dumps({'return': 'success'}))
|
||||
gfs.umount(part)
|
||||
except RuntimeError:
|
||||
|
@ -52,7 +52,7 @@ class MyTCPServerHandler(SocketServer.BaseRequestHandler):
|
|||
gfs.shutdown()
|
||||
gfs.close()
|
||||
except RuntimeError as err:
|
||||
self.request.sendall(json.dumps({'return': 'error', 'message': err.message}))
|
||||
self.request.sendall(json.dumps({'return': 'error', 'message': err}))
|
||||
|
||||
|
||||
server = MyTCPServer((ADDRESS, PORT), MyTCPServerHandler)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
Django==1.11.28
|
||||
Django==2.2.12
|
||||
websockify==0.9.0
|
||||
gunicorn==19.9.0
|
||||
lxml==4.4.2
|
||||
libvirt-python==5.10.0
|
||||
gunicorn==20.0.4
|
||||
lxml==4.5.0
|
||||
libvirt-python==6.1.0
|
||||
six
|
||||
pytz
|
||||
rwlock
|
||||
|
|
|
@ -6,7 +6,7 @@ RUNAS=`which setuser`
|
|||
USER=www-data
|
||||
|
||||
DJANGO_PROJECT=/srv/webvirtcloud
|
||||
PYTHON=$DJANGO_PROJECT/venv/bin/python
|
||||
PYTHON=$DJANGO_PROJECT/venv/bin/python3
|
||||
NOVNCD=$DJANGO_PROJECT/console/novncd
|
||||
|
||||
# make novncd debug, verbose
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import random, string
|
||||
|
||||
haystack = string.ascii_letters + string.digits + string.punctuation
|
||||
print(''.join([random.SystemRandom().choice(haystack.replace('/','').replace('\'', '').replace('\"','')) for _ in range(50)]))
|
||||
print(''.join([random.SystemRandom().choice(haystack.replace('/', '').replace('\'', '').replace('\"', '')) for _ in range(50)]))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[program:gstfsd]
|
||||
command=/usr/bin/python /usr/local/bin/gstfsd
|
||||
command=/srv/webvirtcloud/venv/bin/python3 /usr/local/bin/gstfsd
|
||||
directory=/usr/local/bin
|
||||
user=root
|
||||
autostart=true
|
||||
|
|
|
@ -7,7 +7,7 @@ autorestart=true
|
|||
redirect_stderr=true
|
||||
|
||||
[program:novncd]
|
||||
command=/srv/webvirtcloud/venv/bin/python /srv/webvirtcloud/console/novncd
|
||||
command=/srv/webvirtcloud/venv/bin/python3 /srv/webvirtcloud/console/novncd
|
||||
directory=/srv/webvirtcloud
|
||||
user=www-data
|
||||
autostart=true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
@ -7,7 +7,7 @@ import django
|
|||
|
||||
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_PATH = os.path.abspath(os.path.join(DIR_PATH, '..', ''))
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "webvirtcloud.settings")
|
||||
os.environ["DJANGO_SETTINGS_MODULE"] = "webvirtcloud.settings"
|
||||
CERT = DIR_PATH + '/cert.pem'
|
||||
|
||||
if ROOT_PATH not in sys.path:
|
||||
|
@ -15,16 +15,16 @@ if ROOT_PATH not in sys.path:
|
|||
|
||||
django.setup()
|
||||
|
||||
# VENV_PATH = ROOT_PATH + '/venv/lib/python2.7/site-packages'
|
||||
# VENV_PATH = ROOT_PATH + '/venv/lib/python3.6/site-packages'
|
||||
# if VENV_PATH not in sys.path:
|
||||
# sys.path.append(VENV_PATH)
|
||||
|
||||
import re
|
||||
import Cookie
|
||||
import socket
|
||||
from six.moves import http_cookies as Cookie
|
||||
from webvirtcloud.settings import WS_PORT, WS_HOST, WS_CERT
|
||||
from vrtManager.connection import CONN_SSH, CONN_SOCKET
|
||||
from tunnel import Tunnel
|
||||
from console.tunnel import Tunnel
|
||||
from optparse import OptionParser
|
||||
|
||||
parser = OptionParser()
|
||||
|
@ -127,12 +127,30 @@ def get_connection_infos(token):
|
|||
|
||||
class CompatibilityMixIn(object):
|
||||
def _new_client(self, daemon, socket_factory):
|
||||
cookie = Cookie.SimpleCookie()
|
||||
cookie.load(self.headers.getheader('cookie'))
|
||||
if 'token' not in cookie:
|
||||
self.msg('No token cookie found !')
|
||||
return False
|
||||
token = cookie['token'].value
|
||||
# NoVNC uses it's own convention that forward token
|
||||
# from the request to a cookie header, we should check
|
||||
# also for this behavior
|
||||
hcookie = self.headers.get('cookie')
|
||||
if hcookie:
|
||||
cookie = Cookie.SimpleCookie()
|
||||
for hcookie_part in hcookie.split(';'):
|
||||
hcookie_part = hcookie_part.lstrip()
|
||||
try:
|
||||
cookie.load(hcookie_part)
|
||||
except Cookie.CookieError:
|
||||
# NOTE(stgleb): Do not print out cookie content
|
||||
# for security reasons.
|
||||
self.msg('Found malformed cookie')
|
||||
else:
|
||||
if 'token' in cookie:
|
||||
token = cookie['token'].value
|
||||
|
||||
# cookie = Cookie.SimpleCookie()
|
||||
# cookie.load(self.headers.getheader('cookie'))
|
||||
# if 'token' not in cookie:
|
||||
# self.msg('No token cookie found !')
|
||||
# return False
|
||||
# token = cookie['token'].value
|
||||
|
||||
(connhost, connport, connuser, conntype, console_host, console_port,
|
||||
console_socket) = get_connection_infos(token)
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#status {
|
||||
|
|
|
@ -25,65 +25,28 @@
|
|||
{% extends "console-base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block head %}
|
||||
<title>WebVirtCloud - Spice Client - Full</title>
|
||||
<script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/enums.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/utils.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/png.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/lz.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/quic.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/bitmap.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicetype.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/wire.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/display.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/port.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/main.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/inputs.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/webm.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/playback.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/cursor.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/ticket.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/resize.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="login" hidden>
|
||||
<span class="logo">SPICE</span>
|
||||
<label for="host">{% trans 'Host' %}:</label> <input type='text' id='host' value='{{ ws_host }}'> <!-- localhost -->
|
||||
<label for="port">{% trans 'Port' %}:</label> <input type='text' id='port' value='{{ ws_port }}'>
|
||||
<label for="password">{% trans 'Password' %}:</label> <input type='password' id='password' value='{{ console_passwd }}'>
|
||||
<label for="show_console">{% trans 'Show console' %}</label><input type="checkbox" id="show_console" value="1" onchange="toggle_console()" checked>
|
||||
<button id="connectButton" onclick="connect();">{% trans 'Start' %}</button>
|
||||
</div>
|
||||
|
||||
<div id="spice-area">
|
||||
<div id="spice-screen" class="spice-screen"></div>
|
||||
<div id="message-div" class="spice-message"></div>
|
||||
</div>
|
||||
|
||||
<div id="debug-div">
|
||||
<!-- If DUMPXXX is turned on, dumped images will go here -->
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block foot %}
|
||||
|
||||
<!-- ES2015/ES6 modules polyfill -->
|
||||
<script type="module">
|
||||
window._spice_has_module_support = true;
|
||||
</script>
|
||||
<script>
|
||||
window.addEventListener("load", function() {
|
||||
if (window._spice_has_module_support) return;
|
||||
var loader = document.createElement("script");
|
||||
loader.src = '{% static "thirdparty/browser-es-module-loader/dist/browser-es-module-loader.js" %}';
|
||||
document.head.appendChild(loader);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="module" crossorigin="anonymous">
|
||||
|
||||
import * as SpiceHtml5 from '{% static "js/spice-html5/main.js" %}';
|
||||
|
||||
var host = null, port = null;
|
||||
var sc;
|
||||
|
||||
|
@ -96,9 +59,6 @@
|
|||
|
||||
host = document.getElementById("host").value;
|
||||
port = document.getElementById("port").value;
|
||||
if (window.location.protocol == 'https:') {
|
||||
scheme = "wss://";
|
||||
}
|
||||
password = document.getElementById("password").value;
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
|
@ -116,11 +76,10 @@
|
|||
document.getElementById('connectButton').onclick = disconnect;
|
||||
|
||||
try {
|
||||
sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
sc = new SpiceHtml5.SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
catch (e) {
|
||||
alert(e.toString());
|
||||
disconnect();
|
||||
}
|
||||
|
@ -135,25 +94,27 @@
|
|||
document.getElementById('connectButton').onclick = connect;
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
var spice_xfer_area = document.getElementById('spice-xfer-area');
|
||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
||||
if (spice_xfer_area != null) {
|
||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||
}
|
||||
document.getElementById('spice-area').removeEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
|
||||
document.getElementById('spice-area').removeEventListener('drop', SpiceHtml5.handle_file_drop, false);
|
||||
}
|
||||
console.log("<< disconnect");
|
||||
}
|
||||
|
||||
function agent_connected(sc) {
|
||||
window.addEventListener('resize', handle_resize);
|
||||
window.addEventListener('resize', SpiceHtml5.handle_resize);
|
||||
window.spice_connection = this;
|
||||
|
||||
resize_helper(this);
|
||||
SpiceHtml5.resize_helper(this);
|
||||
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
var spice_xfer_area = document.createElement("div");
|
||||
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
|
||||
document.getElementById('spice-area').appendChild(spice_xfer_area);
|
||||
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
|
||||
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
|
||||
document.getElementById('spice-area').addEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
|
||||
document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
|
||||
}
|
||||
else {
|
||||
console.log("File API is not supported");
|
||||
|
@ -171,20 +132,9 @@
|
|||
m.style.display = 'none';
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handle_resize);
|
||||
resize_helper(sc);
|
||||
}
|
||||
|
||||
function fullscreen() {
|
||||
var screen=document.getElementById('spice-screen');
|
||||
if(screen.requestFullscreen) {
|
||||
screen.requestFullscreen();
|
||||
} else if(screen.mozRequestFullScreen) {
|
||||
screen.mozRequestFullScreen();
|
||||
} else if(screen.webkitRequestFullscreen) {
|
||||
screen.webkitRequestFullscreen();
|
||||
} else if(screen.msRequestFullscreen) {
|
||||
screen.msRequestFullscreen();
|
||||
window.addEventListener('resize', SpiceHtml5.handle_resize);
|
||||
if (sc) {
|
||||
SpiceHtml5.resize_helper(sc);
|
||||
}
|
||||
}
|
||||
/* SPICE port event listeners
|
||||
|
@ -198,20 +148,72 @@
|
|||
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent);
|
||||
});
|
||||
*/
|
||||
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
|
||||
document.getElementById('ctrlaltdel').addEventListener('click', sendCtrlAltDel);
|
||||
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
|
||||
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
|
||||
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
|
||||
document.getElementById('ctrlaltf4').addEventListener('click', function(){sendCtrlAltFN(3);});
|
||||
document.getElementById('ctrlaltf5').addEventListener('click', function(){sendCtrlAltFN(4);});
|
||||
document.getElementById('ctrlaltf6').addEventListener('click', function(){sendCtrlAltFN(5);});
|
||||
document.getElementById('ctrlaltf7').addEventListener('click', function(){sendCtrlAltFN(6);});
|
||||
document.getElementById('ctrlaltf8').addEventListener('click', function(){sendCtrlAltFN(7);});
|
||||
document.getElementById('ctrlaltf9').addEventListener('click', function(){sendCtrlAltFN(8);});
|
||||
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
|
||||
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
|
||||
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
|
||||
connect();
|
||||
</script>
|
||||
|
||||
function fullscreen() {
|
||||
var screen=document.getElementById('spice-screen');
|
||||
if(screen.requestFullscreen) {
|
||||
screen.requestFullscreen();
|
||||
} else if(screen.mozRequestFullScreen) {
|
||||
screen.mozRequestFullScreen();
|
||||
} else if(screen.webkitRequestFullscreen) {
|
||||
screen.webkitRequestFullscreen();
|
||||
} else if(screen.msRequestFullscreen) {
|
||||
screen.msRequestFullscreen();
|
||||
}
|
||||
}
|
||||
/* SPICE port event listeners
|
||||
window.addEventListener('spice-port-data', function(event) {
|
||||
// Here we convert data to text, but really we can obtain binary data also
|
||||
var msg_text = arraybuffer_to_str(new Uint8Array(event.detail.data));
|
||||
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'message text:', msg_text);
|
||||
});
|
||||
|
||||
window.addEventListener('spice-port-event', function(event) {
|
||||
DEBUG > 0 && console.log('SPICE port', event.detail.channel.portName, 'event data:', event.detail.spiceEvent);
|
||||
});
|
||||
*/
|
||||
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
|
||||
document.getElementById('ctrlaltdel').addEventListener('click', function(){sendCtrlAltDel(sc);});
|
||||
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
|
||||
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
|
||||
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
|
||||
document.getElementById('ctrlaltf4').addEventListener('click', function(){sendCtrlAltFN(3);});
|
||||
document.getElementById('ctrlaltf5').addEventListener('click', function(){sendCtrlAltFN(4);});
|
||||
document.getElementById('ctrlaltf6').addEventListener('click', function(){sendCtrlAltFN(5);});
|
||||
document.getElementById('ctrlaltf7').addEventListener('click', function(){sendCtrlAltFN(6);});
|
||||
document.getElementById('ctrlaltf8').addEventListener('click', function(){sendCtrlAltFN(7);});
|
||||
document.getElementById('ctrlaltf9').addEventListener('click', function(){sendCtrlAltFN(8);});
|
||||
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
|
||||
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
|
||||
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
|
||||
|
||||
document.getElementById('connectButton').onclick = connect;
|
||||
document.getElementById('show_console').onchange = toggle_console;
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="login">
|
||||
<span class="logo">SPICE</span>
|
||||
<label for="host">{% trans 'Host' %}:</label> <input type='text' id='host' value='{{ ws_host }}'> <!-- localhost -->
|
||||
<label for="port">{% trans 'Port' %}:</label> <input type='text' id='port' value='{{ ws_port }}'>
|
||||
<label for="password">{% trans 'Password' %}:</label> <input type='password' id='password' value='{{ console_passwd }}'>
|
||||
<label for="show_console">{% trans 'Show console' %}</label><input type="checkbox" id="show_console" value="1" onchange="toggle_console()" checked>
|
||||
<button id="connectButton">{% trans 'Start' %}</button>
|
||||
</div>
|
||||
|
||||
<div id="spice-area">
|
||||
<div id="spice-screen" class="spice-screen"></div>
|
||||
</div>
|
||||
|
||||
<div id="message-div" class="spice-message"></div>
|
||||
|
||||
<div id="debug-div">
|
||||
<!-- If DUMPXXX is turned on, dumped images will go here -->
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block foot %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -25,56 +25,27 @@
|
|||
{% extends "console-base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block head %}
|
||||
|
||||
<title>WebVirtCloud - Spice - Lite</title>
|
||||
<script src="{% static "js/spice-html5/spicearraybuffer.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/enums.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/atKeynames.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/utils.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/png.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/lz.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/quic.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/bitmap.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicedataview.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicetype.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spicemsg.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/wire.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/spiceconn.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/display.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/main.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/inputs.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/webm.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/playback.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/simulatecursor.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/cursor.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/jsbn.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/rsa.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/prng4.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/rng.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/thirdparty/sha1.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/ticket.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/resize.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/filexfer.js" %}"></script>
|
||||
<script src="{% static "js/spice-html5/port.js" %}"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="spice-area">
|
||||
<div id="spice-screen" class="spice-screen"></div>
|
||||
<div id="message-div" class="spice-message"></div>
|
||||
</div>
|
||||
|
||||
<div id="debug-div">
|
||||
<!-- If DUMPXXX is turned on, dumped images will go here -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block foot %}
|
||||
<!-- ES2015/ES6 modules polyfill -->
|
||||
<script type="module">
|
||||
window._spice_has_module_support = true;
|
||||
</script>
|
||||
<script>
|
||||
window.addEventListener("load", function() {
|
||||
if (window._spice_has_module_support) return;
|
||||
var loader = document.createElement("script");
|
||||
loader.src = '{% static "thirdparty/browser-es-module-loader/dist/browser-es-module-loader.js" %}';
|
||||
document.head.appendChild(loader);
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="module" crossorigin="anonymous">
|
||||
import * as SpiceHtml5 from '{% static "js/spice-html5/main.js" %}';
|
||||
|
||||
var host = null, port = null;
|
||||
var sc;
|
||||
|
||||
|
@ -92,21 +63,18 @@
|
|||
}
|
||||
|
||||
function spice_error(e) {
|
||||
console.log(e);
|
||||
disconnect();
|
||||
if (e.message !== undefined) {
|
||||
log_error(e.message);
|
||||
}
|
||||
else {
|
||||
log_error('Unknown error');
|
||||
if (e !== undefined && e.message === "Permission denied.") {
|
||||
var pass = prompt("Password");
|
||||
connect(pass);
|
||||
}
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var host, port, password, scheme = "ws://", uri;
|
||||
console.log('>> connect');
|
||||
function connect(password) {
|
||||
var host, port, scheme = "ws://", uri;
|
||||
|
||||
// By default, use the host and port of server that served this file
|
||||
//host = spice_query_var('host', window.location.hostname);
|
||||
// host = spice_query_var('host', window.location.hostname);
|
||||
host = '{{ ws_host| safe }}';
|
||||
|
||||
// Note that using the web server port only makes sense
|
||||
|
@ -129,16 +97,18 @@
|
|||
|
||||
// If a token variable is passed in, set the parameter in a cookie.
|
||||
// This is used by nova-spiceproxy.
|
||||
token = spice_query_var('token', null);
|
||||
var token = spice_query_var('token', null);
|
||||
if (token) {
|
||||
spice_set_cookie('token', token, 1)
|
||||
}
|
||||
|
||||
//password = spice_query_var('password', '');
|
||||
password = '{{ console_passwd | safe }}';
|
||||
if (password === undefined) {
|
||||
password = spice_query_var('password', '');
|
||||
password = '{{ console_passwd | safe }}';
|
||||
}
|
||||
if (password === 'None') password = '';
|
||||
|
||||
path = spice_query_var('path', 'websockify');
|
||||
var path = spice_query_var('path', 'websockify');
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
console.log("must specify host and port in URL");
|
||||
|
@ -156,43 +126,44 @@
|
|||
}
|
||||
|
||||
try {
|
||||
sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
sc = new SpiceHtml5.SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected });
|
||||
}
|
||||
catch (e) {
|
||||
alert(e.toString());
|
||||
disconnect();
|
||||
}
|
||||
console.log('<< connect')
|
||||
|
||||
}
|
||||
|
||||
function disconnect()
|
||||
{
|
||||
function disconnect() {
|
||||
console.log(">> disconnect");
|
||||
if (sc) {
|
||||
sc.stop();
|
||||
}
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
var spice_xfer_area = document.getElementById('spice-xfer-area');
|
||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
||||
if (spice_xfer_area != null) {
|
||||
document.getElementById('spice-area').removeChild(spice_xfer_area);
|
||||
}
|
||||
document.getElementById('spice-area').removeEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
|
||||
document.getElementById('spice-area').removeEventListener('drop', SpiceHtml5.handle_file_drop, false);
|
||||
}
|
||||
console.log("<< disconnect");
|
||||
}
|
||||
|
||||
function agent_connected(sc) {
|
||||
window.addEventListener('resize', handle_resize);
|
||||
window.addEventListener('resize', SpiceHtml5.handle_resize);
|
||||
window.spice_connection = this;
|
||||
|
||||
resize_helper(this);
|
||||
SpiceHtml5.resize_helper(this);
|
||||
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob) {
|
||||
var spice_xfer_area = document.createElement("div");
|
||||
spice_xfer_area.setAttribute('id', 'spice-xfer-area');
|
||||
document.getElementById('spice-area').appendChild(spice_xfer_area);
|
||||
document.getElementById('spice-area').addEventListener('dragover', handle_file_dragover, false);
|
||||
document.getElementById('spice-area').addEventListener('drop', handle_file_drop, false);
|
||||
document.getElementById('spice-area').addEventListener('dragover', SpiceHtml5.handle_file_dragover, false);
|
||||
document.getElementById('spice-area').addEventListener('drop', SpiceHtml5.handle_file_drop, false);
|
||||
}
|
||||
else {
|
||||
console.log("File API is not supported");
|
||||
|
@ -224,7 +195,7 @@
|
|||
});
|
||||
*/
|
||||
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
|
||||
document.getElementById('ctrlaltdel').addEventListener('click', sendCtrlAltDel);
|
||||
document.getElementById('ctrlaltdel').addEventListener('click', function(){sendCtrlAltDel(sc);});
|
||||
document.getElementById('ctrlaltf1').addEventListener('click', function(){sendCtrlAltFN(0);});
|
||||
document.getElementById('ctrlaltf2').addEventListener('click', function(){sendCtrlAltFN(1);});
|
||||
document.getElementById('ctrlaltf3').addEventListener('click', function(){sendCtrlAltFN(2);});
|
||||
|
@ -237,6 +208,22 @@
|
|||
document.getElementById('ctrlaltf10').addEventListener('click', function(){sendCtrlAltFN(9);});
|
||||
document.getElementById('ctrlaltf11').addEventListener('click', function(){sendCtrlAltFN(10);});
|
||||
document.getElementById('ctrlaltf12').addEventListener('click', function(){sendCtrlAltFN(11);});
|
||||
connect();
|
||||
connect(undefined);
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="login">
|
||||
<span class="logo">SPICE</span>
|
||||
</div>
|
||||
|
||||
<div id="spice-area">
|
||||
<div id="spice-screen" class="spice-screen"></div>
|
||||
</div>
|
||||
|
||||
<div id="message-div" class="spice-message"></div>
|
||||
|
||||
<div id="debug-div">
|
||||
<!-- If DUMPXXX is turned on, dumped images will go here -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import os
|
|||
import socket
|
||||
import signal
|
||||
import logging
|
||||
from functools import reduce
|
||||
|
||||
|
||||
class Tunnel(object):
|
||||
|
@ -103,7 +104,7 @@ class Tunnel(object):
|
|||
|
||||
logging.debug("Tunnel PID=%d OUTFD=%d ERRFD=%d",
|
||||
pid, fds[0].fileno(), errorfds[0].fileno())
|
||||
errorfds[0].setblocking(0)
|
||||
errorfds[0].setblocking(False)
|
||||
|
||||
self.outfd = fds[0]
|
||||
self.errfd = errorfds[0]
|
||||
|
|
|
@ -33,7 +33,7 @@ def console(request):
|
|||
console_type = conn.get_console_type()
|
||||
console_websocket_port = conn.get_console_websocket_port()
|
||||
console_passwd = conn.get_console_passwd()
|
||||
except libvirtError as lib_err:
|
||||
except libvirtError:
|
||||
console_type = None
|
||||
console_websocket_port = None
|
||||
console_passwd = None
|
||||
|
|
|
@ -62,4 +62,3 @@ class NewVMForm(forms.Form):
|
|||
elif len(name) > 64:
|
||||
raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters'))
|
||||
return name
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
|
@ -13,14 +14,11 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Flavor',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('label', models.CharField(max_length=12)),
|
||||
('memory', models.IntegerField()),
|
||||
('vcpu', models.IntegerField()),
|
||||
('disk', models.IntegerField()),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
@ -28,4 +27,4 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.RunPython(add_favors),
|
||||
]
|
||||
]
|
|
@ -1,11 +1,11 @@
|
|||
from django.db import models
|
||||
from django.db.models import Model, CharField, IntegerField
|
||||
|
||||
|
||||
class Flavor(models.Model):
|
||||
label = models.CharField(max_length=12)
|
||||
memory = models.IntegerField()
|
||||
vcpu = models.IntegerField()
|
||||
disk = models.IntegerField()
|
||||
class Flavor(Model):
|
||||
label = CharField(max_length=12)
|
||||
memory = IntegerField()
|
||||
vcpu = IntegerField()
|
||||
disk = IntegerField()
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
|
|
@ -94,7 +94,7 @@
|
|||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="clearfix"/>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
<div class="col-sm-6">
|
||||
<select class="form-control" id="select_firmware" name="firmware">
|
||||
{% for frm in firmwares %}
|
||||
<option value="{{ frm }}" {% if frm == default_firmware %}selected{% endif %}>{{ frm }}</option>
|
||||
<option value="{{ frm }}" {% if frm in default_firmware %}selected{% endif %}>{{ frm }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -792,7 +792,7 @@
|
|||
$.each(input_value.split(','), function (index, value) {
|
||||
let li = '<li>eth' + counter +
|
||||
' -> ' + value + ' ' +
|
||||
'<a class="btn-link pull-right" onclick="javascript:$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
|
||||
'<a class="btn-link pull-right" onclick="$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
|
||||
selected_list_html += li;
|
||||
counter++;
|
||||
});
|
||||
|
|
|
@ -19,9 +19,13 @@ from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
|
|||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_IO
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DETECT_ZEROES
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DISCARD
|
||||
from webvirtcloud.settings import INSTANCE_ARCH_DEFAULT_TYPE
|
||||
from webvirtcloud.settings import INSTANCE_FIRMWARE_DEFAULT_TYPE
|
||||
|
||||
from django.contrib import messages
|
||||
from logs.views import addlogmsg
|
||||
|
||||
|
||||
@login_required
|
||||
def create_instance_select_type(request, compute_id):
|
||||
|
||||
|
@ -45,8 +49,9 @@ def create_instance_select_type(request, compute_id):
|
|||
all_hypervisors = conn.get_hypervisors_machines()
|
||||
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
|
||||
supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"]
|
||||
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch ]
|
||||
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch]
|
||||
default_machine = INSTANCE_MACHINE_DEFAULT_TYPE
|
||||
default_arch = INSTANCE_ARCH_DEFAULT_TYPE
|
||||
|
||||
if request.method == 'POST':
|
||||
if 'create_xml' in request.POST:
|
||||
|
@ -63,18 +68,21 @@ def create_instance_select_type(request, compute_id):
|
|||
conn._defineXML(xml)
|
||||
return HttpResponseRedirect(reverse('instance', args=[compute_id, name]))
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
||||
return render(request, 'create_instance_w1.html', locals())
|
||||
|
||||
|
||||
@login_required
|
||||
def create_instance(request, compute_id, arch, machine):
|
||||
"""
|
||||
:param request:
|
||||
:param compute_id:
|
||||
:param arch:
|
||||
:param machine:
|
||||
:return:
|
||||
"""
|
||||
if not request.user.is_superuser:
|
||||
|
@ -96,6 +104,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
compute.password,
|
||||
compute.type)
|
||||
|
||||
default_firmware = INSTANCE_FIRMWARE_DEFAULT_TYPE
|
||||
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
|
||||
instances = conn.get_instances()
|
||||
videos = conn.get_video_models(arch, machine)
|
||||
|
@ -189,7 +198,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
volume_list.append(volume)
|
||||
is_disk_created = True
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
elif data['template']:
|
||||
templ_path = conn.get_volume_path(data['template'])
|
||||
dest_vol = conn.get_volume_path(data["name"] + ".img", data['storage'])
|
||||
|
@ -221,7 +230,7 @@ def create_instance(request, compute_id, arch, machine):
|
|||
volume['bus'] = request.POST.get('bus' + str(idx), '')
|
||||
volume_list.append(volume)
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if data['cache_mode'] not in conn.get_cache_modes():
|
||||
error_msg = _("Invalid cache mode")
|
||||
error_messages.append(error_msg)
|
||||
|
|
|
@ -457,7 +457,7 @@ daemons_running_centos() {
|
|||
systemctl start supervisord.service
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
}
|
||||
#
|
||||
# Ended CentOS Install Functions
|
||||
#
|
||||
|
@ -468,7 +468,7 @@ daemons_running_centos() {
|
|||
# Fedora Install Functions
|
||||
#
|
||||
install_fedora() {
|
||||
yum -y install kvm libvirt bridge-utils python-libguestfs supervisor || return 1
|
||||
yum -y install kvm libvirt bridge-utils python3-libguestfs supervisor || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -519,7 +519,7 @@ daemons_running_fedora() {
|
|||
systemctl start supervisord.service
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
}
|
||||
#
|
||||
# Ended Fedora Install Functions
|
||||
#
|
||||
|
@ -530,7 +530,7 @@ daemons_running_fedora() {
|
|||
# Opensuse Install Functions
|
||||
#
|
||||
install_opensuse() {
|
||||
zypper -n install -l kvm libvirt bridge-utils python-libguestfs supervisor || return 1
|
||||
zypper -n install -l kvm libvirt bridge-utils python3-libguestfs supervisor || return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -596,7 +596,7 @@ install_ubuntu() {
|
|||
if [ $DISTRO_MAJOR_VERSION -lt 18 ]; then
|
||||
apt-get -y install kvm libvirt-bin bridge-utils sasl2-bin python-guestfs supervisor || return 1
|
||||
else
|
||||
apt install -y qemu-kvm libvirt-bin bridge-utils virt-manager sasl2-bin python-guestfs supervisor || return 1
|
||||
apt install -y qemu-kvm libvirt-bin bridge-utils virt-manager sasl2-bin python3-guestfs supervisor || return 1
|
||||
fi
|
||||
|
||||
|
||||
|
@ -651,7 +651,7 @@ daemons_running_ubuntu() {
|
|||
service supervisor start
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
}
|
||||
#
|
||||
# Ended Ubuntu Install Functions
|
||||
#
|
||||
|
@ -737,7 +737,7 @@ daemons_running_debian() {
|
|||
service supervisor start
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
}
|
||||
#
|
||||
# Ended Debian Install Functions
|
||||
#
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
-r ../conf/requirements.txt
|
||||
pep8==1.7.1
|
||||
pycodestyle
|
||||
pyflakes==2.1.1
|
||||
pylint==1.9.4
|
||||
pylint==2.4.4
|
||||
|
|
|
@ -20,7 +20,7 @@ import os
|
|||
# range.
|
||||
#
|
||||
|
||||
#bind = 'unix:/srv/webvirtcloud/venv/wvcloud.socket'
|
||||
# bind = 'unix:/srv/webvirtcloud/venv/wvcloud.socket'
|
||||
bind = '127.0.0.1:8000'
|
||||
backlog = 2048
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('computes', '0001_initial'),
|
||||
]
|
||||
|
@ -14,13 +16,12 @@ class Migration(migrations.Migration):
|
|||
migrations.CreateModel(
|
||||
name='Instance',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=20)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=120)),
|
||||
('uuid', models.CharField(max_length=36)),
|
||||
('compute', models.ForeignKey(to='computes.Compute')),
|
||||
('is_template', models.BooleanField(default=False)),
|
||||
('created', models.DateField(auto_now_add=True)),
|
||||
('compute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='computes.Compute')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
class Instance(models.Model):
|
||||
compute = models.ForeignKey(Compute, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=120)
|
||||
uuid = models.CharField(max_length=36)
|
||||
is_template = models.BooleanField(default=False)
|
||||
created = models.DateField(auto_now_add=True)
|
||||
class Instance(Model):
|
||||
compute = ForeignKey(Compute, on_delete=CASCADE)
|
||||
name = CharField(max_length=120)
|
||||
uuid = CharField(max_length=36)
|
||||
is_template = BooleanField(default=False)
|
||||
created = DateField(auto_now_add=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans 'Bus' %}</label>
|
||||
<div class="col-sm-8">
|
||||
<select class="form-control" name="vol_bus">
|
||||
<select class="form-control" name="vol_bus" {% if status != 5 %} disabled {% endif %}>
|
||||
{% for bus in bus_host %}
|
||||
<option value="{{ bus }}" {% if bus == disk.bus %}selected{% endif %}>{{ bus }}</option>
|
||||
{% endfor %}
|
||||
|
@ -110,6 +110,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<input class="form-control" name="vol_bus_old" value="{{ disk.bus }}"/>
|
||||
</div><!-- /.tabpane-content -->
|
||||
</div><!-- /.tab-content -->
|
||||
</div> <!-- /.modal-body -->
|
||||
|
|
|
@ -4,35 +4,37 @@
|
|||
{% block title %}{% trans "Instance" %} - {{ vname }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include 'pleasewaitdialog.html' %}
|
||||
<nav class="navbar navbar-default navba navbar-fixed-bottom">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar_bottom" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">{% trans 'HOST' %}</a>
|
||||
{% if bottom_bar %}
|
||||
<nav class="navbar navbar-default navbar navbar-fixed-bottom">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar_bottom" aria-expanded="false" aria-controls="navbar">
|
||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">{% trans 'HOST' %}</a>
|
||||
</div>
|
||||
<div id="navbar_bottom" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a class="navbar-link" href="{% url 'overview' compute.id %}">
|
||||
<i class="fa fa-server"></i>
|
||||
{{ compute.name }} {% if compute.name != compute.hostname %} - {{ compute.hostname }}{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'instances' compute.id %}"><i class="fa fa-desktop"></i> {% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'instance' compute.id vname %}"><i class="fa fa-hdd-o"></i> {{ vname }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div id="navbar_bottom" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a class="navbar-link" href="{% url 'overview' compute.id %}">
|
||||
<i class="fa fa-server"></i>
|
||||
{{ compute.name }} {% if compute.name != compute.hostname %} - {{ compute.hostname }}{% endif %}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'instances' compute.id %}"><i class="fa fa-desktop"></i> {% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="{% url 'instance' compute.id vname %}"><i class="fa fa-hdd-o"></i> {{ vname }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</nav>
|
||||
{% endif %}
|
||||
<!-- Page Heading -->
|
||||
<div>
|
||||
<div>
|
||||
|
@ -510,7 +512,7 @@
|
|||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="resizevm_disk">
|
||||
{% if request.user.is_superuser or request.user.is_staff or userinstance.is_change %}
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<p style="font-weight:bold;">{% trans "Disk allocation (B):" %}</p>
|
||||
<p style="font-weight:bold;">{% trans "Disk allocation (GB):" %}</p>
|
||||
{% for disk in disks %}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label" style="font-weight:normal;">{% trans "Current allocation" %} ({{ disk.dev }})</label>
|
||||
|
@ -704,25 +706,25 @@
|
|||
</div>
|
||||
</form>
|
||||
<p style="font-weight:bold;">{% trans 'Boot Order' %}</p>
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 col-sm-offset-2">
|
||||
{% ifequal status 5 %}
|
||||
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
|
||||
{% ifequal bootmenu 0 %}
|
||||
<input type="submit" class="btn btn-success" name="set_bootmenu" title="Show boot menu" value="{% trans "Enable" %}">
|
||||
{% else %}
|
||||
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="Hide boot menu" value="{% trans "Disable" %}">
|
||||
{% endifequal %}
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12 col-sm-offset-2">
|
||||
{% ifequal status 5 %}
|
||||
<p>{% trans "Enable Boot Menu for your instance when it starts up " %}
|
||||
{% ifequal bootmenu 0 %}
|
||||
<input type="submit" class="btn btn-success" name="set_bootmenu" title="Show boot menu" value="{% trans "Enable" %}">
|
||||
{% else %}
|
||||
{% ifequal bootmenu 0 %}
|
||||
<p>{% trans "**** Please shutdown instance to modify boot menu ****" %}</p>
|
||||
{% endifequal %}
|
||||
<input type="submit" class="btn btn-danger" name="unset_bootmenu" title="Hide boot menu" value="{% trans "Disable" %}">
|
||||
{% endifequal %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% ifequal bootmenu 0 %}
|
||||
<p>{% trans "**** Please shutdown instance to modify boot menu ****" %}</p>
|
||||
{% endifequal %}
|
||||
{% endifequal %}
|
||||
</div>
|
||||
</form>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% ifequal bootmenu 1 %}
|
||||
<div class="col-sm-6 col-sm-offset-2">
|
||||
<div class="well">
|
||||
|
@ -841,12 +843,14 @@
|
|||
<div class="col-xs-12 col-sm-12">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<th>{% trans "Device" %}</th>
|
||||
<th>{% trans "Used" %}</th>
|
||||
<th>{% trans "Capacity" %}</th>
|
||||
<th>{% trans "Storage" %}</th>
|
||||
<th>{% trans "Source" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
<tr>
|
||||
<th>{% trans "Device" %}</th>
|
||||
<th>{% trans "Used" %}</th>
|
||||
<th>{% trans "Capacity" %}</th>
|
||||
<th>{% trans "Storage" %}</th>
|
||||
<th>{% trans "Source" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for disk in disks %}
|
||||
|
@ -1169,7 +1173,7 @@
|
|||
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Migrate" %}</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
<div class="clearfix"></div></p>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="xmledit">
|
||||
<p>{% trans "If you need to edit xml please Power Off the instance" %}</p>
|
||||
|
@ -1354,7 +1358,7 @@
|
|||
</div>
|
||||
{% else %}
|
||||
<div class="col-sm-4">
|
||||
<select id="select_clone_name" class="form-control" name="name" size="1"/>
|
||||
<select id="select_clone_name" class="form-control" name="name" size="1">
|
||||
{% for name in clone_free_names %}
|
||||
<option value="{{ name }}">{{ name }}</option>
|
||||
{% endfor %}
|
||||
|
@ -1730,7 +1734,7 @@
|
|||
$.getJSON('{% url 'random_mac_address' %}', function (data) {
|
||||
$('input[name="' + net + '"]').val(data['mac']);
|
||||
});
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function show_console() {
|
||||
|
@ -1765,13 +1769,13 @@
|
|||
vname = '{{ vname }}';
|
||||
{% for disk in disks %}
|
||||
disk_name = '{{ disk.image }}';
|
||||
disk_dot = disk_name.split('.')
|
||||
disk_dot = disk_name.split('.');
|
||||
disk_dot_suffix = disk_dot[disk_dot.length-1];
|
||||
if (disk_name.startsWith(vname)) {
|
||||
image = disk_name.replace(vname, new_vname);
|
||||
} else if (disk_name.lastIndexOf('.') > -1 && disk_dot_suffix.length <= 7) {
|
||||
disk_dot.pop();
|
||||
disk_name_only = disk_dot.join('-')
|
||||
disk_name_only = disk_dot.join('-');
|
||||
image = new_vname + "-" + disk_name_only + "." + disk_dot_suffix
|
||||
} else if (new_vname != disk_name) {
|
||||
image = new_vname
|
||||
|
@ -1779,7 +1783,7 @@
|
|||
image = new_vname + '-clone';
|
||||
}
|
||||
$('#disk_name-{{ disk.dev }}').val(image);
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
|
@ -1811,28 +1815,28 @@
|
|||
});
|
||||
$(document).ready(function () {
|
||||
// set current console keymap or fall back to default
|
||||
var keymap = "{{ console_keymap }}"
|
||||
var keymap = "{{ console_keymap }}";
|
||||
if (keymap != '') {
|
||||
$("#console_select_keymap option[value='" + keymap + "']").prop('selected', true);
|
||||
}
|
||||
});
|
||||
$(document).ready(function () {
|
||||
// set current console type or fall back to default
|
||||
var console_type = "{{ console_type }}"
|
||||
var console_type = "{{ console_type }}";
|
||||
if (console_type != '') {
|
||||
$("#console_select_type option[value='" + console_type + "']").prop('selected', true);
|
||||
}
|
||||
});
|
||||
$(document).ready(function () {
|
||||
// set current console listen address or fall back to default
|
||||
var console_listen_address = "{{ console_listen_address }}"
|
||||
var console_listen_address = "{{ console_listen_address }}";
|
||||
if (console_listen_address != '') {
|
||||
$("#console_select_listen_address option[value='" + console_listen_address + "']").prop('selected', true);
|
||||
}
|
||||
});
|
||||
$(document).ready(function () {
|
||||
// get video model or fall back to default
|
||||
let video_model = "{{ video_model }}"
|
||||
let video_model = "{{ video_model }}";
|
||||
if (video_model != '') {
|
||||
$("#video_model_select option[value='" + video_model + "']").prop('selected', true);
|
||||
}
|
||||
|
@ -2103,7 +2107,7 @@
|
|||
var netChart = {};
|
||||
{% for net in networks %}
|
||||
var net_ctx_{{ forloop.counter0 }} = $("#netEth{{ forloop.counter0 }}Chart").get(0).getContext("2d");
|
||||
netChart['{{ forloop.counter0 }}'] = new Chart(net_ctx_{{ forloop.counter0 }}, {
|
||||
netChart[{{ forloop.counter0 }}] = new Chart(net_ctx_{{ forloop.counter0 }}, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets : [
|
||||
|
@ -2182,8 +2186,8 @@
|
|||
for (let j = 0; j < data.blkdata.length; j++) {
|
||||
diskChart[data.blkdata[j].dev].data.labels.push(data.timeline);
|
||||
|
||||
diskChart[data.blkdata[j].dev].data.datasets[0].data.push(data.blkdata[0].data[0]);
|
||||
diskChart[data.blkdata[j].dev].data.datasets[1].data.push(data.blkdata[0].data[1]);
|
||||
diskChart[data.blkdata[j].dev].data.datasets[0].data.push(data.blkdata[j].data[0]);
|
||||
diskChart[data.blkdata[j].dev].data.datasets[1].data.push(data.blkdata[j].data[1]);
|
||||
if (diskChart[data.blkdata[j].dev].data.datasets[0].data.length > 10){
|
||||
diskChart[data.blkdata[j].dev].data.labels.shift();
|
||||
diskChart[data.blkdata[j].dev].data.datasets[0].data.shift();
|
||||
|
@ -2195,8 +2199,8 @@
|
|||
for (let j = 0; j < data.netdata.length; j++) {
|
||||
netChart[data.netdata[j].dev].data.labels.push(data.timeline);
|
||||
|
||||
netChart[data.netdata[j].dev].data.datasets[0].data.push(data.netdata[0].data[0]);
|
||||
netChart[data.netdata[j].dev].data.datasets[1].data.push(data.netdata[0].data[1]);
|
||||
netChart[data.netdata[j].dev].data.datasets[0].data.push(data.netdata[j].data[0]);
|
||||
netChart[data.netdata[j].dev].data.datasets[1].data.push(data.netdata[j].data[1]);
|
||||
if (netChart[data.netdata[j].dev].data.datasets[0].data.length > 10){
|
||||
netChart[data.netdata[j].dev].data.labels.shift();
|
||||
netChart[data.netdata[j].dev].data.datasets[0].data.shift();
|
||||
|
@ -2211,7 +2215,7 @@
|
|||
<script>
|
||||
backgroundJobRunning = false;
|
||||
window.setInterval(function get_status() {
|
||||
var status = {{ status }};
|
||||
var status = {{ status|lower }};
|
||||
$.getJSON('{% url 'inst_status' compute_id vname %}', function (data) {
|
||||
if (data['status'] != status && !backgroundJobRunning) {
|
||||
window.location.reload()
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.allinstances, name='allinstances'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'),
|
||||
url(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'),
|
||||
url(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'),
|
||||
url(r'^guess_mac_address/(?P<vname>[\w\-\.]+)/$', views.guess_mac_address, name='guess_mac_address'),
|
||||
url(r'^guess_clone_name/$', views.guess_clone_name, name='guess_clone_name'),
|
||||
url(r'^random_mac_address/$', views.random_mac_address, name='random_mac_address'),
|
||||
url(r'^check_instance/(?P<vname>[\w\-\.]+)/$', views.check_instance, name='check_instance'),
|
||||
url(r'^sshkeys/(?P<vname>[\w\-\.]+)/$', views.sshkeys, name='sshkeys'),
|
||||
path('', views.allinstances, name='allinstances'),
|
||||
re_path(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'),
|
||||
re_path(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'),
|
||||
re_path(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'),
|
||||
re_path(r'^guess_mac_address/(?P<vname>[\w\-\.]+)/$', views.guess_mac_address, name='guess_mac_address'),
|
||||
re_path(r'^guess_clone_name/$', views.guess_clone_name, name='guess_clone_name'),
|
||||
re_path(r'^random_mac_address/$', views.random_mac_address, name='random_mac_address'),
|
||||
re_path(r'^check_instance/(?P<vname>[\w\-\.]+)/$', views.check_instance, name='check_instance'),
|
||||
re_path(r'^sshkeys/(?P<vname>[\w\-\.]+)/$', views.sshkeys, name='sshkeys'),
|
||||
]
|
||||
|
|
|
@ -63,7 +63,7 @@ def allinstances(request):
|
|||
return instances_actions(request)
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
|
||||
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err)
|
||||
|
||||
view_style = settings.VIEW_INSTANCES_LIST_STYLE
|
||||
|
||||
|
@ -94,7 +94,7 @@ def instances(request, compute_id):
|
|||
return instances_actions(request)
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err.message)
|
||||
addlogmsg(request.user.username, request.POST.get("name", "instance"), lib_err)
|
||||
|
||||
return render(request, 'instances.html', locals())
|
||||
|
||||
|
@ -117,6 +117,7 @@ def instance(request, compute_id, vname):
|
|||
keymaps = settings.QEMU_KEYMAPS
|
||||
console_types = settings.QEMU_CONSOLE_TYPES
|
||||
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
bottom_bar = settings.VIEW_INSTANCE_DETAIL_BOTTOM_BAR
|
||||
try:
|
||||
userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
|
||||
instance__name=vname,
|
||||
|
@ -131,19 +132,19 @@ def instance(request, compute_id, vname):
|
|||
def filesizefstr(size_str):
|
||||
if size_str == '':
|
||||
return 0
|
||||
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
|
||||
size_str = size_str.upper().replace("B", "")
|
||||
if 'K' == size_str[-1]:
|
||||
return long(float(size_str[:-1])) << 10
|
||||
return int(float(size_str[:-1])) << 10
|
||||
elif 'M' == size_str[-1]:
|
||||
return long(float(size_str[:-1])) << 20
|
||||
return int(float(size_str[:-1])) << 20
|
||||
elif 'G' == size_str[-1]:
|
||||
return long(float(size_str[:-1])) << 30
|
||||
return int(float(size_str[:-1])) << 30
|
||||
elif 'T' == size_str[-1]:
|
||||
return long(float(size_str[:-1])) << 40
|
||||
return int(float(size_str[:-1])) << 40
|
||||
elif 'P' == size_str[-1]:
|
||||
return long(float(size_str[:-1])) << 50
|
||||
return int(float(size_str[:-1])) << 50
|
||||
else:
|
||||
return long(float(size_str))
|
||||
return int(float(size_str))
|
||||
|
||||
def get_clone_free_names(size=10):
|
||||
prefix = settings.CLONE_INSTANCE_DEFAULT_PREFIX
|
||||
|
@ -169,7 +170,7 @@ def instance(request, compute_id, vname):
|
|||
for usr_inst in user_instances:
|
||||
if connection_manager.host_is_up(usr_inst.instance.compute.type,
|
||||
usr_inst.instance.compute.hostname):
|
||||
conn = wvmInstance(usr_inst.instance.compute,
|
||||
conn = wvmInstance(usr_inst.instance.compute.hostname,
|
||||
usr_inst.instance.compute.login,
|
||||
usr_inst.instance.compute.password,
|
||||
usr_inst.instance.compute.type,
|
||||
|
@ -217,8 +218,8 @@ def instance(request, compute_id, vname):
|
|||
if media:
|
||||
existing_media_devs = [m['dev'] for m in media]
|
||||
|
||||
for l in string.lowercase:
|
||||
dev = dev_base + l
|
||||
for al in string.ascii_lowercase:
|
||||
dev = dev_base + al
|
||||
if dev not in existing_disk_devs and dev not in existing_media_devs:
|
||||
return dev
|
||||
raise Exception(_('None available device name'))
|
||||
|
@ -576,7 +577,7 @@ def instance(request, compute_id, vname):
|
|||
target_dev = get_new_disk_dev(media, disks, bus)
|
||||
|
||||
source = conn_create.create_volume(storage, name, size, format, meta_prealloc, default_owner)
|
||||
conn.attach_disk(source, target_dev, target_bus=bus, driver_type=format, cache_mode=cache)
|
||||
conn.attach_disk(target_dev, source, target_bus=bus, driver_type=format, cache_mode=cache)
|
||||
msg = _('Attach new disk {} ({})'.format(name, format))
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#disks')
|
||||
|
@ -598,7 +599,7 @@ def instance(request, compute_id, vname):
|
|||
target_dev = get_new_disk_dev(media, disks, bus)
|
||||
source = path + "/" + name
|
||||
|
||||
conn.attach_disk(source, target_dev, target_bus=bus, driver_type=driver_type, cache_mode=cache)
|
||||
conn.attach_disk(target_dev, source, target_bus=bus, driver_type=driver_type, cache_mode=cache)
|
||||
msg = _('Attach Existing disk: ' + target_dev)
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#disks')
|
||||
|
@ -609,16 +610,26 @@ def instance(request, compute_id, vname):
|
|||
new_path = request.POST.get('vol_path', '')
|
||||
shareable = bool(request.POST.get('vol_shareable', False))
|
||||
readonly = bool(request.POST.get('vol_readonly', False))
|
||||
bus = request.POST.get('vol_bus', '')
|
||||
disk_type = request.POST.get('vol_type', '')
|
||||
new_bus = request.POST.get('vol_bus', '')
|
||||
bus = request.POST.get('vol_bus_old', '')
|
||||
serial = request.POST.get('vol_serial', '')
|
||||
format = request.POST.get('vol_format', '')
|
||||
cache = request.POST.get('vol_cache', default_cache)
|
||||
io = request.POST.get('vol_io_mode', default_io)
|
||||
discard = request.POST.get('vol_discard_mode', default_discard)
|
||||
zeroes = request.POST.get('vol_detect_zeroes', default_zeroes)
|
||||
new_target_dev = get_new_disk_dev(media, disks, new_bus)
|
||||
|
||||
conn.edit_disk(target_dev, new_path, readonly, shareable, bus, serial, format,
|
||||
cache, io, discard, zeroes)
|
||||
if new_bus != bus:
|
||||
conn.detach_disk(target_dev)
|
||||
conn.attach_disk(new_target_dev, new_path, target_bus=new_bus,
|
||||
driver_type=format, cache_mode=cache,
|
||||
readonly=readonly, shareable=shareable, serial=serial,
|
||||
io_mode=io, discard_mode=discard, detect_zeroes_mode=zeroes)
|
||||
else:
|
||||
conn.edit_disk(target_dev, new_path, readonly, shareable, new_bus, serial, format,
|
||||
cache, io, discard, zeroes)
|
||||
|
||||
if not conn.get_status() == 5:
|
||||
messages.success(request, _("Disk changes changes are applied. " +
|
||||
|
@ -646,7 +657,7 @@ def instance(request, compute_id, vname):
|
|||
try:
|
||||
conn_delete.del_volume(name)
|
||||
except libvirtError as err:
|
||||
msg = _('The disk: ' + dev + ' is detached but not deleted. ' + err.message)
|
||||
msg = _('The disk: ' + dev + ' is detached but not deleted. ' + err)
|
||||
messages.warning(request, msg)
|
||||
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
|
@ -663,7 +674,7 @@ def instance(request, compute_id, vname):
|
|||
if 'add_cdrom' in request.POST and allow_admin_or_not_template:
|
||||
bus = request.POST.get('bus', 'ide' if machine == 'pc' else 'sata')
|
||||
target = get_new_disk_dev(media, disks, bus)
|
||||
conn.attach_disk("", target, disk_device='cdrom', cache_mode='none', target_bus=bus, readonly=True)
|
||||
conn.attach_disk(target, "", disk_device='cdrom', cache_mode='none', target_bus=bus, readonly=True)
|
||||
msg = _('Add CD-ROM: ' + target)
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#disks')
|
||||
|
@ -746,7 +757,7 @@ def instance(request, compute_id, vname):
|
|||
try:
|
||||
conn.set_vcpu_hotplug(eval(status))
|
||||
except libvirtError as lib_err:
|
||||
messages.error(request, lib_err.message)
|
||||
messages.error(request, lib_err)
|
||||
messages.success(request, msg)
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#resize')
|
||||
|
@ -951,7 +962,7 @@ def instance(request, compute_id, vname):
|
|||
"Stop and start network to activate new config")
|
||||
|
||||
except libvirtError as le:
|
||||
messages.error(request, le.message)
|
||||
messages.error(request, le)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#network')
|
||||
if 'unset_qos' in request.POST:
|
||||
qos_dir = request.POST.get('qos_direction', '')
|
||||
|
@ -1028,13 +1039,13 @@ def instance(request, compute_id, vname):
|
|||
error_messages.append(msg)
|
||||
else:
|
||||
new_instance = Instance(compute_id=compute_id, name=clone_data['name'])
|
||||
#new_instance.save()
|
||||
# new_instance.save()
|
||||
try:
|
||||
new_uuid = conn.clone_instance(clone_data)
|
||||
new_instance.uuid = new_uuid
|
||||
new_instance.save()
|
||||
except Exception as e:
|
||||
#new_instance.delete()
|
||||
# new_instance.delete()
|
||||
raise e
|
||||
|
||||
user_instance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True)
|
||||
|
@ -1064,8 +1075,8 @@ def instance(request, compute_id, vname):
|
|||
conn.close()
|
||||
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
addlogmsg(request.user.username, vname, lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
addlogmsg(request.user.username, vname, lib_err)
|
||||
|
||||
return render(request, 'instance.html', locals())
|
||||
|
||||
|
@ -1152,7 +1163,7 @@ def get_host_instances(request, comp):
|
|||
status = connection_manager.host_is_up(comp.type, comp.hostname)
|
||||
|
||||
if status is True:
|
||||
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
|
||||
conn = wvmHostDetails(comp.hostname, comp.login, comp.password, comp.type)
|
||||
comp_node_info = conn.get_node_info()
|
||||
comp_mem = conn.get_memory_usage()
|
||||
comp_instances = conn.get_host_instances(True)
|
||||
|
@ -1183,7 +1194,7 @@ def get_user_instances(request):
|
|||
for usr_inst in user_instances:
|
||||
if connection_manager.host_is_up(usr_inst.instance.compute.type,
|
||||
usr_inst.instance.compute.hostname):
|
||||
conn = wvmHostDetails(usr_inst.instance.compute,
|
||||
conn = wvmHostDetails(usr_inst.instance.compute.hostname,
|
||||
usr_inst.instance.compute.login,
|
||||
usr_inst.instance.compute.password,
|
||||
usr_inst.instance.compute.type)
|
||||
|
@ -1419,7 +1430,7 @@ def sshkeys(request, vname):
|
|||
|
||||
instance_keys = []
|
||||
userinstances = UserInstance.objects.filter(instance__name=vname)
|
||||
|
||||
|
||||
for ui in userinstances:
|
||||
keys = UserSSHKey.objects.filter(user=ui.user)
|
||||
for k in keys:
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
# Generated by Django 2.2.10 on 2020-01-28 07:01
|
||||
|
||||
from django.db import models, migrations
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('instances', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Logs',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('user', models.CharField(max_length=50)),
|
||||
('instance', models.CharField(max_length=50)),
|
||||
('message', models.CharField(max_length=255)),
|
||||
('date', models.DateTimeField(auto_now=True)),
|
||||
('instance', models.ForeignKey(to='instances.Instance')),
|
||||
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
bases=(models.Model,),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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):
|
||||
user = models.CharField(max_length=50)
|
||||
instance = models.CharField(max_length=50)
|
||||
message = models.CharField(max_length=255)
|
||||
date = models.DateTimeField(auto_now=True)
|
||||
class Logs(Model):
|
||||
user = CharField(max_length=50)
|
||||
instance = CharField(max_length=50)
|
||||
message = CharField(max_length=255)
|
||||
date = DateTimeField(auto_now=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.instance
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<center>
|
||||
<div class="center-block">
|
||||
{% if page > 1 %}
|
||||
<a href="{% url 'showlogspage' page|add:"-1" %}">←</a>
|
||||
{% else %}
|
||||
|
@ -9,4 +9,4 @@
|
|||
{% else %}
|
||||
|
||||
{% endif %}
|
||||
</center>
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.showlogs, name='showlogs'),
|
||||
url(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
|
||||
url(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
|
||||
path('', views.showlogs, name='showlogs'),
|
||||
re_path(r'^(?P<page>[0-9]+)/$', views.showlogs, name='showlogspage'),
|
||||
re_path(r'^vm_logs/(?P<vname>[\w\-\.]+)/$', views.vm_logs, name='vm_logs'),
|
||||
]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class AddNetPool(forms.Form):
|
|||
subnet = forms.CharField(error_messages={'required': _('No IPv4 subnet has been entered')},
|
||||
max_length=20, required=False)
|
||||
subnet6 = forms.CharField(error_messages={'required': _('No IPv6 subnet has been entered')},
|
||||
max_length=42, required=False)
|
||||
max_length=42, required=False)
|
||||
forward = forms.CharField(max_length=100)
|
||||
dhcp4 = forms.BooleanField(required=False)
|
||||
dhcp6 = forms.BooleanField(required=False)
|
||||
|
|
|
@ -191,6 +191,7 @@
|
|||
<tbody style="text-align: center">
|
||||
{% for fix4 in ipv4_fixed_address %}
|
||||
<tr>
|
||||
<td>
|
||||
<form method="post" role="form">{% csrf_token %}
|
||||
<td><input class="form-control" value="{{ fix4.mac }}" name="mac" readonly/></td>
|
||||
<td><input class="form-control" value="{{ fix4.ip }}" name="address" /></td>
|
||||
|
@ -209,6 +210,7 @@
|
|||
</button>
|
||||
</td>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -126,31 +126,31 @@ def network(request, compute_id, pool):
|
|||
conn.start()
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'stop' in request.POST:
|
||||
try:
|
||||
conn.stop()
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'delete' in request.POST:
|
||||
try:
|
||||
conn.delete()
|
||||
return HttpResponseRedirect(reverse('networks', args=[compute_id]))
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'set_autostart' in request.POST:
|
||||
try:
|
||||
conn.set_autostart(1)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'unset_autostart' in request.POST:
|
||||
try:
|
||||
conn.set_autostart(0)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'modify_fixed_address' in request.POST:
|
||||
name = request.POST.get('name', '')
|
||||
address = request.POST.get('address', '')
|
||||
|
@ -166,9 +166,9 @@ def network(request, compute_id, pool):
|
|||
messages.success(request, _("{} Fixed Address Operation Completed.").format(family.upper()))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
except ValueError as val_err:
|
||||
error_messages.append(val_err.message)
|
||||
error_messages.append(val_err)
|
||||
if 'delete_fixed_address' in request.POST:
|
||||
ip = request.POST.get('address', '')
|
||||
family = request.POST.get('family', 'ipv4')
|
||||
|
@ -184,7 +184,7 @@ def network(request, compute_id, pool):
|
|||
messages.success(request, _("{} DHCP Range is Changed.").format(family.upper()))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'edit_network' in request.POST:
|
||||
edit_xml = request.POST.get('edit_xml', '')
|
||||
if edit_xml:
|
||||
|
@ -208,8 +208,8 @@ def network(request, compute_id, pool):
|
|||
_("Stop and start network to activate new config"))
|
||||
else:
|
||||
messages.success(request, _("{} Qos is set").format(qos_dir.capitalize()))
|
||||
except libvirtError as le:
|
||||
messages.error(request, le.message)
|
||||
except libvirtError as lib_err:
|
||||
messages.error(request, lib_err)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
if 'unset_qos' in request.POST:
|
||||
qos_dir = request.POST.get('qos_direction', '')
|
||||
|
|
|
@ -95,9 +95,11 @@
|
|||
<div class="col-xs-12 col-sm-12">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<th style="width: 45px;">#</th>
|
||||
<th>{% trans "Reference" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
<tr>
|
||||
<th style="width: 45px;">#</th>
|
||||
<th>{% trans "Reference" %}</th>
|
||||
<th>{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ref in refs %}
|
||||
|
@ -125,6 +127,7 @@
|
|||
<div class="col-xs-12 col-sm-12">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:45px;">{% trans "Rule" %}</th>
|
||||
<th>{% trans "ActionType" %}</th>
|
||||
<th>{% trans "Direction" %}</th>
|
||||
|
@ -132,6 +135,7 @@
|
|||
<th>{% trans "Statematch" %}</th>
|
||||
<th>{% trans "Directives" %}</th>
|
||||
<th style="width:100px;">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for rule in rules %}
|
||||
|
|
|
@ -59,8 +59,8 @@ def nwfilters(request, compute_id):
|
|||
conn.create_nwfilter(xml)
|
||||
addlogmsg(request.user.username, compute.hostname, msg)
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
addlogmsg(request.user.username, compute.hostname, lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
addlogmsg(request.user.username, compute.hostname, lib_err)
|
||||
|
||||
if 'del_nwfilter' in request.POST:
|
||||
name = request.POST.get('nwfiltername', '')
|
||||
|
|
|
@ -22,13 +22,13 @@ def secrets(request, compute_id):
|
|||
secrets_all = []
|
||||
error_messages = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
|
||||
try:
|
||||
conn = wvmSecrets(compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
secrets = conn.get_secrets()
|
||||
|
||||
for uuid in secrets:
|
||||
secrt = conn.get_secret(uuid)
|
||||
try:
|
||||
|
|
4
static/js/bootstrap-multiselect.js
vendored
4
static/js/bootstrap-multiselect.js
vendored
|
@ -813,7 +813,7 @@
|
|||
}
|
||||
|
||||
$options.push(this.getOptionByValue(value));
|
||||
}, this))
|
||||
}, this));
|
||||
|
||||
// Cannot use select or deselect here because it would call updateOptGroups again.
|
||||
|
||||
|
@ -1608,7 +1608,7 @@
|
|||
* Update opt groups.
|
||||
*/
|
||||
updateOptGroups: function() {
|
||||
var $groups = $('li.multiselect-group', this.$ul)
|
||||
var $groups = $('li.multiselect-group', this.$ul);
|
||||
var selectedClass = this.options.selectedClass;
|
||||
|
||||
$groups.each(function() {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-family: Helvetica;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
/*Background image with light grey curve.*/
|
||||
background-color:#494949;
|
||||
background-repeat:no-repeat;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -35,7 +35,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
var ready = function() {
|
||||
document.removeEventListener('DOMContentLoaded', ready, false );
|
||||
|
@ -63,7 +63,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// simple DOM ready
|
||||
if (document.readyState !== 'loading')
|
||||
|
@ -105,10 +105,10 @@ function xhrFetch(url, resolve, reject) {
|
|||
var xhr = new XMLHttpRequest();
|
||||
var load = function(source) {
|
||||
resolve(xhr.responseText);
|
||||
}
|
||||
};
|
||||
var error = function() {
|
||||
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
|
@ -235,7 +235,7 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
|
|||
return new Promise(function(resolve, reject) {
|
||||
// anonymous module
|
||||
if (anonSources[key]) {
|
||||
resolve(anonSources[key])
|
||||
resolve(anonSources[key]);
|
||||
anonSources[key] = undefined;
|
||||
}
|
||||
// otherwise we fetch
|
||||
|
|
|
@ -4,7 +4,7 @@ export function shrinkBuf (buf, size) {
|
|||
if (buf.subarray) { return buf.subarray(0, size); }
|
||||
buf.length = size;
|
||||
return buf;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function arraySet (dest, src, src_offs, len, dest_offs) {
|
||||
|
|
|
@ -277,7 +277,7 @@ export default function inflate_fast(strm, start) {
|
|||
}
|
||||
else if ((op & 64) === 0) { /* 2nd level distance code */
|
||||
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue dodist;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
strm.msg = 'invalid distance code';
|
||||
|
@ -290,7 +290,7 @@ export default function inflate_fast(strm, start) {
|
|||
}
|
||||
else if ((op & 64) === 0) { /* 2nd level length code */
|
||||
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue dolen;
|
||||
continue;
|
||||
}
|
||||
else if (op & 32) { /* end-of-block */
|
||||
//Tracevv((stderr, "inflate: end of block\n"));
|
||||
|
@ -320,5 +320,5 @@ export default function inflate_fast(strm, start) {
|
|||
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
|
||||
state.hold = hold;
|
||||
state.bits = bits;
|
||||
return;
|
||||
|
||||
};
|
||||
|
|
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
|
||||
{
|
||||
padding: 5px;
|
||||
background-color: #fAfAfA;
|
||||
border: 1px inset #999999;
|
||||
outline: none;
|
||||
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
|
||||
|
@ -81,7 +80,7 @@ body
|
|||
{
|
||||
min-height: 600px;
|
||||
height: 100%;
|
||||
margin: 62px 10px 10px 10px;
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
background-color: #333333;
|
||||
}
|
||||
|
@ -104,6 +103,8 @@ body
|
|||
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
|
||||
/* We default the message box to hidden. */
|
||||
display: none;
|
||||
}
|
||||
.spice-message p {
|
||||
margin-bottom: 0em;
|
||||
|
|
|
@ -105,7 +105,7 @@ def storage(request, compute_id, pool):
|
|||
size, free = conn.get_size()
|
||||
used = (size - free)
|
||||
if state:
|
||||
percent = (used * 100) / size
|
||||
percent = (used * 100) // size
|
||||
else:
|
||||
percent = 0
|
||||
status = conn.get_status()
|
||||
|
@ -127,31 +127,31 @@ def storage(request, compute_id, pool):
|
|||
conn.start()
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'stop' in request.POST:
|
||||
try:
|
||||
conn.stop()
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'delete' in request.POST:
|
||||
try:
|
||||
conn.delete()
|
||||
return HttpResponseRedirect(reverse('storages', args=[compute_id]))
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'set_autostart' in request.POST:
|
||||
try:
|
||||
conn.set_autostart(1)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'unset_autostart' in request.POST:
|
||||
try:
|
||||
conn.set_autostart(0)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'add_volume' in request.POST:
|
||||
form = AddImage(request.POST)
|
||||
if form.is_valid():
|
||||
|
@ -175,7 +175,7 @@ def storage(request, compute_id, pool):
|
|||
messages.success(request, _('Volume: {} is deleted.'.format(volname)))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
error_messages.append(lib_err)
|
||||
if 'iso_upload' in request.POST:
|
||||
if str(request.FILES['file']) in conn.update_volumes():
|
||||
error_msg = _("ISO image already exist")
|
||||
|
|
|
@ -80,7 +80,7 @@ class wvmConnection(object):
|
|||
elif self.type == CONN_SOCKET:
|
||||
self.__connect_socket()
|
||||
else:
|
||||
raise ValueError('"{type}" is not a valid connection type'.format(type=self.type))
|
||||
raise ValueError(f'"{self.type}" is not a valid connection type')
|
||||
|
||||
if self.connected:
|
||||
# do some preprocessing of the connection:
|
||||
|
@ -135,14 +135,14 @@ class wvmConnection(object):
|
|||
def __connect_tcp(self):
|
||||
flags = [libvirt.VIR_CRED_AUTHNAME, libvirt.VIR_CRED_PASSPHRASE]
|
||||
auth = [flags, self.__libvirt_auth_credentials_callback, None]
|
||||
uri = 'qemu+tcp://%s/system' % self.host
|
||||
uri = f'qemu+tcp://{self.host}/system'
|
||||
|
||||
try:
|
||||
self.connection = libvirt.openAuth(uri, auth, 0)
|
||||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.last_error = f'Connection Failed: {str(e)}'
|
||||
self.connection = None
|
||||
|
||||
def __connect_ssh(self):
|
||||
|
@ -153,7 +153,7 @@ class wvmConnection(object):
|
|||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e) + ' --- ' + repr(libvirt.virGetLastError())
|
||||
self.last_error = f'Connection Failed: {str(e)} --- ' + repr(libvirt.virGetLastError())
|
||||
self.connection = None
|
||||
|
||||
def __connect_tls(self):
|
||||
|
@ -166,7 +166,7 @@ class wvmConnection(object):
|
|||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.last_error = f'Connection Failed: {str(e)}'
|
||||
self.connection = None
|
||||
|
||||
def __connect_socket(self):
|
||||
|
@ -177,7 +177,7 @@ class wvmConnection(object):
|
|||
self.last_error = None
|
||||
|
||||
except libvirtError as e:
|
||||
self.last_error = 'Connection Failed: ' + str(e)
|
||||
self.last_error = f'Connection Failed: {str(e)}'
|
||||
self.connection = None
|
||||
|
||||
def close(self):
|
||||
|
@ -208,18 +208,18 @@ class wvmConnection(object):
|
|||
|
||||
def __unicode__(self):
|
||||
if self.type == CONN_TCP:
|
||||
type_str = u'tcp'
|
||||
type_str = 'tcp'
|
||||
elif self.type == CONN_SSH:
|
||||
type_str = u'ssh'
|
||||
type_str = 'ssh'
|
||||
elif self.type == CONN_TLS:
|
||||
type_str = u'tls'
|
||||
type_str = 'tls'
|
||||
else:
|
||||
type_str = u'invalid_type'
|
||||
type_str = 'invalid_type'
|
||||
|
||||
return u'qemu+{type}://{user}@{host}/system'.format(type=type_str, user=self.login, host=self.host)
|
||||
return f'qemu+{type_str}://{self.login}@{self.host}/system'
|
||||
|
||||
def __repr__(self):
|
||||
return '<wvmConnection {connection_str}>'.format(connection_str=unicode(self))
|
||||
return f'<wvmConnection {str(self)}>'
|
||||
|
||||
|
||||
class wvmConnectionManager(object):
|
||||
|
@ -264,9 +264,9 @@ class wvmConnectionManager(object):
|
|||
raises libvirtError if (re)connecting fails
|
||||
"""
|
||||
# force all string values to unicode
|
||||
host = unicode(host)
|
||||
login = unicode(login)
|
||||
passwd = unicode(passwd) if passwd is not None else None
|
||||
host = str(host)
|
||||
login = str(login)
|
||||
passwd = str(passwd) if passwd is not None else None
|
||||
|
||||
connection = self._search_connection(host, login, passwd, conn)
|
||||
|
||||
|
@ -432,21 +432,21 @@ class wvmConnect(object):
|
|||
|
||||
def get_version(self):
|
||||
ver = self.wvm.getVersion()
|
||||
major = ver / 1000000
|
||||
major = ver // 1000000
|
||||
ver = ver % 1000000
|
||||
minor = ver / 1000
|
||||
minor = ver // 1000
|
||||
ver = ver % 1000
|
||||
release = ver
|
||||
return "%s.%s.%s" % (major, minor, release)
|
||||
return f"{major}.{minor}.{release}"
|
||||
|
||||
def get_lib_version(self):
|
||||
ver = self.wvm.getLibVersion()
|
||||
major = ver / 1000000
|
||||
major = ver // 1000000
|
||||
ver %= 1000000
|
||||
minor = ver / 1000
|
||||
minor = ver // 1000
|
||||
ver %= 1000
|
||||
release = ver
|
||||
return "%s.%s.%s" % (major,minor,release)
|
||||
return f"{major}.{minor}.{release}"
|
||||
|
||||
def is_kvm_supported(self):
|
||||
"""Return KVM capabilities."""
|
||||
|
|
|
@ -6,9 +6,6 @@ from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
|
|||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_rbd_storage_data(stg):
|
||||
xml = stg.XMLDesc(0)
|
||||
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
|
||||
|
@ -236,10 +233,10 @@ class wvmCreate(wvmConnect):
|
|||
"""
|
||||
xml += """<devices>"""
|
||||
|
||||
vd_disk_letters = list(string.lowercase)
|
||||
fd_disk_letters = list(string.lowercase)
|
||||
hd_disk_letters = list(string.lowercase)
|
||||
sd_disk_letters = list(string.lowercase)
|
||||
vd_disk_letters = list(string.ascii_lowercase)
|
||||
fd_disk_letters = list(string.ascii_lowercase)
|
||||
hd_disk_letters = list(string.ascii_lowercase)
|
||||
sd_disk_letters = list(string.ascii_lowercase)
|
||||
add_cd = True
|
||||
|
||||
disk_opts = ''
|
||||
|
|
|
@ -19,10 +19,10 @@ class wvmHostDetails(wvmConnect):
|
|||
all_mem = self.wvm.getInfo()[1] * 1048576
|
||||
freemem = self.wvm.getMemoryStats(-1, 0)
|
||||
if type(freemem) == dict:
|
||||
free = (freemem.values()[0] +
|
||||
freemem.values()[2] +
|
||||
freemem.values()[3]) * 1024
|
||||
percent = (100 - ((free * 100) / all_mem))
|
||||
free = (freemem['buffers'] +
|
||||
freemem['free'] +
|
||||
freemem['cached']) * 1024
|
||||
percent = abs(100 - ((free * 100) // all_mem))
|
||||
usage = (all_mem - free)
|
||||
mem_usage = {'total': all_mem, 'usage': usage, 'percent': percent}
|
||||
else:
|
||||
|
@ -38,7 +38,7 @@ class wvmHostDetails(wvmConnect):
|
|||
cpu = self.wvm.getCPUStats(-1, 0)
|
||||
if type(cpu) == dict:
|
||||
for num in range(2):
|
||||
idle = self.wvm.getCPUStats(-1, 0).values()[1]
|
||||
idle = self.wvm.getCPUStats(-1, 0)['idle']
|
||||
total = sum(self.wvm.getCPUStats(-1, 0).values())
|
||||
diff_idle = idle - prev_idle
|
||||
diff_total = total - prev_total
|
||||
|
|
|
@ -275,7 +275,7 @@ class wvmInstance(wvmConnect):
|
|||
"""Get number of physical CPUs."""
|
||||
hostinfo = self.wvm.getInfo()
|
||||
pcpus = hostinfo[4] * hostinfo[5] * hostinfo[6] * hostinfo[7]
|
||||
range_pcpus = xrange(1, int(pcpus + 1))
|
||||
range_pcpus = range(1, int(pcpus + 1))
|
||||
return range_pcpus
|
||||
|
||||
def get_interface_addresses(self, iface_mac):
|
||||
|
@ -322,11 +322,11 @@ class wvmInstance(wvmConnect):
|
|||
return None, None
|
||||
|
||||
def _get_interface_addresses(self, source):
|
||||
#("Calling interfaceAddresses source=%s", source)
|
||||
# ("Calling interfaceAddresses source=%s", source)
|
||||
try:
|
||||
return self.instance.interfaceAddresses(source)
|
||||
except Exception as e:
|
||||
#log.debug("interfaceAddresses failed: %s", str(e))
|
||||
# log.debug("interfaceAddresses failed: %s", str(e))
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
@ -340,7 +340,7 @@ class wvmInstance(wvmConnect):
|
|||
self._ip_cache["qemuga"] = self._get_interface_addresses(
|
||||
VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
|
||||
|
||||
arp_flag = 3 # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
|
||||
arp_flag = 3 # libvirt."VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP"
|
||||
self._ip_cache["arp"] = self._get_interface_addresses(arp_flag)
|
||||
|
||||
def get_net_devices(self):
|
||||
|
@ -501,7 +501,7 @@ class wvmInstance(wvmConnect):
|
|||
else:
|
||||
raise Exception('Unknown boot menu option, please choose one of 0:disable, 1:enable, -1:remove')
|
||||
|
||||
xmldom = ElementTree.tostring(tree)
|
||||
xmldom = ElementTree.tostring(tree).decode()
|
||||
self._defineXML(xmldom)
|
||||
|
||||
def get_bootorder(self):
|
||||
|
@ -592,7 +592,7 @@ class wvmInstance(wvmConnect):
|
|||
d.append(order)
|
||||
else:
|
||||
raise Exception('Invalid Device Type for boot order')
|
||||
self._defineXML(ElementTree.tostring(tree))
|
||||
self._defineXML(ElementTree.tostring(tree).decode())
|
||||
|
||||
def mount_iso(self, dev, image):
|
||||
def attach_iso(dev, disk, vol):
|
||||
|
@ -618,11 +618,11 @@ class wvmInstance(wvmConnect):
|
|||
if attach_iso(dev, disk, vol):
|
||||
break
|
||||
if self.get_status() == 1:
|
||||
xml = ElementTree.tostring(disk)
|
||||
xml = ElementTree.tostring(disk).decode()
|
||||
self.instance.attachDevice(xml)
|
||||
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
if self.get_status() == 5:
|
||||
xmldom = ElementTree.tostring(tree)
|
||||
xmldom = ElementTree.tostring(tree).decode()
|
||||
self._defineXML(xmldom)
|
||||
|
||||
def umount_iso(self, dev, image):
|
||||
|
@ -637,14 +637,14 @@ class wvmInstance(wvmConnect):
|
|||
if elm.get('dev') == dev:
|
||||
disk.remove(src_media)
|
||||
if self.get_status() == 1:
|
||||
xml_disk = ElementTree.tostring(disk)
|
||||
xml_disk = ElementTree.tostring(disk).decode()
|
||||
self.instance.attachDevice(xml_disk)
|
||||
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
if self.get_status() == 5:
|
||||
xmldom = ElementTree.tostring(tree)
|
||||
xmldom = ElementTree.tostring(tree).decode()
|
||||
self._defineXML(xmldom)
|
||||
|
||||
def attach_disk(self, source, target_dev, target_bus='ide', disk_type='file',
|
||||
def attach_disk(self, target_dev, source, target_bus='ide', disk_type='file',
|
||||
disk_device='disk', driver_name='qemu', driver_type='raw',
|
||||
readonly=False, shareable=False, serial=None,
|
||||
cache_mode=None, io_mode=None, discard_mode=None, detect_zeroes_mode=None):
|
||||
|
@ -683,7 +683,7 @@ class wvmInstance(wvmConnect):
|
|||
tree = etree.fromstring(self._XMLDesc(0))
|
||||
|
||||
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[0].getparent()
|
||||
xml_disk = etree.tostring(disk_el)
|
||||
xml_disk = etree.tostring(disk_el).decode()
|
||||
devices = tree.find('devices')
|
||||
devices.remove(disk_el)
|
||||
|
||||
|
@ -699,6 +699,7 @@ class wvmInstance(wvmConnect):
|
|||
old_disk_type = disk_el.get('type')
|
||||
old_disk_device = disk_el.get('device')
|
||||
old_driver_name = disk_el.xpath('driver/@name')[0]
|
||||
old_target_bus = disk_el.xpath('target/@bus')[0]
|
||||
|
||||
additionals = ''
|
||||
if cache_mode is not None and cache_mode != 'default':
|
||||
|
@ -715,6 +716,7 @@ class wvmInstance(wvmConnect):
|
|||
xml_disk += "<driver name='%s' type='%s'/>" % (old_driver_name, format)
|
||||
elif old_disk_device == 'disk':
|
||||
xml_disk += "<driver name='%s' type='%s' %s/>" % (old_driver_name, format, additionals)
|
||||
|
||||
xml_disk += """<source file='%s'/>
|
||||
<target dev='%s' bus='%s'/>""" % (source, target_dev, target_bus)
|
||||
if readonly:
|
||||
|
@ -735,7 +737,7 @@ class wvmInstance(wvmConnect):
|
|||
time.sleep(1)
|
||||
cpu_use_now = self.instance.info()[4]
|
||||
diff_usage = cpu_use_now - cpu_use_ago
|
||||
cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10 ** 9L)
|
||||
cpu_usage['cpu'] = 100 * diff_usage / (1 * nbcore * 10 ** 9)
|
||||
else:
|
||||
cpu_usage['cpu'] = 0
|
||||
return cpu_usage
|
||||
|
@ -746,7 +748,7 @@ class wvmInstance(wvmConnect):
|
|||
def set_vcpu_hotplug(self, status, vcpus_hotplug=0):
|
||||
""" vcpus_hotplug = 0 make all vpus hotpluggable """
|
||||
vcpus_hotplug = int(self.get_vcpu()) if vcpus_hotplug == 0 else vcpus_hotplug
|
||||
if self.get_status() == 5: # shutoff
|
||||
if self.get_status() == 5: # shutoff
|
||||
if status:
|
||||
xml = """ <vcpus>"""
|
||||
xml += """<vcpu id='0' enabled='yes' hotpluggable='no' order='1'/>"""
|
||||
|
@ -758,14 +760,14 @@ class wvmInstance(wvmConnect):
|
|||
vcpus = tree.xpath("/domain/vcpus")
|
||||
if not vcpus:
|
||||
tree.append(etree.fromstring(xml))
|
||||
self._defineXML(etree.tostring(tree))
|
||||
self._defineXML(etree.tostring(tree).decode())
|
||||
else:
|
||||
tree = etree.fromstring(self._XMLDesc(0))
|
||||
vcpus = tree.xpath("/domain/vcpus")
|
||||
for vcpu in vcpus:
|
||||
parent = vcpu.getparent()
|
||||
parent.remove(vcpu)
|
||||
self._defineXML(etree.tostring(tree))
|
||||
self._defineXML(etree.tostring(tree).decode())
|
||||
else:
|
||||
raise libvirtError("Please shutdown the instance then try to enable vCPU hotplug")
|
||||
|
||||
|
@ -892,7 +894,7 @@ class wvmInstance(wvmConnect):
|
|||
listen.attrib.pop("address")
|
||||
except:
|
||||
pass
|
||||
newxml = ElementTree.tostring(root)
|
||||
newxml = ElementTree.tostring(root).decode()
|
||||
return self._defineXML(newxml)
|
||||
|
||||
def get_console_socket(self):
|
||||
|
@ -917,7 +919,7 @@ class wvmInstance(wvmConnect):
|
|||
# Little fix for old version ElementTree
|
||||
graphic = root.find("devices/graphics")
|
||||
graphic.set('type', console_type)
|
||||
newxml = ElementTree.tostring(root)
|
||||
newxml = ElementTree.tostring(root).decode()
|
||||
self._defineXML(newxml)
|
||||
|
||||
def get_console_port(self, console_type=None):
|
||||
|
@ -953,7 +955,7 @@ class wvmInstance(wvmConnect):
|
|||
graphic.attrib.pop('passwd')
|
||||
except:
|
||||
pass
|
||||
newxml = ElementTree.tostring(root)
|
||||
newxml = ElementTree.tostring(root).decode()
|
||||
return self._defineXML(newxml)
|
||||
|
||||
def set_console_keymap(self, keymap):
|
||||
|
@ -972,7 +974,7 @@ class wvmInstance(wvmConnect):
|
|||
graphic.attrib.pop('keymap')
|
||||
except:
|
||||
pass
|
||||
newxml = ElementTree.tostring(root)
|
||||
newxml = ElementTree.tostring(root).decode()
|
||||
self._defineXML(newxml)
|
||||
|
||||
def get_console_keymap(self):
|
||||
|
@ -998,7 +1000,7 @@ class wvmInstance(wvmConnect):
|
|||
parent = model.getparent()
|
||||
parent.remove(model)
|
||||
parent.append(etree.fromstring(video_xml))
|
||||
self._defineXML(etree.tostring(tree))
|
||||
self._defineXML(etree.tostring(tree).decode())
|
||||
|
||||
def resize_cpu(self, cur_vcpu, vcpu):
|
||||
"""
|
||||
|
@ -1011,11 +1013,11 @@ class wvmInstance(wvmConnect):
|
|||
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
tree = etree.fromstring(xml)
|
||||
|
||||
set_vcpu = tree.find('vcpu')
|
||||
set_vcpu.text = vcpu
|
||||
set_vcpu.set('current', cur_vcpu)
|
||||
vcpu_elem = tree.find('vcpu')
|
||||
vcpu_elem.text = vcpu
|
||||
vcpu_elem.set('current', cur_vcpu)
|
||||
|
||||
new_xml = etree.tostring(tree)
|
||||
new_xml = etree.tostring(tree).decode()
|
||||
self._defineXML(new_xml)
|
||||
|
||||
if is_vcpus_enabled:
|
||||
|
@ -1034,14 +1036,14 @@ class wvmInstance(wvmConnect):
|
|||
return
|
||||
|
||||
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
tree = ElementTree.fromstring(xml)
|
||||
tree = etree.fromstring(xml)
|
||||
|
||||
set_mem = tree.find('memory')
|
||||
set_mem.text = str(memory)
|
||||
set_cur_mem = tree.find('currentMemory')
|
||||
set_cur_mem.text = str(cur_memory)
|
||||
mem_elem = tree.find('memory')
|
||||
mem_elem.text = str(memory)
|
||||
cur_mem_elem = tree.find('currentMemory')
|
||||
cur_mem_elem.text = str(cur_memory)
|
||||
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
new_xml = etree.tostring(tree).decode()
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def resize_disk(self, disks):
|
||||
|
@ -1049,14 +1051,14 @@ class wvmInstance(wvmConnect):
|
|||
Function change disks on vds.
|
||||
"""
|
||||
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
tree = ElementTree.fromstring(xml)
|
||||
tree = etree.fromstring(xml)
|
||||
|
||||
for disk in disks:
|
||||
source_dev = disk['path']
|
||||
vol = self.get_volume_by_path(source_dev)
|
||||
vol.resize(disk['size_new'])
|
||||
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
new_xml = etree.tostring(tree).decode()
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def get_iso_media(self):
|
||||
|
@ -1250,7 +1252,7 @@ class wvmInstance(wvmConnect):
|
|||
'description': clone_data.get('clone-description', ''),
|
||||
}
|
||||
self._set_options(tree, options)
|
||||
self._defineXML(ElementTree.tostring(tree))
|
||||
self._defineXML(ElementTree.tostring(tree).decode())
|
||||
|
||||
return self.get_instance(clone_data['name']).UUIDString()
|
||||
|
||||
|
@ -1297,7 +1299,7 @@ class wvmInstance(wvmConnect):
|
|||
for interface in tree.findall('devices/interface'):
|
||||
source = interface.find('mac')
|
||||
if source.get('address', '') == mac_address:
|
||||
new_xml = ElementTree.tostring(interface)
|
||||
new_xml = ElementTree.tostring(interface).decode()
|
||||
|
||||
if self.get_status() == 1:
|
||||
self.instance.detachDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
|
||||
|
@ -1359,7 +1361,7 @@ class wvmInstance(wvmConnect):
|
|||
else:
|
||||
if source is not None: interface.remove(source)
|
||||
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
new_xml = ElementTree.tostring(tree).decode()
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def set_link_state(self, mac_address, state):
|
||||
|
@ -1373,7 +1375,7 @@ class wvmInstance(wvmConnect):
|
|||
link_el = etree.Element("link")
|
||||
link_el.attrib["state"] = state
|
||||
interface.append(link_el)
|
||||
new_xml = etree.tostring(interface)
|
||||
new_xml = etree.tostring(interface).decode()
|
||||
if self.get_status() == 1:
|
||||
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_LIVE)
|
||||
self.instance.updateDeviceFlags(new_xml, VIR_DOMAIN_AFFECT_CONFIG)
|
||||
|
@ -1400,7 +1402,7 @@ class wvmInstance(wvmConnect):
|
|||
tree = ElementTree.fromstring(xml)
|
||||
|
||||
self._set_options(tree, options)
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
new_xml = ElementTree.tostring(tree).decode()
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def set_memory(self, size, flags=0):
|
||||
|
@ -1460,7 +1462,7 @@ class wvmInstance(wvmConnect):
|
|||
parent.append(etree.fromstring(xml))
|
||||
else:
|
||||
band.append(etree.fromstring(xml))
|
||||
new_xml = etree.tostring(tree)
|
||||
new_xml = etree.tostring(tree).decode()
|
||||
self.wvm.defineXML(new_xml)
|
||||
|
||||
def unset_qos(self, mac, direction):
|
||||
|
@ -1472,7 +1474,7 @@ class wvmInstance(wvmConnect):
|
|||
if parent_mac[0] == mac:
|
||||
band_el.remove(direct)
|
||||
|
||||
self.wvm.defineXML(etree.tostring(tree))
|
||||
self.wvm.defineXML(etree.tostring(tree).decode())
|
||||
|
||||
def add_guest_agent(self):
|
||||
channel_xml = """
|
||||
|
@ -1490,7 +1492,7 @@ class wvmInstance(wvmConnect):
|
|||
tree = etree.fromstring(self._XMLDesc(0))
|
||||
for target in tree.xpath("/domain/devices/channel[@type='unix']/target[@name='org.qemu.guest_agent.0']"):
|
||||
parent = target.getparent()
|
||||
channel_xml = etree.tostring(parent)
|
||||
channel_xml = etree.tostring(parent).decode()
|
||||
if self.get_status() == 1:
|
||||
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_LIVE)
|
||||
self.instance.detachDeviceFlags(channel_xml, VIR_DOMAIN_AFFECT_CONFIG)
|
||||
|
|
|
@ -213,7 +213,7 @@ class wvmNetwork(wvmConnect):
|
|||
for host in hosts:
|
||||
ip = host.get('ip')
|
||||
mac = host.get('mac')
|
||||
name = host.get('name','')
|
||||
name = host.get('name', '')
|
||||
result.append({'ip': ip, 'mac': mac, 'name': name})
|
||||
return result
|
||||
else:
|
||||
|
@ -223,7 +223,7 @@ class wvmNetwork(wvmConnect):
|
|||
for host in hosts:
|
||||
ip = host.get('ip')
|
||||
id = host.get('id')
|
||||
name = host.get('name','')
|
||||
name = host.get('name', '')
|
||||
result.append({'ip': ip, 'id': id, 'name': name})
|
||||
return result
|
||||
|
||||
|
@ -236,7 +236,7 @@ class wvmNetwork(wvmConnect):
|
|||
range = tree.xpath("./ip[@family='ipv6']/dhcp/range")
|
||||
range[0].set('start', range_start)
|
||||
range[0].set('end', range_end)
|
||||
self.wvm.networkDefineXML(etree.tostring(tree))
|
||||
self.wvm.networkDefineXML(etree.tostring(tree).decode())
|
||||
|
||||
def delete_fixed_address(self, ip, family='ipv4'):
|
||||
tree = etree.fromstring(self._XMLDesc(0))
|
||||
|
@ -335,7 +335,7 @@ class wvmNetwork(wvmConnect):
|
|||
parent.append(etree.fromstring(xml))
|
||||
else:
|
||||
band[0].append(etree.fromstring(xml))
|
||||
new_xml = etree.tostring(tree)
|
||||
new_xml = etree.tostring(tree).decode()
|
||||
self.wvm.networkDefineXML(new_xml)
|
||||
|
||||
def unset_qos(self, direction):
|
||||
|
@ -344,7 +344,7 @@ class wvmNetwork(wvmConnect):
|
|||
parent = direct.getparent()
|
||||
parent.remove(direct)
|
||||
|
||||
self.wvm.networkDefineXML(etree.tostring(tree))
|
||||
self.wvm.networkDefineXML(etree.tostring(tree).decode())
|
||||
|
||||
def edit_network(self, new_xml):
|
||||
self.wvm.networkDefineXML(new_xml)
|
||||
|
@ -354,7 +354,7 @@ class wvmNetwork(wvmConnect):
|
|||
self.leases = self.net.DHCPLeases()
|
||||
except Exception as e:
|
||||
self.leases = []
|
||||
raise "Error getting %s DHCP leases: %s" % self, str(e)
|
||||
raise "Error getting %s DHCP leases: %s" % (self, e)
|
||||
|
||||
def get_dhcp_leases(self):
|
||||
if self.leases is None:
|
||||
|
|
|
@ -19,7 +19,7 @@ class wvmNWFilters(wvmConnect):
|
|||
tree.set('name', cln_name)
|
||||
uuid = tree.find('uuid')
|
||||
tree.remove(uuid)
|
||||
self.create_nwfilter(ElementTree.tostring(tree))
|
||||
self.create_nwfilter(ElementTree.tostring(tree).decode())
|
||||
|
||||
|
||||
class wvmNWFilter(wvmConnect):
|
||||
|
@ -43,7 +43,7 @@ class wvmNWFilter(wvmConnect):
|
|||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
uuid = tree.find('uuid')
|
||||
tree.remove(uuid)
|
||||
return ElementTree.tostring(tree)
|
||||
return ElementTree.tostring(tree).decode()
|
||||
|
||||
def get_filter_refs(self):
|
||||
refs = []
|
||||
|
@ -64,7 +64,7 @@ class wvmNWFilter(wvmConnect):
|
|||
|
||||
rule_directives = r.find("./")
|
||||
if rule_directives is not None:
|
||||
rule_directives = ElementTree.tostring(rule_directives)
|
||||
rule_directives = ElementTree.tostring(rule_directives).decode()
|
||||
|
||||
rule_info = {
|
||||
"action": rule_action,
|
||||
|
@ -84,7 +84,7 @@ class wvmNWFilter(wvmConnect):
|
|||
if name == ref.get('filter'):
|
||||
tree.remove(ref)
|
||||
break
|
||||
return ElementTree.tostring(tree)
|
||||
return ElementTree.tostring(tree).decode()
|
||||
|
||||
def delete_rule(self, action, direction, priority):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
|
@ -93,14 +93,14 @@ class wvmNWFilter(wvmConnect):
|
|||
if rule_tree:
|
||||
tree.remove(rule_tree[0])
|
||||
|
||||
return ElementTree.tostring(tree)
|
||||
return ElementTree.tostring(tree).decode()
|
||||
|
||||
def add_ref(self, name):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
element = ElementTree.Element("filterref")
|
||||
element.attrib['filter'] = name
|
||||
tree.append(element)
|
||||
return ElementTree.tostring(tree)
|
||||
return ElementTree.tostring(tree).decode()
|
||||
|
||||
def add_rule(self, xml):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
|
@ -122,4 +122,4 @@ class wvmNWFilter(wvmConnect):
|
|||
element.append(rule_directives)
|
||||
tree.append(element)
|
||||
|
||||
return ElementTree.tostring(tree)
|
||||
return ElementTree.tostring(tree).decode()
|
||||
|
|
|
@ -147,13 +147,13 @@ class wvmStorage(wvmConnect):
|
|||
return util.get_xml_path(self._XMLDesc(0), "/pool/target/path")
|
||||
|
||||
def get_allocation(self):
|
||||
return long(util.get_xml_path(self._XMLDesc(0), "/pool/allocation"))
|
||||
return int(util.get_xml_path(self._XMLDesc(0), "/pool/allocation"))
|
||||
|
||||
def get_available(self):
|
||||
return long(util.get_xml_path(self._XMLDesc(0), "/pool/available"))
|
||||
return int(util.get_xml_path(self._XMLDesc(0), "/pool/available"))
|
||||
|
||||
def get_capacity(self):
|
||||
return long(util.get_xml_path(self._XMLDesc(0), "/pool/capacity"))
|
||||
return int(util.get_xml_path(self._XMLDesc(0), "/pool/capacity"))
|
||||
|
||||
def get_pretty_allocation(self):
|
||||
return util.pretty_bytes(self.get_allocation())
|
||||
|
|
|
@ -34,9 +34,9 @@ def randomUUID():
|
|||
"%02x" * 6]) % tuple(u)
|
||||
|
||||
|
||||
def randomPasswd(length=12, alphabet=string.letters + string.digits):
|
||||
def randomPasswd(length=12, alphabet=string.ascii_letters + string.digits):
|
||||
"""Generate a random password"""
|
||||
return ''.join([random.choice(alphabet) for i in xrange(length)])
|
||||
return ''.join([random.choice(alphabet) for i in range(length)])
|
||||
|
||||
|
||||
def get_max_vcpus(conn, type=None):
|
||||
|
@ -75,7 +75,7 @@ def compareMAC(p, q):
|
|||
else:
|
||||
return -1
|
||||
|
||||
for i in xrange(len(pa)):
|
||||
for i in range(len(pa)):
|
||||
n = int(pa[i], 0x10) - int(qa[i], 0x10)
|
||||
if n > 0:
|
||||
return 1
|
||||
|
@ -160,12 +160,12 @@ def validate_macaddr(val):
|
|||
if val is None:
|
||||
return
|
||||
|
||||
if not (isinstance(val, str) or isinstance(val, basestring)):
|
||||
if not (isinstance(val, str) or isinstance(val, str)):
|
||||
raise ValueError("MAC address must be a string.")
|
||||
|
||||
form = re.match("^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$", val)
|
||||
if form is None:
|
||||
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was '%s'" % val)
|
||||
raise ValueError(f"MAC address must be of the format AA:BB:CC:DD:EE:FF, was {val}")
|
||||
|
||||
|
||||
# Mapping of UEFI binary names to their associated architectures. We
|
||||
|
@ -189,4 +189,4 @@ uefi_arch_patterns = {
|
|||
"armv7l": [
|
||||
r".*arm/QEMU_EFI.*", # fedora, gerd's firmware repo
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
"""
|
||||
Django dev settings for webvirtcloud project.
|
||||
|
||||
HowTo: python manage.py runserver 0:8000 --settings=webvirtcloud.settings-dev
|
||||
HowTo: python3 manage.py runserver 0:8000 --settings=webvirtcloud.settings-dev
|
||||
|
||||
"""
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ Django settings for webvirtcloud project.
|
|||
"""
|
||||
|
||||
import os
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
SECRET_KEY = ''
|
||||
|
||||
|
@ -12,73 +14,44 @@ DEBUG = True
|
|||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
INSTALLED_APPS = (
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'accounts',
|
||||
'computes',
|
||||
'console',
|
||||
'networks',
|
||||
'storages',
|
||||
'interfaces',
|
||||
'nwfilters',
|
||||
'instances',
|
||||
'secrets',
|
||||
'logs',
|
||||
'accounts',
|
||||
'create',
|
||||
'datasource',
|
||||
)
|
||||
'networks',
|
||||
'instances',
|
||||
'interfaces',
|
||||
'nwfilters',
|
||||
'storages',
|
||||
'secrets',
|
||||
'logs',
|
||||
]
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.RemoteUserMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
#'django.contrib.auth.backends.RemoteUserBackend',
|
||||
#'accounts.backends.MyRemoteUserBackend',
|
||||
)
|
||||
|
||||
LOGIN_URL = '/accounts/login'
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'webvirtcloud.urls'
|
||||
|
||||
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, "static"),
|
||||
)
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
|
@ -95,9 +68,57 @@ TEMPLATES = [
|
|||
},
|
||||
]
|
||||
|
||||
## WebVirtCloud settings
|
||||
WSGI_APPLICATION = 'webvirtcloud.wsgi.application'
|
||||
|
||||
# Wobsock port
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
#'django.contrib.auth.backends.RemoteUserBackend',
|
||||
#'accounts.backends.MyRemoteUserBackend',
|
||||
]
|
||||
|
||||
LOGIN_URL = '/accounts/login'
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, "static"),
|
||||
]
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"handlers": {"mail_admins": {"level": "ERROR", "class": "django.utils.log.AdminEmailHandler"}},
|
||||
"loggers": {
|
||||
"django.request": {"handlers": ["mail_admins"], "level": "ERROR", "propagate": True}
|
||||
},
|
||||
}
|
||||
|
||||
#
|
||||
## WebVirtCloud settings
|
||||
#
|
||||
|
||||
# Websock port
|
||||
WS_PORT = 6080
|
||||
|
||||
# Websock host
|
||||
|
@ -112,19 +133,19 @@ WS_PUBLIC_HOST = None
|
|||
# Websock SSL connection
|
||||
WS_CERT = None
|
||||
|
||||
# list of console types
|
||||
# List of console types
|
||||
QEMU_CONSOLE_TYPES = ['vnc', 'spice']
|
||||
|
||||
# default console type
|
||||
# Default console type
|
||||
QEMU_CONSOLE_DEFAULT_TYPE = 'vnc'
|
||||
|
||||
# list of console listen addresses
|
||||
# List of console listen addresses
|
||||
QEMU_CONSOLE_LISTEN_ADDRESSES = (
|
||||
('127.0.0.1', 'Localhost'),
|
||||
('0.0.0.0', 'All interfaces'),
|
||||
)
|
||||
|
||||
# list taken from http://qemu.weilnetz.de/qemu-doc.html#sec_005finvocation
|
||||
# List taken from http://qemu.weilnetz.de/qemu-doc.html#sec_005finvocation
|
||||
QEMU_KEYMAPS = ['ar', 'da', 'de', 'de-ch', 'en-gb', 'en-us', 'es', 'et', 'fi',
|
||||
'fo', 'fr', 'fr-be', 'fr-ca', 'fr-ch', 'hr', 'hu', 'is', 'it',
|
||||
'ja', 'lt', 'lv', 'mk', 'nl', 'nl-be', 'no', 'pl', 'pt',
|
||||
|
@ -152,6 +173,9 @@ VIEW_ACCOUNTS_STYLE = 'grid'
|
|||
# available list style: default (grouped), nongrouped
|
||||
VIEW_INSTANCES_LIST_STYLE = 'grouped'
|
||||
|
||||
# available options: True, False
|
||||
VIEW_INSTANCE_DETAIL_BOTTOM_BAR = True
|
||||
|
||||
# available volume format: raw, qcow2, qcow
|
||||
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
|
||||
|
||||
|
@ -181,3 +205,10 @@ INSTANCE_CPU_DEFAULT_MODE = 'host-model'
|
|||
|
||||
# Chipset/Machine: pc or q35 for x86_64
|
||||
INSTANCE_MACHINE_DEFAULT_TYPE = 'q35'
|
||||
|
||||
# Firmware: BIOS or UEFI for x86_64
|
||||
INSTANCE_FIRMWARE_DEFAULT_TYPE = 'BIOS'
|
||||
|
||||
# Architecture: x86_64, i686, etc
|
||||
INSTANCE_ARCH_DEFAULT_TYPE = 'x86_64'
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
|
||||
from instances.views import index
|
||||
from console.views import console
|
||||
# from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', index, name='index'),
|
||||
path('', index, name='index'),
|
||||
|
||||
url(r'^instances/', include('instances.urls')),
|
||||
url(r'^accounts/', include('accounts.urls')),
|
||||
url(r'^computes/', include('computes.urls')),
|
||||
url(r'^logs/', include('logs.urls')),
|
||||
url(r'^datasource/', include('datasource.urls')),
|
||||
url(r'^console/$', console, name='console'),
|
||||
#url(r'^admin/', include(admin.site.urls)),
|
||||
path('instances/', include('instances.urls')),
|
||||
path('accounts/', include('accounts.urls')),
|
||||
path('computes/', include('computes.urls')),
|
||||
path('logs/', include('logs.urls')),
|
||||
path('datasource/', include('datasource.urls')),
|
||||
path('console/', console, name='console'),
|
||||
# url(r'^admin/', include(admin.site.urls)),
|
||||
]
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue