mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +00:00
Merge pull request #156 from catborise/master
Django version requirement, Libxml2->lxml change, account detail validation add, some bugs
This commit is contained in:
commit
85aac6fa0a
23 changed files with 353 additions and 136 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@ console/cert.pem*
|
||||||
tags
|
tags
|
||||||
dhcpd.*
|
dhcpd.*
|
||||||
webvirtcloud/settings.py
|
webvirtcloud/settings.py
|
||||||
|
*migrations/*
|
||||||
|
|
|
@ -32,7 +32,7 @@ print(''.join([random.SystemRandom().choice(haystack) for _ in range(50)]))
|
||||||
### Install WebVirtCloud panel (Ubuntu)
|
### Install WebVirtCloud panel (Ubuntu)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt-get -y install git python-virtualenv python-dev libxml2-dev libvirt-dev zlib1g-dev nginx supervisor libsasl2-modules gcc pkg-config
|
sudo apt-get -y install git python-virtualenv python-dev python-lxml libvirt-dev zlib1g-dev nginx supervisor libsasl2-modules gcc pkg-config
|
||||||
git clone https://github.com/retspen/webvirtcloud
|
git clone https://github.com/retspen/webvirtcloud
|
||||||
cd webvirtcloud
|
cd webvirtcloud
|
||||||
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
|
cp webvirtcloud/settings.py.template webvirtcloud/settings.py
|
||||||
|
|
35
accounts/migrations/0010_auto_20180625_1236.py
Normal file
35
accounts/migrations/0010_auto_20180625_1236.py
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# -*- 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'),
|
||||||
|
),
|
||||||
|
]
|
36
accounts/migrations/0011_auto_20180625_1313.py
Normal file
36
accounts/migrations/0011_auto_20180625_1313.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- 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)]),
|
||||||
|
),
|
||||||
|
]
|
37
accounts/migrations/0012_auto_20180625_1331.py
Normal file
37
accounts/migrations/0012_auto_20180625_1331.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# -*- 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.')]),
|
||||||
|
),
|
||||||
|
]
|
37
accounts/migrations/0013_auto_20180625_1358.py
Normal file
37
accounts/migrations/0013_auto_20180625_1358.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# -*- 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)]),
|
||||||
|
),
|
||||||
|
]
|
|
@ -2,6 +2,7 @@ from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
|
from django.core.validators import integer_validator, MinValueValidator
|
||||||
|
|
||||||
|
|
||||||
class UserInstance(models.Model):
|
class UserInstance(models.Model):
|
||||||
|
@ -26,10 +27,10 @@ class UserSSHKey(models.Model):
|
||||||
class UserAttributes(models.Model):
|
class UserAttributes(models.Model):
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
can_clone_instances = models.BooleanField(default=True)
|
can_clone_instances = models.BooleanField(default=True)
|
||||||
max_instances = models.IntegerField(default=1)
|
max_instances = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[integer_validator,MinValueValidator(-1),])
|
||||||
max_cpus = models.IntegerField(default=1)
|
max_cpus = models.IntegerField(default=1, help_text="-1 for unlimited. Any integer value", validators=[integer_validator,MinValueValidator(-1)])
|
||||||
max_memory = models.IntegerField(default=2048)
|
max_memory = models.IntegerField(default=2048, help_text="-1 for unlimited. Any integer value", validators=[integer_validator,MinValueValidator(-1)])
|
||||||
max_disk_size = models.IntegerField(default=20)
|
max_disk_size = models.IntegerField(default=20, help_text="-1 for unlimited. Any integer value", validators=[integer_validator,MinValueValidator(-1)])
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create_missing_userattributes(user):
|
def create_missing_userattributes(user):
|
||||||
|
|
|
@ -92,25 +92,25 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">{% trans "Max instances" %}</label>
|
<label class="col-sm-4 control-label">{% trans "Max instances" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" name="userattributes_max_instances" class="form-control" value="{{ user.userattributes.max_instances }}">
|
<input type="text" name="userattributes_max_instances" class="form-control" value="{{ user.userattributes.max_instances}}" required="True" >
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">{% trans "Max cpus" %}</label>
|
<label class="col-sm-4 control-label">{% trans "Max cpus" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" name="userattributes_max_cpus" class="form-control" value="{{ user.userattributes.max_cpus }}">
|
<input type="text" name="userattributes_max_cpus" class="form-control" value="{{ user.userattributes.max_cpus }}" required="True">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">{% trans "Max memory (MB)" %}</label>
|
<label class="col-sm-4 control-label">{% trans "Max memory (MB)" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" name="userattributes_max_memory" class="form-control" value="{{ user.userattributes.max_memory }}">
|
<input type="text" name="userattributes_max_memory" class="form-control" value="{{ user.userattributes.max_memory}}" required="True">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-sm-4 control-label">{% trans "Max disk size (GB)" %}</label>
|
<label class="col-sm-4 control-label">{% trans "Max disk size (GB)" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" name="userattributes_max_disk_size" class="form-control" value="{{ user.userattributes.max_disk_size }}">
|
<input type="text" name="userattributes_max_disk_size" class="form-control" value="{{ user.userattributes.max_disk_size }}" required="True">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
from django.contrib import auth
|
||||||
|
from django.contrib.auth import views as auth_views
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^login/$', 'django.contrib.auth.views.login',
|
url(r'^login/$', auth_views.LoginView.as_view(template_name='login.html'), name='login'),
|
||||||
{'template_name': 'login.html'}, name='login'),
|
url(r'^logout/$', auth_views.LogoutView.as_view(template_name='logout.html'), name='logout'),
|
||||||
url(r'^logout/$', 'django.contrib.auth.views.logout',
|
url(r'^profile/$', views.profile, name='profile'), url(r'^$', views.accounts, name='accounts'),
|
||||||
{'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'),
|
url(r'^profile/(?P<user_id>[0-9]+)/$', views.account, name='account'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -8,6 +8,8 @@ from accounts.models import *
|
||||||
from instances.models import Instance
|
from instances.models import Instance
|
||||||
from accounts.forms import UserAddForm
|
from accounts.forms import UserAddForm
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.validators import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
|
@ -92,22 +94,31 @@ def accounts(request):
|
||||||
UserAttributes.configure_user(new_user)
|
UserAttributes.configure_user(new_user)
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'edit' in request.POST:
|
if 'edit' in request.POST:
|
||||||
|
CHECKBOX_MAPPING = {'on': True, 'off': False, }
|
||||||
|
|
||||||
user_id = request.POST.get('user_id', '')
|
user_id = request.POST.get('user_id', '')
|
||||||
user_pass = request.POST.get('user_pass', '')
|
user_pass = request.POST.get('user_pass', '')
|
||||||
user_edit = User.objects.get(id=user_id)
|
user_edit = User.objects.get(id=user_id)
|
||||||
user_edit.set_password(user_pass)
|
|
||||||
user_edit.is_staff = request.POST.get('user_is_staff', False)
|
if user_pass != '': user_edit.set_password(user_pass)
|
||||||
user_edit.is_superuser = request.POST.get('user_is_superuser', False)
|
user_edit.is_staff = CHECKBOX_MAPPING.get(request.POST.get('user_is_staff', 'off'))
|
||||||
|
user_edit.is_superuser = CHECKBOX_MAPPING.get(request.POST.get('user_is_superuser', 'off'))
|
||||||
user_edit.save()
|
user_edit.save()
|
||||||
|
|
||||||
userattributes = user_edit.userattributes
|
UserAttributes.create_missing_userattributes(user_edit)
|
||||||
userattributes.can_clone_instances = request.POST.get('userattributes_can_clone_instances', False)
|
user_edit.userattributes.can_clone_instances = CHECKBOX_MAPPING.get(request.POST.get('userattributes_can_clone_instances', 'off'))
|
||||||
userattributes.max_instances = request.POST.get('userattributes_max_instances', 0)
|
user_edit.userattributes.max_instances = request.POST.get('userattributes_max_instances', 0)
|
||||||
userattributes.max_cpus = request.POST.get('userattributes_max_cpus', 0)
|
user_edit.userattributes.max_cpus = request.POST.get('userattributes_max_cpus', 0)
|
||||||
userattributes.max_memory = request.POST.get('userattributes_max_memory', 0)
|
user_edit.userattributes.max_memory = request.POST.get('userattributes_max_memory', 0)
|
||||||
userattributes.max_disk_size = request.POST.get('userattributes_max_disk_size', 0)
|
user_edit.userattributes.max_disk_size = request.POST.get('userattributes_max_disk_size', 0)
|
||||||
userattributes.save()
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
try:
|
||||||
|
user_edit.userattributes.clean_fields()
|
||||||
|
except ValidationError as exc:
|
||||||
|
error_messages.append(exc)
|
||||||
|
else:
|
||||||
|
user_edit.userattributes.save()
|
||||||
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
if 'block' in request.POST:
|
if 'block' in request.POST:
|
||||||
user_id = request.POST.get('user_id', '')
|
user_id = request.POST.get('user_id', '')
|
||||||
user_block = User.objects.get(id=user_id)
|
user_block = User.objects.get(id=user_id)
|
||||||
|
|
|
@ -123,7 +123,7 @@ def computes(request):
|
||||||
compute_edit.hostname = data['hostname']
|
compute_edit.hostname = data['hostname']
|
||||||
compute_edit.login = data['login']
|
compute_edit.login = data['login']
|
||||||
compute_edit.password = data['password']
|
compute_edit.password = data['password']
|
||||||
compute.edit_details = data['details']
|
#compute_edit.details = data['details']
|
||||||
compute_edit.save()
|
compute_edit.save()
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -37,21 +37,40 @@
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
|
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
|
||||||
<thead>
|
<thead >
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
<th>Name<br>Description</th>
|
<th>Name<br>Description</th>
|
||||||
<th>Host<br>User</th>
|
<th>Host<br>User</th>
|
||||||
<th>Status</th>
|
<th>Status</th>
|
||||||
<th>VCPU</th>
|
<th>VCPU</th>
|
||||||
<th>Memory<br>({% trans "MB" %})</th>
|
<th>Memory<br>({% trans "MB" %})</th>
|
||||||
<th data-sortable="false" style="width:205px;">Actions</th>
|
<th data-sortable="false" style="width:205px;">Actions & Usage</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="searchable">
|
<tbody class="searchable">
|
||||||
{% for host, inst in all_host_vms.items %}
|
{% for host, inst in all_host_vms.items %}
|
||||||
|
<!-- copied /-->
|
||||||
|
<tr class="success" style="font-size:16px">
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a></td>
|
||||||
|
<td></td>
|
||||||
|
<td>{% ifequal host.2 1 %}<span class="label label-success">{% trans "Active" %}
|
||||||
|
</span>{% endifequal %}
|
||||||
|
{% ifequal host.2 2 %}<span class="label label-danger">{% trans "Not Active" %}
|
||||||
|
</span>{% endifequal %}
|
||||||
|
{% ifequal host.2 3 %}<span class="label label-danger">{% trans "Connection Failed" %}
|
||||||
|
</span>{% endifequal %}
|
||||||
|
</td>
|
||||||
|
<td style="text-align:center;">{{ host.3 }}</td>
|
||||||
|
<td style="text-align:right;">{{ host.4|filesizeformat }}</td>
|
||||||
|
<td style="text-align:left;">Mem Usage: {{ host.5 }}%</td>
|
||||||
|
</tr>
|
||||||
|
<!-- copied /-->
|
||||||
{% for vm, info in inst.items %}
|
{% for vm, info in inst.items %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br><small><em>{{ info.title }}</em></small></td>
|
<td></td>
|
||||||
|
<td>{{ forloop.counter }}   <a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br><small><em>{{ info.title }}</em></small></td>
|
||||||
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a><br><small><em>{% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
|
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a><br><small><em>{% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
|
||||||
<td>{% ifequal info.status 1 %}
|
<td>{% ifequal info.status 1 %}
|
||||||
<span class="text-success">{% trans "Active" %}</span>
|
<span class="text-success">{% trans "Active" %}</span>
|
||||||
|
@ -63,8 +82,8 @@
|
||||||
<span class="text-warning">{% trans "Suspend" %}</span>
|
<span class="text-warning">{% trans "Suspend" %}</span>
|
||||||
{% endifequal %}
|
{% endifequal %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ info.vcpu }}</td>
|
<td style="text-align:center;">{{ info.vcpu }}</td>
|
||||||
<td>{{ info.memory }}</td>
|
<td style="text-align:right;">{{ info.memory |filesizeformat }}</td>
|
||||||
<td><form action="" method="post" role="form">{% csrf_token %}
|
<td><form action="" method="post" role="form">{% csrf_token %}
|
||||||
<input type="hidden" name="name" value="{{ vm }}"/>
|
<input type="hidden" name="name" value="{{ vm }}"/>
|
||||||
<input type="hidden" name="compute_id" value="{{ host.0 }}"/>
|
<input type="hidden" name="compute_id" value="{{ host.0 }}"/>
|
||||||
|
|
|
@ -68,11 +68,22 @@ def instances(request):
|
||||||
i.delete()
|
i.delete()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
check_uuid = Instance.objects.get(compute_id=comp.id, name=vm)
|
check_uuid = Instance.objects.get(compute_id=comp["id"], name=vm)
|
||||||
if check_uuid.uuid != info['uuid']:
|
if check_uuid.uuid != info['uuid']:
|
||||||
check_uuid.save()
|
check_uuid.save()
|
||||||
all_host_vms[comp.id, comp.name][vm]['is_template'] = check_uuid.is_template
|
|
||||||
all_host_vms[comp.id, comp.name][vm]['userinstances'] = get_userinstances_info(check_uuid)
|
all_host_vms[comp_info["id"],
|
||||||
|
comp_info["name"],
|
||||||
|
comp_info["status"],
|
||||||
|
comp_info["cpu"],
|
||||||
|
comp_info["mem_size"],
|
||||||
|
comp_info["mem_perc"]][vm]['is_template'] = check_uuid.is_template
|
||||||
|
all_host_vms[comp_info["id"],
|
||||||
|
comp_info["name"],
|
||||||
|
comp_info["status"],
|
||||||
|
comp_info["cpu"],
|
||||||
|
comp_info["mem_size"],
|
||||||
|
comp_info["mem_perc"]][vm]['userinstances'] = get_userinstances_info(check_uuid)
|
||||||
except Instance.DoesNotExist:
|
except Instance.DoesNotExist:
|
||||||
check_uuid = Instance(compute_id=comp.id, name=vm, uuid=info['uuid'])
|
check_uuid = Instance(compute_id=comp.id, name=vm, uuid=info['uuid'])
|
||||||
check_uuid.save()
|
check_uuid.save()
|
||||||
|
@ -90,14 +101,27 @@ def instances(request):
|
||||||
all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
|
all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
|
||||||
else:
|
else:
|
||||||
for comp in computes:
|
for comp in computes:
|
||||||
if connection_manager.host_is_up(comp.type, comp.hostname):
|
status = connection_manager.host_is_up(comp.type, comp.hostname)
|
||||||
|
if status:
|
||||||
try:
|
try:
|
||||||
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
|
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
|
||||||
host_instances = conn.get_host_instances()
|
comp_node_info = conn.get_node_info()
|
||||||
if host_instances:
|
comp_mem = conn.get_memory_usage()
|
||||||
all_host_vms[comp.id, comp.name] = host_instances
|
comp_instances = conn.get_host_instances(True)
|
||||||
for vm, info in host_instances.items():
|
|
||||||
refresh_instance_database(comp, vm, info)
|
if comp_instances:
|
||||||
|
comp_info= {
|
||||||
|
"id": comp.id,
|
||||||
|
"name": comp.name,
|
||||||
|
"status": status,
|
||||||
|
"cpu": comp_node_info[3],
|
||||||
|
"mem_size": comp_node_info[2],
|
||||||
|
"mem_perc": comp_mem['percent']
|
||||||
|
}
|
||||||
|
all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"],
|
||||||
|
comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances
|
||||||
|
for vm, info in comp_instances.items():
|
||||||
|
refresh_instance_database(comp_info, vm, info)
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
|
|
7
static/css/bootstrap.min.css
vendored
7
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
5
static/css/bootstrap.min.css_332
Executable file
5
static/css/bootstrap.min.css_332
Executable file
File diff suppressed because one or more lines are too long
10
static/js/bootstrap.min.js
vendored
10
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.min.js_332
Executable file
7
static/js/bootstrap.min.js_332
Executable file
File diff suppressed because one or more lines are too long
|
@ -444,8 +444,8 @@ class wvmConnect(object):
|
||||||
def get_net_device(self):
|
def get_net_device(self):
|
||||||
netdevice = []
|
netdevice = []
|
||||||
def get_info(ctx):
|
def get_info(ctx):
|
||||||
dev_type = util.get_xpath(ctx, '/device/capability/@type')
|
dev_type = util.get_xpath('/device/capability/@type')
|
||||||
interface = util.get_xpath(ctx, '/device/capability/interface')
|
interface = util.get_xpath('/device/capability/interface')
|
||||||
return (dev_type, interface)
|
return (dev_type, interface)
|
||||||
for dev in self.wvm.listAllDevices(0):
|
for dev in self.wvm.listAllDevices(0):
|
||||||
xml = dev.XMLDesc(0)
|
xml = dev.XMLDesc(0)
|
||||||
|
@ -454,19 +454,21 @@ class wvmConnect(object):
|
||||||
netdevice.append(interface)
|
netdevice.append(interface)
|
||||||
return netdevice
|
return netdevice
|
||||||
|
|
||||||
def get_host_instances(self):
|
def get_host_instances(self, raw_mem_size=False):
|
||||||
vname = {}
|
vname = {}
|
||||||
def get_info(ctx):
|
def get_info(doc):
|
||||||
mem = util.get_xpath(ctx, "/domain/currentMemory")
|
mem = util.get_xpath(doc, "/domain/currentMemory")
|
||||||
mem = int(mem) / 1024
|
mem = int(mem) / 1024
|
||||||
cur_vcpu = util.get_xpath(ctx, "/domain/vcpu/@current")
|
if raw_mem_size:
|
||||||
|
mem = int(mem) * (1024*1024)
|
||||||
|
cur_vcpu = util.get_xpath(doc, "/domain/vcpu/@current")
|
||||||
if cur_vcpu:
|
if cur_vcpu:
|
||||||
vcpu = cur_vcpu
|
vcpu = cur_vcpu
|
||||||
else:
|
else:
|
||||||
vcpu = util.get_xpath(ctx, "/domain/vcpu")
|
vcpu = util.get_xpath(doc, "/domain/vcpu")
|
||||||
title = util.get_xpath(ctx, "/domain/title")
|
title = util.get_xpath(doc, "/domain/title")
|
||||||
title = title if title else ''
|
title = title if title else ''
|
||||||
description = util.get_xpath(ctx, "/domain/description")
|
description = util.get_xpath(doc, "/domain/description")
|
||||||
description = description if description else ''
|
description = description if description else ''
|
||||||
return (mem, vcpu, title, description)
|
return (mem, vcpu, title, description)
|
||||||
for name in self.get_instances():
|
for name in self.get_instances():
|
||||||
|
|
|
@ -3,11 +3,11 @@ from vrtManager.connection import wvmConnect
|
||||||
from vrtManager.util import get_xml_path
|
from vrtManager.util import get_xml_path
|
||||||
|
|
||||||
|
|
||||||
def cpu_version(ctx):
|
def cpu_version(doc):
|
||||||
for info in ctx.xpathEval('/sysinfo/processor/entry'):
|
for info in doc.xpath('/sysinfo/processor/entry'):
|
||||||
elem = info.xpathEval('@name')[0].content
|
elem = info.xpath('@name')[0]
|
||||||
if elem == 'version':
|
if elem == 'version':
|
||||||
return info.content
|
return info.text
|
||||||
return 'Unknown'
|
return 'Unknown'
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,12 +59,12 @@ class wvmHostDetails(wvmConnect):
|
||||||
Function return host server information: hostname, cpu, memory, ...
|
Function return host server information: hostname, cpu, memory, ...
|
||||||
"""
|
"""
|
||||||
info = []
|
info = []
|
||||||
info.append(self.wvm.getHostname())
|
info.append(self.wvm.getHostname()) # hostname
|
||||||
info.append(self.wvm.getInfo()[0])
|
info.append(self.wvm.getInfo()[0]) # architecture
|
||||||
info.append(self.wvm.getInfo()[1] * 1048576)
|
info.append(self.wvm.getInfo()[1] * 1048576) # memory
|
||||||
info.append(self.wvm.getInfo()[2])
|
info.append(self.wvm.getInfo()[2]) # cpu core count
|
||||||
info.append(get_xml_path(self.wvm.getSysinfo(0), func=cpu_version))
|
info.append(get_xml_path(self.wvm.getSysinfo(0), func=cpu_version)) # cpu version
|
||||||
info.append(self.wvm.getURI())
|
info.append(self.wvm.getURI()) #uri
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def hypervisor_type(self):
|
def hypervisor_type(self):
|
||||||
|
|
|
@ -203,10 +203,10 @@ class wvmInstance(wvmConnect):
|
||||||
|
|
||||||
def get_net_device(self):
|
def get_net_device(self):
|
||||||
def get_mac_ipaddr(net, mac_host):
|
def get_mac_ipaddr(net, mac_host):
|
||||||
def fixed(ctx):
|
def fixed(doc):
|
||||||
for net in ctx.xpathEval('/network/ip/dhcp/host'):
|
for net in doc.xpath('/network/ip/dhcp/host'):
|
||||||
mac = net.xpathEval('@mac')[0].content
|
mac = net.xpath('@mac')[0]
|
||||||
host = net.xpathEval('@ip')[0].content
|
host = net.xpath('@ip')[0]
|
||||||
if mac == mac_host:
|
if mac == mac_host:
|
||||||
return host
|
return host
|
||||||
return None
|
return None
|
||||||
|
@ -215,9 +215,9 @@ class wvmInstance(wvmConnect):
|
||||||
|
|
||||||
def networks(ctx):
|
def networks(ctx):
|
||||||
result = []
|
result = []
|
||||||
for net in ctx.xpathEval('/domain/devices/interface'):
|
for net in ctx.xpath('/domain/devices/interface'):
|
||||||
mac_host = net.xpathEval('mac/@address')[0].content
|
mac_host = net.xpath('mac/@address')[0]
|
||||||
nic_host = net.xpathEval('source/@network|source/@bridge|source/@dev|target/@dev')[0].content
|
nic_host = net.xpath('source/@network|source/@bridge|source/@dev|target/@dev')[0]
|
||||||
try:
|
try:
|
||||||
net = self.get_network(nic_host)
|
net = self.get_network(nic_host)
|
||||||
ip = get_mac_ipaddr(net, mac_host)
|
ip = get_mac_ipaddr(net, mac_host)
|
||||||
|
@ -229,7 +229,7 @@ class wvmInstance(wvmConnect):
|
||||||
return util.get_xml_path(self._XMLDesc(0), func=networks)
|
return util.get_xml_path(self._XMLDesc(0), func=networks)
|
||||||
|
|
||||||
def get_disk_device(self):
|
def get_disk_device(self):
|
||||||
def disks(ctx):
|
def disks(doc):
|
||||||
result = []
|
result = []
|
||||||
dev = None
|
dev = None
|
||||||
volume = None
|
volume = None
|
||||||
|
@ -237,13 +237,14 @@ class wvmInstance(wvmConnect):
|
||||||
src_fl = None
|
src_fl = None
|
||||||
disk_format = None
|
disk_format = None
|
||||||
disk_size = None
|
disk_size = None
|
||||||
for disk in ctx.xpathEval('/domain/devices/disk'):
|
|
||||||
device = disk.xpathEval('@device')[0].content
|
for disk in doc.xpath('/domain/devices/disk'):
|
||||||
|
device = disk.xpath('@device')[0]
|
||||||
if device == 'disk':
|
if device == 'disk':
|
||||||
try:
|
try:
|
||||||
dev = disk.xpathEval('target/@dev')[0].content
|
dev = disk.xpath('target/@dev')[0]
|
||||||
src_fl = disk.xpathEval('source/@file|source/@dev|source/@name|source/@volume')[0].content
|
src_fl = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0]
|
||||||
disk_format = disk.xpathEval('driver/@type')[0].content
|
disk_format = disk.xpath('driver/@type')[0]
|
||||||
try:
|
try:
|
||||||
vol = self.get_volume_by_path(src_fl)
|
vol = self.get_volume_by_path(src_fl)
|
||||||
volume = vol.name()
|
volume = vol.name()
|
||||||
|
@ -263,19 +264,19 @@ class wvmInstance(wvmConnect):
|
||||||
return util.get_xml_path(self._XMLDesc(0), func=disks)
|
return util.get_xml_path(self._XMLDesc(0), func=disks)
|
||||||
|
|
||||||
def get_media_device(self):
|
def get_media_device(self):
|
||||||
def disks(ctx):
|
def disks(doc):
|
||||||
result = []
|
result = []
|
||||||
dev = None
|
dev = None
|
||||||
volume = None
|
volume = None
|
||||||
storage = None
|
storage = None
|
||||||
src_fl = None
|
src_fl = None
|
||||||
for media in ctx.xpathEval('/domain/devices/disk'):
|
for media in doc.xpath('/domain/devices/disk'):
|
||||||
device = media.xpathEval('@device')[0].content
|
device = media.xpath('@device')[0]
|
||||||
if device == 'cdrom':
|
if device == 'cdrom':
|
||||||
try:
|
try:
|
||||||
dev = media.xpathEval('target/@dev')[0].content
|
dev = media.xpath('target/@dev')
|
||||||
try:
|
try:
|
||||||
src_fl = media.xpathEval('source/@file')[0].content
|
src_fl = media.xpath('source/@file')
|
||||||
vol = self.get_volume_by_path(src_fl)
|
vol = self.get_volume_by_path(src_fl)
|
||||||
volume = vol.name()
|
volume = vol.name()
|
||||||
stg = vol.storagePoolLookupByVolume()
|
stg = vol.storagePoolLookupByVolume()
|
||||||
|
@ -487,8 +488,7 @@ class wvmInstance(wvmConnect):
|
||||||
return socket
|
return socket
|
||||||
|
|
||||||
def get_console_type(self):
|
def get_console_type(self):
|
||||||
console_type = util.get_xml_path(self._XMLDesc(0),
|
console_type = util.get_xml_path(self._XMLDesc(0),"/domain/devices/graphics/@type")
|
||||||
"/domain/devices/graphics/@type")
|
|
||||||
return console_type
|
return console_type
|
||||||
|
|
||||||
def set_console_type(self, console_type):
|
def set_console_type(self, console_type):
|
||||||
|
|
|
@ -166,11 +166,11 @@ class wvmNetwork(wvmConnect):
|
||||||
return bool(util.get_xml_path(xml, "/network/ip/dhcp/bootp/@file"))
|
return bool(util.get_xml_path(xml, "/network/ip/dhcp/bootp/@file"))
|
||||||
|
|
||||||
def get_mac_ipaddr(self):
|
def get_mac_ipaddr(self):
|
||||||
def network(ctx):
|
def network(doc):
|
||||||
result = []
|
result = []
|
||||||
for net in ctx.xpathEval('/network/ip/dhcp/host'):
|
for net in doc.xpath('/network/ip/dhcp/host'):
|
||||||
host = net.xpathEval('@ip')[0].content
|
host = net.xpath('@ip')[0]
|
||||||
mac = net.xpathEval('@mac')[0].content
|
mac = net.xpathEval('@mac')[0]
|
||||||
result.append({'host': host, 'mac': mac})
|
result.append({'host': host, 'mac': mac})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import random
|
import random
|
||||||
import libxml2
|
import lxml.etree as etree
|
||||||
import libvirt
|
import libvirt
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
@ -86,41 +86,43 @@ def get_xml_path(xml, path=None, func=None):
|
||||||
of a passed function (receives xpathContext as its only arg)
|
of a passed function (receives xpathContext as its only arg)
|
||||||
"""
|
"""
|
||||||
doc = None
|
doc = None
|
||||||
ctx = None
|
#ctx = None
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
try:
|
#try:
|
||||||
doc = libxml2.parseDoc(xml)
|
doc = etree.fromstring(xml)
|
||||||
ctx = doc.xpathNewContext()
|
#ctx = doc.xpathNewContext()
|
||||||
|
if path:
|
||||||
|
result = get_xpath(doc, path)
|
||||||
|
|
||||||
if path:
|
elif func:
|
||||||
result = get_xpath(ctx, path)
|
result = func(doc)
|
||||||
|
|
||||||
elif func:
|
else:
|
||||||
result = func(ctx)
|
raise ValueError("'path' or 'func' is required.")
|
||||||
|
#finally:
|
||||||
else:
|
#if doc:
|
||||||
raise ValueError("'path' or 'func' is required.")
|
# doc.freeDoc()
|
||||||
finally:
|
#if ctx:
|
||||||
if doc:
|
# ctx.xpathFreeContext()
|
||||||
doc.freeDoc()
|
|
||||||
if ctx:
|
|
||||||
ctx.xpathFreeContext()
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def get_xpath(ctx, path):
|
def get_xpath(doc, path):
|
||||||
result = None
|
result = None
|
||||||
ret = ctx.xpathEval(path)
|
|
||||||
|
ret = doc.xpath(path)
|
||||||
if ret is not None:
|
if ret is not None:
|
||||||
if type(ret) == list:
|
if type(ret) == list:
|
||||||
if len(ret) >= 1:
|
if len(ret) >= 1:
|
||||||
result = ret[0].content
|
if hasattr(ret[0],'text'):
|
||||||
|
result = ret[0].text
|
||||||
|
else:
|
||||||
|
result = ret[0]
|
||||||
else:
|
else:
|
||||||
result = ret
|
result = ret
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
def pretty_mem(val):
|
def pretty_mem(val):
|
||||||
val = int(val)
|
val = int(val)
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
from django.conf.urls import patterns, include, url
|
from django.conf.urls import include, url
|
||||||
|
|
||||||
|
from instances.views import instances, instance, index
|
||||||
|
from storages.views import storages, storage
|
||||||
|
from networks.views import networks, network
|
||||||
|
from secrets.views import secrets
|
||||||
|
from create.views import create_instance
|
||||||
|
from interfaces.views import interfaces, interface
|
||||||
|
from console.views import console
|
||||||
# from django.contrib import admin
|
# from django.contrib import admin
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = [
|
||||||
url(r'^$', 'instances.views.index', name='index'),
|
url(r'^$', index, name='index'),
|
||||||
url(r'^instances/$', 'instances.views.instances', name='instances'),
|
url(r'^instances/$', instances, name='instances'),
|
||||||
|
|
||||||
url(r'^instance/', include('instances.urls')),
|
url(r'^instance/', include('instances.urls')),
|
||||||
url(r'^accounts/', include('accounts.urls')),
|
url(r'^accounts/', include('accounts.urls')),
|
||||||
|
@ -11,23 +19,15 @@ urlpatterns = patterns('',
|
||||||
url(r'^logs/', include('logs.urls')),
|
url(r'^logs/', include('logs.urls')),
|
||||||
url(r'^datasource/', include('datasource.urls')),
|
url(r'^datasource/', include('datasource.urls')),
|
||||||
|
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/storages/$',
|
url(r'^compute/(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
|
||||||
'storages.views.storages', name='storages'),
|
url(r'^compute/(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$',
|
url(r'^compute/(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
|
||||||
'storages.views.storage', name='storage'),
|
url(r'^compute/(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/networks/$',
|
url(r'^compute/(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
|
||||||
'networks.views.networks', name='networks'),
|
url(r'^compute/(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$',
|
url(r'^compute/(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
|
||||||
'networks.views.network', name='network'),
|
url(r'^compute/(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/interfaces/$',
|
|
||||||
'interfaces.views.interfaces', name='interfaces'),
|
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$',
|
|
||||||
'interfaces.views.interface', name='interface'),
|
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/secrets/$',
|
|
||||||
'secrets.views.secrets', name='secrets'),
|
|
||||||
url(r'^compute/(?P<compute_id>[0-9]+)/create/$',
|
|
||||||
'create.views.create_instance', name='create_instance'),
|
|
||||||
|
|
||||||
url(r'^console/$', 'console.views.console', name='console'),
|
url(r'^console/$', console, name='console'),
|
||||||
# (r'^admin/', include(admin.site.urls)),
|
# (r'^admin/', include(admin.site.urls)),
|
||||||
)
|
]
|
||||||
|
|
Loading…
Reference in a new issue