10
README.md
|
@ -335,13 +335,13 @@ python manage.py test
|
||||||
|
|
||||||
### Screenshots
|
### Screenshots
|
||||||
Instance Detail:
|
Instance Detail:
|
||||||
<img src="doc/images/instance.PNG" width="95%" align="center"/>
|
<img src="doc/images/instance.PNG" width="96%" align="center"/>
|
||||||
Instance List:</br>
|
Instance List:</br>
|
||||||
<img src="doc/images/grouped.PNG" width="47%"/>
|
<img src="doc/images/grouped.PNG" width="43%"/>
|
||||||
<img src="doc/images/nongrouped.PNG" width="51%"/>
|
<img src="doc/images/nongrouped.PNG" width="53%"/>
|
||||||
Other: </br>
|
Other: </br>
|
||||||
<img src="doc/images/hosts.PNG" width="52%"/>
|
<img src="doc/images/hosts.PNG" width="47%"/>
|
||||||
<img src="doc/images/log.PNG" width="47%"/>
|
<img src="doc/images/log.PNG" width="49%"/>
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<h3 class="page-header">{% trans "Edit Profile" %}</h3>
|
<h3 class="page-header">{% trans "Edit Profile" %}</h3>
|
||||||
{% if perms.accounts.change_password %}
|
{% if perms.accounts.change_password %}
|
||||||
<a href="{% url 'change_password' %}" class="btn btn-primary">{% icon 'lock' %} {% trans "Change Password" %}</a>
|
<a href="{% url 'change_password' %}" class="ml-3 btn btn-primary">{% icon 'lock' %} {% trans "Change Password" %}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<form method="post" action="" role="form" aria-label="Edit user info form">{% csrf_token %}
|
<form method="post" action="" role="form" aria-label="Edit user info form">{% csrf_token %}
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
|
from django.contrib.auth.forms import ReadOnlyPasswordHashField
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.utils.text import format_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from accounts.models import UserAttributes
|
from accounts.models import UserAttributes
|
||||||
|
@ -68,6 +71,16 @@ class UserForm(forms.ModelForm):
|
||||||
'is_superuser',
|
'is_superuser',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(UserForm, self).__init__(*args, **kwargs)
|
||||||
|
password = ReadOnlyPasswordHashField(label=_("Password"),
|
||||||
|
help_text=format_lazy(_("""Raw passwords are not stored, so there is no way to see
|
||||||
|
this user's password, but you can change the password
|
||||||
|
using <a href='{}'>this form</a>."""),
|
||||||
|
reverse_lazy('admin:user_update_password', args=[self.instance.id,]))
|
||||||
|
)
|
||||||
|
self.fields['Password'] = password
|
||||||
|
|
||||||
|
|
||||||
class UserCreateForm(UserForm):
|
class UserCreateForm(UserForm):
|
||||||
password = forms.CharField(widget=forms.PasswordInput)
|
password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django.contrib.auth.views import PasswordChangeView, PasswordChangeDoneView
|
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
|
@ -10,6 +9,7 @@ urlpatterns = [
|
||||||
path('groups/<int:pk>/delete/', views.group_delete, name='group_delete'),
|
path('groups/<int:pk>/delete/', views.group_delete, name='group_delete'),
|
||||||
path('users/', views.user_list, name='user_list'),
|
path('users/', views.user_list, name='user_list'),
|
||||||
path('users/create/', views.user_create, name='user_create'),
|
path('users/create/', views.user_create, name='user_create'),
|
||||||
|
path('users/<int:pk>/update_password/', views.user_update_password, name='user_update_password'),
|
||||||
path('users/<int:pk>/update/', views.user_update, name='user_update'),
|
path('users/<int:pk>/update/', views.user_update, name='user_update'),
|
||||||
path('users/<int:pk>/delete/', views.user_delete, name='user_delete'),
|
path('users/<int:pk>/delete/', views.user_delete, name='user_delete'),
|
||||||
path('users/<int:pk>/block/', views.user_block, name='user_block'),
|
path('users/<int:pk>/block/', views.user_block, name='user_block'),
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth import update_session_auth_hash
|
||||||
|
from django.contrib.auth.forms import AdminPasswordChangeForm
|
||||||
from django.contrib.auth.models import Group, User
|
from django.contrib.auth.models import Group, User
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
@ -133,6 +136,29 @@ def user_update(request, pk):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@superuser_only
|
||||||
|
def user_update_password(request, pk):
|
||||||
|
user = get_object_or_404(User, pk=pk)
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = AdminPasswordChangeForm(user, request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
user = form.save()
|
||||||
|
update_session_auth_hash(request, user) # Important!
|
||||||
|
messages.success(request, _('User password changed: {}'.format(user.username)))
|
||||||
|
return redirect('admin:user_list')
|
||||||
|
else:
|
||||||
|
messages.error(request, _('Wrong Data Provided'))
|
||||||
|
else:
|
||||||
|
form = AdminPasswordChangeForm(user)
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
'accounts/change_password_form.html',
|
||||||
|
{
|
||||||
|
'form': form,
|
||||||
|
'user': user.username
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
@superuser_only
|
@superuser_only
|
||||||
def user_delete(request, pk):
|
def user_delete(request, pk):
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
<!-- /.row -->
|
<!-- /.row -->
|
||||||
|
|
||||||
{% include 'errors_block.html' %}
|
{% include 'errors_block.html' %}
|
||||||
{% include 'messages_block.html' %}
|
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
|
|
|
@ -12,6 +12,7 @@ class TcpComputeForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Compute
|
model = Compute
|
||||||
|
widgets = {'password': forms.PasswordInput()}
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,6 +31,7 @@ class TlsComputeForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Compute
|
model = Compute
|
||||||
|
widgets = {'password': forms.PasswordInput()}
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,26 +25,26 @@
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr class="d-flex">
|
||||||
<th span="col">{% trans "Name" %}</th>
|
<th span="col" class="col-sm-3">{% trans "Name" %}</th>
|
||||||
<th span="col">{% trans "Status" %}</th>
|
<th span="col" class="col-sm-2">{% trans "Status" %}</th>
|
||||||
<th span="col">{% trans "Details" %}</th>
|
<th span="col" class="col-sm-5">{% trans "Details" %}</th>
|
||||||
<th span="col">{% trans "Actions" %}</th>
|
<th span="col" class="col-sm-2 text-center">{% trans "Actions" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="searchable">
|
<tbody class="searchable">
|
||||||
{% for compute in computes %}
|
{% for compute in computes %}
|
||||||
<tr>
|
<tr class="d-flex">
|
||||||
<td>
|
<td class="col-sm-3">
|
||||||
{{ compute.name }}
|
{{ compute.name }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="col-sm-2">
|
||||||
{% if compute.status is True %}{% trans "Connected" %}{% else %}{% trans "Not Connected" %}{% endif %}
|
{% if compute.status is True %}{% trans "Connected" %}{% else %}{% trans "Not Connected" %}{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="col-sm-5">
|
||||||
{{ compute.details|default:"" }}
|
{{ compute.details|default:"" }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="col-sm-2">
|
||||||
<div class="float-right btn-group">
|
<div class="float-right btn-group">
|
||||||
{% if compute.status is True %}
|
{% if compute.status is True %}
|
||||||
<a class="btn btn-success" title="{%trans "Overview" %}" href="{% url 'overview' compute.id %}">{% icon 'eye' %}</a>
|
<a class="btn btn-success" title="{%trans "Overview" %}" href="{% url 'overview' compute.id %}">{% icon 'eye' %}</a>
|
||||||
|
|
|
@ -160,6 +160,11 @@
|
||||||
SpiceHtml5.sendCtrlAltFN(sc, f);
|
SpiceHtml5.sendCtrlAltFN(sc, f);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendCtrlAltDel() {
|
||||||
|
SpiceHtml5.sendCtrlAltDel(sc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/* SPICE port event listeners
|
/* SPICE port event listeners
|
||||||
window.addEventListener('spice-port-data', function(event) {
|
window.addEventListener('spice-port-data', function(event) {
|
||||||
// Here we convert data to text, but really we can obtain binary data also
|
// Here we convert data to text, but really we can obtain binary data also
|
||||||
|
|
|
@ -186,6 +186,11 @@
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendCtrlAltDel() {
|
||||||
|
SpiceHtml5.sendCtrlAltDel(sc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* SPICE port event listeners
|
/* SPICE port event listeners
|
||||||
window.addEventListener('spice-port-data', function(event) {
|
window.addEventListener('spice-port-data', function(event) {
|
||||||
// Here we convert data to text, but really we can obtain binary data also
|
// Here we convert data to text, but really we can obtain binary data also
|
||||||
|
@ -198,7 +203,7 @@
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
|
document.getElementById("fullscreen_button").addEventListener('click', fullscreen);
|
||||||
document.getElementById('ctrlaltdel').addEventListener('click', function () { sendCtrlAltDel(sc); });
|
document.getElementById('ctrlaltdel').addEventListener('click', function () { sendCtrlAltDel(); });
|
||||||
document.getElementById('ctrlaltf1').addEventListener('click', function () { sendctrlaltfn(0) });
|
document.getElementById('ctrlaltf1').addEventListener('click', function () { sendctrlaltfn(0) });
|
||||||
document.getElementById('ctrlaltf2').addEventListener('click', function () { sendctrlaltfn(1) });
|
document.getElementById('ctrlaltf2').addEventListener('click', function () { sendctrlaltfn(1) });
|
||||||
document.getElementById('ctrlaltf3').addEventListener('click', function () { sendctrlaltfn(2) });
|
document.getElementById('ctrlaltf3').addEventListener('click', function () { sendctrlaltfn(2) });
|
||||||
|
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
|
@ -12,6 +12,7 @@
|
||||||
<!-- Tab panes -->
|
<!-- Tab panes -->
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
|
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="destroy">
|
||||||
|
<p>{% trans 'This action starts remove instance process' %}</p>
|
||||||
{% if request.user.is_superuser or userinstance.is_delete %}
|
{% if request.user.is_superuser or userinstance.is_delete %}
|
||||||
{% if instance.status == 3 %}
|
{% if instance.status == 3 %}
|
||||||
<a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
|
<a class="btn btn-lg btn-success disabled float-right">{% trans "Destroy" %}</a>
|
||||||
|
|
|
@ -353,6 +353,7 @@
|
||||||
<input name="set_link_state" value="{{ network.state }}" hidden/>
|
<input name="set_link_state" value="{{ network.state }}" hidden/>
|
||||||
<input type="checkbox" {% if network.state == 'up' %} checked{% endif %} onclick='submit();' />
|
<input type="checkbox" {% if network.state == 'up' %} checked{% endif %} onclick='submit();' />
|
||||||
<strong>{% trans 'active' %}</strong>
|
<strong>{% trans 'active' %}</strong>
|
||||||
|
<small>{{ network.type }}</small>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
<th class="d-none d-table-cell d-sm-table-cell">{% trans 'MAC' %}</th>
|
<th class="d-none d-table-cell d-sm-table-cell">{% trans 'MAC' %}</th>
|
||||||
|
@ -390,10 +391,10 @@
|
||||||
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
||||||
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
|
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
|
||||||
{% for c_net in networks_host %}
|
{% for c_net in networks_host %}
|
||||||
<option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
|
<option value="net:{{ c_net }}" {% if c_net == network.nic %} selected {% endif %}>{% trans 'Network' %} {{ c_net }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for c_iface in interfaces_host %}
|
{% for c_iface in interfaces_host %}
|
||||||
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
|
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %} selected {% endif %}>{% trans 'Interface' %} {{ c_iface }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -405,7 +406,7 @@
|
||||||
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
|
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
|
||||||
<option value="">{% trans "None" %}</option>
|
<option value="">{% trans "None" %}</option>
|
||||||
{% for c_filters in nwfilters_host %}
|
{% for c_filters in nwfilters_host %}
|
||||||
<option value="{{ c_filters }}" {% if c_filters == network.filterref %} selected {% endif %}>{{ c_filters }}</option>
|
<option value="{{ c_filters }}" {% if c_filters == network.filterref %} selected {% endif %}>{{ c_filters }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -462,7 +463,11 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="bg-primary" colspan="9"></td>
|
<td class="py-1 bg-primary text-white" colspan="9">
|
||||||
|
{% if network.type == 'direct' %}
|
||||||
|
<small>{% trans 'In most configurations, macvtap does not work for host to guest network communication' %}</small>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -965,8 +965,8 @@ def set_qos(request, pk):
|
||||||
else:
|
else:
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_(f"{qos_dir.capitalize()} QoS is set. Network XML is changed.") +
|
_(f"{qos_dir.capitalize()} QoS is set. Network XML is changed. \
|
||||||
_("Stop and start network to activate new config"),
|
Stop and start network to activate new config.")
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
||||||
|
@ -984,8 +984,8 @@ def unset_qos(request, pk):
|
||||||
else:
|
else:
|
||||||
messages.success(
|
messages.success(
|
||||||
request,
|
request,
|
||||||
_(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. ") +
|
_(f"{qos_dir.capitalize()} QoS is deleted. Network XML is changed. \
|
||||||
_("Stop and start network to activate new config."),
|
Stop and start network to activate new config.")
|
||||||
)
|
)
|
||||||
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
return redirect(request.META.get('HTTP_REFERER') + '#network')
|
||||||
|
|
||||||
|
@ -1409,7 +1409,7 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
console_pass=data["console_pass"],
|
console_pass=data["console_pass"],
|
||||||
mac=data['mac'],
|
mac=data['mac'],
|
||||||
qemu_ga=data['qemu_ga'])
|
qemu_ga=data['qemu_ga'])
|
||||||
create_instance = Instance(compute_id=compute_id, name=data['name'])
|
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||||
create_instance.save()
|
create_instance.save()
|
||||||
msg = _("Instance is created")
|
msg = _("Instance is created")
|
||||||
messages.success(request, msg)
|
messages.success(request, msg)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class AddNetPool(forms.Form):
|
||||||
|
|
||||||
def clean_bridge_name(self):
|
def clean_bridge_name(self):
|
||||||
bridge_name = self.cleaned_data['bridge_name']
|
bridge_name = self.cleaned_data['bridge_name']
|
||||||
if self.cleaned_data['forward'] == 'bridge':
|
if self.cleaned_data['forward'] in ['bridge', 'macvtap']:
|
||||||
have_symbol = re.match('^[a-zA-Z0-9\.\_\:\-]+$', bridge_name)
|
have_symbol = re.match('^[a-zA-Z0-9\.\_\:\-]+$', bridge_name)
|
||||||
if not have_symbol:
|
if not have_symbol:
|
||||||
raise forms.ValidationError(_('The pool bridge name must not contain any special characters'))
|
raise forms.ValidationError(_('The pool bridge name must not contain any special characters'))
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
<option value="route">{% trans "ROUTE" %}</option>
|
<option value="route">{% trans "ROUTE" %}</option>
|
||||||
<option value="none">{% trans "ISOLATE" %}</option>
|
<option value="none">{% trans "ISOLATE" %}</option>
|
||||||
<option value="bridge">{% trans "BRIDGE" %}</option>
|
<option value="bridge">{% trans "BRIDGE" %}</option>
|
||||||
|
<option value="macvtap">{% trans "MACVTAP" %}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,25 +56,25 @@
|
||||||
<input type="checkbox" id="enable_ipv6" name="enable_ipv6" value="false">
|
<input type="checkbox" id="enable_ipv6" name="enable_ipv6" value="false">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row bridge_name_form_group_dhcp ipv6_group">
|
<div class="form-group row ipv6_group">
|
||||||
<label class="col-sm-4 col-form-label">{% trans "IPv6 Subnet pool" %}</label>
|
<label class="col-sm-4 col-form-label">{% trans "IPv6 Subnet pool" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" class="form-control" name="subnet6" value="" placeholder="fd00:dead:baba:1::/64" required pattern="[0-9\/\.]+">
|
<input type="text" class="form-control" name="subnet6" value="" placeholder="fd00:dead:baba:1::/64" required pattern="[0-9\/\.]+">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row bridge_name_form_group_dhcp ipv6_group">
|
<div class="form-group row ipv6_group">
|
||||||
<label class="col-sm-4 col-form-label">{% trans "DHCPv6" %}</label>
|
<label class="col-sm-4 col-form-label">{% trans "DHCPv6" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="checkbox" name="dhcp6" value="true">
|
<input type="checkbox" name="dhcp6" value="true">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row bridge_name_form_group">
|
<div class="form-group row bridge_name_form_group">
|
||||||
<label class="col-sm-4 col-form-label">{% trans "Bridge Name" %}</label>
|
<label class="col-sm-4 col-form-label" id="bridge_label">{% trans "Bridge Name" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="text" class="form-control" name="bridge_name" placeholder="br0" pattern="[a-z0-9\-_:]+">
|
<input type="text" class="form-control" name="bridge_name" id="bridge_name" placeholder="br0" pattern="[a-z0-9\-_:]+">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row bridge_name_form_group">
|
<div class="form-group row bridge_name_form_group openvswitch">
|
||||||
<label class="col-sm-4 col-form-label">{% trans "Open vSwitch" %}</label>
|
<label class="col-sm-4 col-form-label">{% trans "Open vSwitch" %}</label>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="checkbox" name="openvswitch" value="true">
|
<input type="checkbox" name="openvswitch" value="true">
|
||||||
|
@ -88,4 +89,5 @@
|
||||||
</div> <!-- /.modal-content -->
|
</div> <!-- /.modal-content -->
|
||||||
</div> <!-- /.modal-dialog -->
|
</div> <!-- /.modal-dialog -->
|
||||||
</div> <!-- /.modal -->
|
</div> <!-- /.modal -->
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
<!-- /.row -->
|
<!-- /.row -->
|
||||||
|
|
||||||
{% include 'errors_block.html' %}
|
{% include 'errors_block.html' %}
|
||||||
{% include 'messages_block.html' %}
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<dl class="ml-3 row">
|
<dl class="ml-3 row">
|
||||||
|
|
|
@ -79,6 +79,12 @@
|
||||||
if ($(this).val() == 'bridge') {
|
if ($(this).val() == 'bridge') {
|
||||||
$('.bridge_name_form_group').show();
|
$('.bridge_name_form_group').show();
|
||||||
$('.bridge_name_form_group_dhcp').hide();
|
$('.bridge_name_form_group_dhcp').hide();
|
||||||
|
} else if ($(this).val() == 'macvtap') {
|
||||||
|
$('#bridge_label').text("Dev Name");
|
||||||
|
$('#bridge_name').attr("placeholder", "eth0");
|
||||||
|
$('.bridge_name_form_group').show();
|
||||||
|
$('.bridge_name_form_group_dhcp').hide();
|
||||||
|
$('.openvswitch').hide();
|
||||||
} else {
|
} else {
|
||||||
$('.bridge_name_form_group').hide();
|
$('.bridge_name_form_group').hide();
|
||||||
$('.bridge_name_form_group_dhcp').show();
|
$('.bridge_name_form_group_dhcp').show();
|
||||||
|
|
|
@ -42,8 +42,8 @@ def networks(request, compute_id):
|
||||||
if data['name'] in networks:
|
if data['name'] in networks:
|
||||||
msg = _("Network pool name already in use")
|
msg = _("Network pool name already in use")
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
if data['forward'] == 'bridge' and data['bridge_name'] == '':
|
if data['forward'] in ['bridge', 'macvtap'] and data['bridge_name'] == '':
|
||||||
error_messages.append(_('Please enter bridge name'))
|
error_messages.append(_('Please enter bridge/dev name'))
|
||||||
if data['subnet']:
|
if data['subnet']:
|
||||||
ipv4 = True
|
ipv4 = True
|
||||||
gateway4, netmask4, dhcp4 = network_size(data['subnet'], data['dhcp4'])
|
gateway4, netmask4, dhcp4 = network_size(data['subnet'], data['dhcp4'])
|
||||||
|
|
|
@ -841,7 +841,7 @@ class wvmConnect(object):
|
||||||
|
|
||||||
def get_info(doc):
|
def get_info(doc):
|
||||||
mem = util.get_xpath(doc, "/domain/currentMemory")
|
mem = util.get_xpath(doc, "/domain/currentMemory")
|
||||||
mem = int(mem) / 1024
|
mem = int(mem) // 1024
|
||||||
if raw_mem_size:
|
if raw_mem_size:
|
||||||
mem = int(mem) * (1024 * 1024)
|
mem = int(mem) * (1024 * 1024)
|
||||||
cur_vcpu = util.get_xpath(doc, "/domain/vcpu/@current")
|
cur_vcpu = util.get_xpath(doc, "/domain/vcpu/@current")
|
||||||
|
@ -875,7 +875,7 @@ class wvmConnect(object):
|
||||||
|
|
||||||
def get_info(ctx):
|
def get_info(ctx):
|
||||||
mem = util.get_xpath(ctx, "/domain/currentMemory")
|
mem = util.get_xpath(ctx, "/domain/currentMemory")
|
||||||
mem = int(mem) / 1024
|
mem = int(mem) // 1024
|
||||||
cur_vcpu = util.get_xpath(ctx, "/domain/vcpu/@current")
|
cur_vcpu = util.get_xpath(ctx, "/domain/vcpu/@current")
|
||||||
if cur_vcpu:
|
if cur_vcpu:
|
||||||
vcpu = cur_vcpu
|
vcpu = cur_vcpu
|
||||||
|
|
|
@ -32,7 +32,7 @@ class wvmInstances(wvmConnect):
|
||||||
def get_instance_memory(self, name):
|
def get_instance_memory(self, name):
|
||||||
inst = self.get_instance(name)
|
inst = self.get_instance(name)
|
||||||
mem = util.get_xml_path(inst.XMLDesc(0), "/domain/currentMemory")
|
mem = util.get_xml_path(inst.XMLDesc(0), "/domain/currentMemory")
|
||||||
return int(mem) / 1024
|
return int(mem) // 1024
|
||||||
|
|
||||||
def get_instance_vcpu(self, name):
|
def get_instance_vcpu(self, name):
|
||||||
inst = self.get_instance(name)
|
inst = self.get_instance(name)
|
||||||
|
@ -242,11 +242,11 @@ class wvmInstance(wvmConnect):
|
||||||
|
|
||||||
def get_memory(self):
|
def get_memory(self):
|
||||||
mem = util.get_xml_path(self._XMLDesc(0), "/domain/memory")
|
mem = util.get_xml_path(self._XMLDesc(0), "/domain/memory")
|
||||||
return int(mem) / 1024
|
return int(mem) // 1024
|
||||||
|
|
||||||
def get_cur_memory(self):
|
def get_cur_memory(self):
|
||||||
mem = util.get_xml_path(self._XMLDesc(0), "/domain/currentMemory")
|
mem = util.get_xml_path(self._XMLDesc(0), "/domain/currentMemory")
|
||||||
return int(mem) / 1024
|
return int(mem) // 1024
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
|
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
|
||||||
|
@ -345,6 +345,7 @@ class wvmInstance(wvmConnect):
|
||||||
result = []
|
result = []
|
||||||
inbound = outbound = []
|
inbound = outbound = []
|
||||||
for net in ctx.xpath('/domain/devices/interface'):
|
for net in ctx.xpath('/domain/devices/interface'):
|
||||||
|
interface_type = net.xpath('@type')[0]
|
||||||
mac_inst = net.xpath('mac/@address')[0]
|
mac_inst = net.xpath('mac/@address')[0]
|
||||||
nic_inst = net.xpath('source/@network|source/@bridge|source/@dev')[0]
|
nic_inst = net.xpath('source/@network|source/@bridge|source/@dev')[0]
|
||||||
target_inst = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0]
|
target_inst = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0]
|
||||||
|
@ -369,6 +370,7 @@ class wvmInstance(wvmConnect):
|
||||||
except libvirtError:
|
except libvirtError:
|
||||||
ipv4, ipv6 = None, None
|
ipv4, ipv6 = None, None
|
||||||
result.append({
|
result.append({
|
||||||
|
'type': interface_type,
|
||||||
'mac': mac_inst,
|
'mac': mac_inst,
|
||||||
'nic': nic_inst,
|
'nic': nic_inst,
|
||||||
'target': target_inst,
|
'target': target_inst,
|
||||||
|
@ -993,7 +995,7 @@ class wvmInstance(wvmConnect):
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
# Little fix for old version ElementTree
|
# Little fix for old version ElementTree
|
||||||
graphic = root.find("devices/graphics")
|
graphic = root.find("devices/graphics")
|
||||||
if keymap:
|
if keymap != 'auto':
|
||||||
graphic.set('keymap', keymap)
|
graphic.set('keymap', keymap)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -1289,24 +1291,39 @@ class wvmInstance(wvmConnect):
|
||||||
bridge_name = iface.name()
|
bridge_name = iface.name()
|
||||||
else:
|
else:
|
||||||
net = self.get_network(source)
|
net = self.get_network(source)
|
||||||
bridge_name = net.bridgeName()
|
try:
|
||||||
|
bridge_name = net.bridgeName()
|
||||||
|
except libvirtError:
|
||||||
|
bridge_name = None
|
||||||
return bridge_name
|
return bridge_name
|
||||||
|
|
||||||
def add_network(self, mac_address, source, source_type='net', model='virtio', nwfilter=None):
|
def add_network(self, mac_address, source, source_type='net', model='virtio', nwfilter=None):
|
||||||
bridge_name = self.get_bridge_name(source, source_type)
|
forward_mode = ''
|
||||||
|
if source_type != 'iface':
|
||||||
|
forward_mode = self.get_network_forward(source)
|
||||||
|
|
||||||
forward_mode = self.get_network_forward(source)
|
|
||||||
if forward_mode in ['nat', 'isolated', 'routed']:
|
if forward_mode in ['nat', 'isolated', 'routed']:
|
||||||
interface_type = 'network'
|
interface_type = 'network'
|
||||||
|
elif forward_mode == '':
|
||||||
|
interface_type = 'direct'
|
||||||
else:
|
else:
|
||||||
interface_type = 'bridge'
|
if self.get_bridge_name(source, source_type) is None:
|
||||||
|
interface_type = 'network'
|
||||||
|
else:
|
||||||
|
interface_type = 'bridge'
|
||||||
|
|
||||||
xml_iface = f"""
|
xml_iface = f"""
|
||||||
<interface type='{interface_type}'>
|
<interface type='{interface_type}'>
|
||||||
<mac address='{mac_address}'/>"""
|
<mac address='{mac_address}'/>"""
|
||||||
if interface_type == 'network':
|
if interface_type == 'network':
|
||||||
xml_iface += f"""<source network='{source}'/>"""
|
xml_iface += f"""<source network='{source}'/>"""
|
||||||
|
elif interface_type == 'direct':
|
||||||
|
if source_type == 'net':
|
||||||
|
xml_iface += f"""<source network='{source}' mode='bridge'/>"""
|
||||||
|
else:
|
||||||
|
xml_iface += f"""<source dev='{source}' mode='bridge'/>"""
|
||||||
else:
|
else:
|
||||||
|
bridge_name = self.get_bridge_name(source, source_type)
|
||||||
xml_iface += f"""<source bridge='{bridge_name}'/>"""
|
xml_iface += f"""<source bridge='{bridge_name}'/>"""
|
||||||
xml_iface += f"""<model type='{model}'/>"""
|
xml_iface += f"""<model type='{model}'/>"""
|
||||||
if nwfilter:
|
if nwfilter:
|
||||||
|
@ -1342,49 +1359,38 @@ class wvmInstance(wvmConnect):
|
||||||
net_source_type = network_data.get('net-source-' + str(num) + '-type')
|
net_source_type = network_data.get('net-source-' + str(num) + '-type')
|
||||||
net_filter = network_data.get('net-nwfilter-' + str(num))
|
net_filter = network_data.get('net-nwfilter-' + str(num))
|
||||||
net_model = network_data.get('net-model-' + str(num))
|
net_model = network_data.get('net-model-' + str(num))
|
||||||
bridge_name = self.get_bridge_name(net_source, net_source_type)
|
|
||||||
|
source = interface.find('source')
|
||||||
if interface.get('type') == 'bridge':
|
if interface.get('type') == 'bridge':
|
||||||
source = interface.find('mac')
|
bridge_name = self.get_bridge_name(net_source, net_source_type)
|
||||||
source.set('address', net_mac)
|
|
||||||
source = interface.find('source')
|
|
||||||
source.set('bridge', bridge_name)
|
source.set('bridge', bridge_name)
|
||||||
|
elif interface.get('type') in ['network', 'direct']:
|
||||||
source = interface.find('model')
|
if net_source_type == 'net':
|
||||||
if net_model != 'default':
|
source.set('network', net_source)
|
||||||
source.attrib['type'] = net_model
|
elif net_source_type == 'iface':
|
||||||
|
source.set('dev', net_source)
|
||||||
else:
|
else:
|
||||||
interface.remove(source)
|
raise libvirtError(f"Unknown network type: {net_source_type}")
|
||||||
|
else:
|
||||||
|
raise libvirtError(f"Unknown network type: {interface.get('type')}")
|
||||||
|
|
||||||
source = interface.find('filterref')
|
source = interface.find('model')
|
||||||
if net_filter:
|
if net_model != 'default':
|
||||||
if source is not None: source.set('filter', net_filter)
|
source.attrib['type'] = net_model
|
||||||
else:
|
else:
|
||||||
element = ElementTree.Element("filterref")
|
interface.remove(source)
|
||||||
element.attrib['filter'] = net_filter
|
|
||||||
interface.append(element)
|
|
||||||
else:
|
|
||||||
if source is not None: interface.remove(source)
|
|
||||||
elif interface.get('type') == 'network':
|
|
||||||
source = interface.find('mac')
|
|
||||||
source.set('address', net_mac)
|
|
||||||
source = interface.find('source')
|
|
||||||
source.set('network', net_source)
|
|
||||||
|
|
||||||
source = interface.find('model')
|
source = interface.find('mac')
|
||||||
if net_model != 'default':
|
source.set('address', net_mac)
|
||||||
source.attrib['type'] = net_model
|
source = interface.find('filterref')
|
||||||
|
if net_filter:
|
||||||
|
if source is not None: source.set('filter', net_filter)
|
||||||
else:
|
else:
|
||||||
interface.remove(source)
|
element = ElementTree.Element("filterref")
|
||||||
|
element.attrib['filter'] = net_filter
|
||||||
source = interface.find('filterref')
|
interface.append(element)
|
||||||
if net_filter:
|
else:
|
||||||
if source is not None: source.set('filter', net_filter)
|
if source is not None: interface.remove(source)
|
||||||
else:
|
|
||||||
element = ElementTree.Element("filterref")
|
|
||||||
element.attrib['filter'] = net_filter
|
|
||||||
interface.append(element)
|
|
||||||
else:
|
|
||||||
if source is not None: interface.remove(source)
|
|
||||||
|
|
||||||
new_xml = ElementTree.tostring(tree).decode()
|
new_xml = ElementTree.tostring(tree).decode()
|
||||||
self._defineXML(new_xml)
|
self._defineXML(new_xml)
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
from libvirt import libvirtError
|
||||||
from libvirt import VIR_NETWORK_SECTION_IP_DHCP_HOST
|
from libvirt import VIR_NETWORK_SECTION_IP_DHCP_HOST
|
||||||
from libvirt import VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_UPDATE_COMMAND_MODIFY
|
from libvirt import VIR_NETWORK_UPDATE_COMMAND_ADD_LAST, VIR_NETWORK_UPDATE_COMMAND_DELETE, VIR_NETWORK_UPDATE_COMMAND_MODIFY
|
||||||
from libvirt import VIR_NETWORK_UPDATE_AFFECT_LIVE, VIR_NETWORK_UPDATE_AFFECT_CONFIG
|
from libvirt import VIR_NETWORK_UPDATE_AFFECT_LIVE, VIR_NETWORK_UPDATE_AFFECT_CONFIG
|
||||||
|
@ -32,7 +33,11 @@ class wvmNetworks(wvmConnect):
|
||||||
for network in get_networks:
|
for network in get_networks:
|
||||||
net = self.get_network(network)
|
net = self.get_network(network)
|
||||||
net_status = net.isActive()
|
net_status = net.isActive()
|
||||||
net_bridge = net.bridgeName()
|
try:
|
||||||
|
net_bridge = net.bridgeName()
|
||||||
|
except libvirtError:
|
||||||
|
net_bridge = util.get_xml_path(net.XMLDesc(0), "/network/forward/interface/@dev")
|
||||||
|
|
||||||
net_forward = util.get_xml_path(net.XMLDesc(0), "/network/forward/@mode")
|
net_forward = util.get_xml_path(net.XMLDesc(0), "/network/forward/@mode")
|
||||||
networks.append({'name': network, 'status': net_status,
|
networks.append({'name': network, 'status': net_status,
|
||||||
'device': net_bridge, 'forward': net_forward})
|
'device': net_bridge, 'forward': net_forward})
|
||||||
|
@ -50,15 +55,20 @@ class wvmNetworks(wvmConnect):
|
||||||
<name>{name}</name>"""
|
<name>{name}</name>"""
|
||||||
if forward in ['nat', 'route', 'bridge']:
|
if forward in ['nat', 'route', 'bridge']:
|
||||||
xml += f"""<forward mode='{forward}'/>"""
|
xml += f"""<forward mode='{forward}'/>"""
|
||||||
xml += """<bridge """
|
if forward == 'macvtap':
|
||||||
if forward in ['nat', 'route', 'none']:
|
xml += f"""<forward mode='bridge'>
|
||||||
xml += """stp='on' delay='0'"""
|
<interface dev='{bridge}'/>
|
||||||
if forward == 'bridge':
|
</forward>"""
|
||||||
xml += f"""name='{bridge}'"""
|
else:
|
||||||
xml += """/>"""
|
xml += """<bridge """
|
||||||
if openvswitch is True:
|
if forward in ['nat', 'route', 'none']:
|
||||||
xml += """<virtualport type='openvswitch'/>"""
|
xml += """stp='on' delay='0'"""
|
||||||
if forward != 'bridge':
|
if forward == 'bridge':
|
||||||
|
xml += f"""name='{bridge}'"""
|
||||||
|
xml += """/>"""
|
||||||
|
if openvswitch is True:
|
||||||
|
xml += """<virtualport type='openvswitch'/>"""
|
||||||
|
if forward not in ['bridge', 'macvtap']:
|
||||||
if ipv4:
|
if ipv4:
|
||||||
xml += f"""<ip address='{gateway}' netmask='{mask}'>"""
|
xml += f"""<ip address='{gateway}' netmask='{mask}'>"""
|
||||||
if dhcp4:
|
if dhcp4:
|
||||||
|
@ -114,7 +124,7 @@ class wvmNetwork(wvmConnect):
|
||||||
try:
|
try:
|
||||||
return self.net.bridgeName()
|
return self.net.bridgeName()
|
||||||
except:
|
except:
|
||||||
return None
|
return util.get_xml_path(self._XMLDesc(0), "/network/forward/interface/@dev")
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
self.net.create()
|
self.net.create()
|
||||||
|
|
|
@ -19,8 +19,11 @@ urlpatterns = [
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
import debug_toolbar
|
try:
|
||||||
|
import debug_toolbar
|
||||||
|
|
||||||
urlpatterns += [
|
urlpatterns += [
|
||||||
path('__debug__/', include(debug_toolbar.urls)),
|
path('__debug__/', include(debug_toolbar.urls)),
|
||||||
]
|
]
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|