mirror of
https://github.com/retspen/webvirtcloud
synced 2026-03-23 11:04:49 +00:00
Merge 4da783da32 into 5c2232f4e8
This commit is contained in:
commit
f072c2daa2
39 changed files with 1312 additions and 92 deletions
|
|
@ -11,6 +11,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> {% trans "Overview" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,22 @@
|
|||
from django.conf.urls import url
|
||||
from . import views
|
||||
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 computes.views import overview, compute_graph
|
||||
from instances.views import instances
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.computes, name='computes'),
|
||||
url(r'^overview/(?P<compute_id>[0-9]+)/$', views.overview, name='overview'),
|
||||
url(r'^statistics/(?P<compute_id>[0-9]+)/$',
|
||||
views.compute_graph, name='compute_graph'),
|
||||
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='compute_instances'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
|
||||
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]+)/secrets/$', secrets, name='secrets'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -134,6 +134,28 @@ def computes(request):
|
|||
error_messages.append(msg_err.as_text())
|
||||
return render(request, 'computes.html', locals())
|
||||
|
||||
@login_required
|
||||
def compute_instances(request, compute_id):
|
||||
"""
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
if not request.user.is_superuser:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
error_messages = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
try:
|
||||
conn = wvmHostDetails(compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
|
||||
hypervisor = conn.hypervisor_type()
|
||||
mem_usage = conn.get_memory_usage()
|
||||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
return render(request, 'compute_instances.html', locals())
|
||||
|
||||
@login_required
|
||||
def overview(request, compute_id):
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@
|
|||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_host">Host:</label>
|
||||
<input id="noVNC_setting_host" value="{{ ws_host }}"/>
|
||||
<input id="noVNC_setting_host" value="{{ ws_host }}{{ ws_path }}"/>
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_port">Port:</label>
|
||||
|
|
@ -332,4 +332,4 @@
|
|||
<source src="{% static "js/novnc/app/sounds/bell.oga" %}" type="audio/ogg">
|
||||
<source src="{% static "js/novnc/app/sounds/bell.mp3" %}" type="audio/mpeg">
|
||||
</audio>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@
|
|||
//var port = WebUtil.getConfigVar('port', window.location.port);
|
||||
var host = '{{ ws_host }}';
|
||||
var port = '{{ ws_port }}';
|
||||
var wspath = '{{ ws_path }}';
|
||||
|
||||
// if port == 80 (or 443) then it won't be present and should be
|
||||
// set manually
|
||||
|
|
@ -263,7 +264,7 @@
|
|||
url = 'ws';
|
||||
}
|
||||
|
||||
url += '://' + host;
|
||||
url += '://' + host + wspath;
|
||||
if(port) {
|
||||
url += ':' + port;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from instances.models import Instance
|
|||
from vrtManager.instance import wvmInstance
|
||||
from webvirtcloud.settings import WS_PORT
|
||||
from webvirtcloud.settings import WS_PUBLIC_HOST
|
||||
from webvirtcloud.settings import WS_PUBLIC_PORT
|
||||
from webvirtcloud.settings import WS_PUBLIC_PATH
|
||||
from libvirt import libvirtError
|
||||
|
||||
|
||||
|
|
@ -40,8 +42,9 @@ def console(request):
|
|||
console_websocket_port = None
|
||||
console_passwd = None
|
||||
|
||||
ws_port = console_websocket_port if console_websocket_port else WS_PORT
|
||||
ws_port = console_websocket_port if console_websocket_port else WS_PUBLIC_PORT
|
||||
ws_host = WS_PUBLIC_HOST if WS_PUBLIC_HOST else request.get_host()
|
||||
ws_path = WS_PUBLIC_PATH if WS_PUBLIC_PATH else '/'
|
||||
|
||||
if ':' in ws_host:
|
||||
ws_host = re.sub(':[0-9]+', '', ws_host)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ class NewVMForm(forms.Form):
|
|||
disk = forms.IntegerField(required=False)
|
||||
memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
||||
networks = forms.CharField(error_messages={'required': _('No Network pool has been choice')})
|
||||
nwfilter = forms.CharField(required=False)
|
||||
storage = forms.CharField(max_length=20, required=False)
|
||||
template = forms.CharField(required=False)
|
||||
images = forms.CharField(required=False)
|
||||
|
|
|
|||
|
|
@ -107,6 +107,17 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="nwfilter" class="form-control">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
|
||||
|
|
@ -226,6 +237,17 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="nwfilter" class="form-control">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
|
||||
<div class="col-sm-6">
|
||||
|
|
@ -379,6 +401,17 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="nwfilter" class="form-control">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "MAC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ def create_instance(request, compute_id):
|
|||
storages = []
|
||||
networks = []
|
||||
meta_prealloc = False
|
||||
#computes = Compute.objects.all()
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
flavors = Flavor.objects.filter().order_by('id')
|
||||
|
||||
|
|
@ -40,6 +39,7 @@ def create_instance(request, compute_id):
|
|||
|
||||
storages = sorted(conn.get_storages(only_actives=True))
|
||||
networks = sorted(conn.get_networks())
|
||||
nwfilters = conn.get_nwfilters()
|
||||
instances = conn.get_instances()
|
||||
videos = conn.get_video()
|
||||
cache_modes = sorted(conn.get_cache_modes().items())
|
||||
|
|
@ -139,11 +139,11 @@ def create_instance(request, compute_id):
|
|||
try:
|
||||
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
|
||||
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'],
|
||||
data["listener_addr"], None, data["video"], data["console_pass"],
|
||||
data["listener_addr"], data["nwfilter"], data["video"], data["console_pass"],
|
||||
data['mac'])
|
||||
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||
create_instance.save()
|
||||
messages.success(request,"Instance is created.")
|
||||
messages.success(request, _("Instance is created."))
|
||||
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
|
||||
except libvirtError as lib_err:
|
||||
if data['hdd_size'] or volumes[clone_path]:
|
||||
|
|
|
|||
|
|
@ -33,12 +33,24 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-control" name="add-net-nwfilter">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in compute_nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_network">{% trans "Add" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
|
|
|
|||
199
instances/templates/compute_instances.html
Normal file
199
instances/templates/compute_instances.html
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% if request.user.is_superuser %}
|
||||
{% include 'create_inst_block.html' %}
|
||||
{% endif %}
|
||||
{% if all_host_vms or all_user_vms %}
|
||||
<div class="pull-right search">
|
||||
<input id="filter" class="form-control" type="text" placeholder="Search">
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1 class="page-header">{{ compute.name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<ol class="breadcrumb">
|
||||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-server"></i> {% trans "Instances" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
{% include 'errors_block.html' %}
|
||||
<div class="row">
|
||||
{% if not all_host_vms %}
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-warning alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-lg-12">
|
||||
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name<br>Description</th>
|
||||
<th>Host<br>User</th>
|
||||
<th>Status</th>
|
||||
<th>VCPU</th>
|
||||
<th>Memory</th>
|
||||
<th data-sortable="false" style="width:205px;">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
{% for host, inst in all_host_vms.items %}
|
||||
{% for vm, info in inst.items %}
|
||||
<tr>
|
||||
<td><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>
|
||||
{% ifequal info.status 1 %}<span class="text-success">{% trans "Active" %}</span>{% endifequal %}
|
||||
{% ifequal info.status 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endifequal %}
|
||||
{% ifequal info.status 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endifequal %}
|
||||
</td>
|
||||
<td>{{ info.vcpu }}</td>
|
||||
<td>{{ info.memory|filesizeformat }}</td>
|
||||
<td><form action="" method="post" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="name" value="{{ vm }}"/>
|
||||
<input type="hidden" name="compute_id" value="{{ host.0 }}"/>
|
||||
{% ifequal info.status 5 %}
|
||||
{% if info.is_template %}
|
||||
<button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ host.0 }}, '{{ vm }}');">
|
||||
<span class="glyphicon glyphicon-duplicate"></span>
|
||||
</button>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-default" type="submit" name="poweron" title="{% trans "Power On" %}">
|
||||
<span class="glyphicon glyphicon-play"></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
|
||||
<span class="glyphicon glyphicon-pause"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
||||
<span class="glyphicon glyphicon-off"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
||||
<span class="glyphicon glyphicon-refresh"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</button>
|
||||
{% endifequal %}
|
||||
{% ifequal info.status 3 %}
|
||||
<button class="btn btn-sm btn-default" type="submit" name="resume" title="{% trans "Resume" %}">
|
||||
<span class="glyphicon glyphicon-play"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
|
||||
<span class="glyphicon glyphicon-pause"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
||||
<span class="glyphicon glyphicon-off"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
||||
<span class="glyphicon glyphicon-refresh"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</button>
|
||||
{% endifequal %}
|
||||
{% ifequal info.status 1 %}
|
||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
|
||||
<span class="glyphicon glyphicon-play"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default" type="submit" name="suspend" title="{% trans "Suspend" %}">
|
||||
<span class="glyphicon glyphicon-pause"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default" type="submit" name="poweroff" title="{% trans "Power Off" %}" onclick="return confirm('Are you sure?')">
|
||||
<span class="glyphicon glyphicon-off"></span>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-default" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')">
|
||||
<span class="glyphicon glyphicon-refresh"></span>
|
||||
</button>
|
||||
<a href="#" class="btn btn-sm btn-default" onclick='open_console("{{ host.0 }}-{{ info.uuid }}")' title="{% trans "Console" %}">
|
||||
<span class="glyphicon glyphicon-eye-open"></span>
|
||||
</a>
|
||||
{% endifequal %}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
<script>
|
||||
function open_console(uuid) {
|
||||
window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=485");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function filter_table() {
|
||||
var rex = new RegExp($(this).val(), 'i');
|
||||
$('.searchable tr').hide();
|
||||
$('.searchable tr').filter(function () {
|
||||
return rex.test($(this).text());
|
||||
}).show();
|
||||
Cookies.set("instances_filter", $(this).val(), { expires: 1 });
|
||||
}
|
||||
$(document).ready(function () {
|
||||
instances_filter_cookie = Cookies.get("instances_filter");
|
||||
if (instances_filter_cookie) {
|
||||
$('#filter').val(instances_filter_cookie);
|
||||
$('#filter').each(filter_table);
|
||||
}
|
||||
(function ($) {
|
||||
$('#filter').keyup(filter_table)
|
||||
}(jQuery));
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function goto_instance_clone(compute, instance) {
|
||||
window.location = "/instance/" + compute + "/" + instance + "/#clone";
|
||||
}
|
||||
</script>
|
||||
{% if request.user.is_superuser %}
|
||||
<script>
|
||||
function goto_compute() {
|
||||
var compute = $("#compute_select").val();
|
||||
window.location = "/compute/" + compute + "/create/";
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -833,39 +833,63 @@
|
|||
{% if request.user.is_superuser %}
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
|
||||
<p>
|
||||
{% trans "Assign network device to bridge" %}
|
||||
{% include 'add_instance_network_block.html' %}
|
||||
{% trans "Assign network device to bridge" %}
|
||||
{% include 'add_instance_network_block.html' %}
|
||||
</p>
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
||||
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
<form method="post" role="form">{% csrf_token %}
|
||||
{% for network in networks %}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" style="font-weight:normal;">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" name="net-source-{{ forloop.counter0 }}" value="{{ network.nic }}" disabled/>
|
||||
<div class="panel-body">
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
||||
</div>
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
|
||||
{% for c_net in compute_networks %}
|
||||
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
|
||||
{% endfor %}
|
||||
{% for c_iface in compute_interfaces %}
|
||||
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for c_filters in compute_nwfilters %}
|
||||
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<select name="net-source-{{ forloop.counter0 }}" class="form-control" id="network_select" onchange="network_select_enable()">
|
||||
{% for c_nets in compute_networks %}
|
||||
<option value="net:{{ c_nets }}" {% if c_nets == network.nic %}selected{% endif %}>Network {{ c_nets }}</option>
|
||||
{% endfor %}
|
||||
{% for c_iface in compute_interfaces %}
|
||||
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %}selected{% endif %}>Interface {{ c_iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
|
||||
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% ifequal status 5 %}
|
||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
||||
{% endifequal %}
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
|
||||
|
|
@ -1370,12 +1394,6 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function network_select_enable(){
|
||||
// set network button enabled
|
||||
$('button[name="change_network"]').removeAttr('disabled');
|
||||
}
|
||||
</script>
|
||||
<script src="{% static "js/Chart.min.js" %}"></script>
|
||||
<script>
|
||||
$('#chartgraphs').on('shown.bs.tab', function (event) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
{% for host, inst in all_host_vms.items %}
|
||||
<tr class="active" style="font-weight: bold;border-bottom: 2px solid darkgray;border-top: 2px solid darkgray;">
|
||||
<td>
|
||||
<span style="text-align:center;">{{ inst.items|length }}</span>
|
||||
<span id="collapse_host_instances_{{ host.1 }}" class="glyphicon glyphicon-chevron-up" onclick="hide_host_instances('{{ host.1 }}');"></span>
|
||||
</td>
|
||||
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a></td>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ def index(request):
|
|||
|
||||
|
||||
@login_required
|
||||
def instances(request):
|
||||
def instances(request, compute_id=None):
|
||||
"""
|
||||
:param request:
|
||||
:return:
|
||||
|
|
@ -47,7 +47,11 @@ def instances(request):
|
|||
error_messages = []
|
||||
all_host_vms = {}
|
||||
all_user_vms = {}
|
||||
computes = Compute.objects.all().order_by("name")
|
||||
|
||||
if not compute_id:
|
||||
computes = Compute.objects.all().order_by("name")
|
||||
else:
|
||||
computes = [Compute.objects.get(id=compute_id),]
|
||||
|
||||
def get_userinstances_info(instance):
|
||||
info = {}
|
||||
|
|
@ -195,8 +199,11 @@ def instances(request):
|
|||
|
||||
view_style = settings.VIEW_INSTANCES_LIST_STYLE
|
||||
|
||||
return render(request, 'instances.html', locals())
|
||||
if compute_id:
|
||||
compute = computes[0]
|
||||
return render(request, 'compute_instances.html', locals())
|
||||
|
||||
return render(request, 'instances.html', locals())
|
||||
|
||||
@login_required
|
||||
def instance(request, compute_id, vname):
|
||||
|
|
@ -348,6 +355,7 @@ def instance(request, compute_id, vname):
|
|||
vname)
|
||||
compute_networks = sorted(conn.get_networks())
|
||||
compute_interfaces = sorted(conn.get_ifaces())
|
||||
compute_nwfilters = conn.get_nwfilters()
|
||||
status = conn.get_status()
|
||||
autostart = conn.get_autostart()
|
||||
vcpu = conn.get_vcpu()
|
||||
|
|
@ -364,6 +372,7 @@ def instance(request, compute_id, vname):
|
|||
else:
|
||||
media_iso = []
|
||||
networks = conn.get_net_device()
|
||||
|
||||
vcpu_range = conn.get_max_cpus()
|
||||
memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384]
|
||||
if memory not in memory_range:
|
||||
|
|
@ -387,6 +396,7 @@ def instance(request, compute_id, vname):
|
|||
cache_modes = sorted(conn.get_cache_modes().items())
|
||||
default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE
|
||||
default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
||||
default_owner = settings.INSTANCE_VOLUME_DEFAULT_OWNER
|
||||
formats = conn.get_image_formats()
|
||||
|
||||
busses = conn.get_busses()
|
||||
|
|
@ -544,14 +554,14 @@ def instance(request, compute_id, vname):
|
|||
compute.type)
|
||||
storage = request.POST.get('storage', '')
|
||||
name = request.POST.get('name', '')
|
||||
format = request.POST.get('format', '')
|
||||
format = request.POST.get('format', default_format)
|
||||
size = request.POST.get('size', 0)
|
||||
meta_prealloc = request.POST.get('meta_prealloc', False)
|
||||
bus = request.POST.get('bus', '')
|
||||
cache = request.POST.get('cache', '')
|
||||
bus = request.POST.get('bus', default_bus)
|
||||
cache = request.POST.get('cache', default_cache)
|
||||
target = get_new_disk_dev(disks, bus)
|
||||
|
||||
path = connCreate.create_volume(storage, name, size, format, meta_prealloc)
|
||||
path = connCreate.create_volume(storage, name, size, format, meta_prealloc, default_owner)
|
||||
conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus)
|
||||
msg = _('Attach new disk')
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
|
|
@ -689,6 +699,7 @@ def instance(request, compute_id, vname):
|
|||
return HttpResponseRedirect(reverse('instance', args=[new_compute.id, vname]))
|
||||
|
||||
if 'change_network' in request.POST:
|
||||
msg = _("Change network")
|
||||
network_data = {}
|
||||
|
||||
for post in request.POST:
|
||||
|
|
@ -700,23 +711,31 @@ def instance(request, compute_id, vname):
|
|||
network_data[post] = request.POST.get(post, '')
|
||||
|
||||
conn.change_network(network_data)
|
||||
msg = _("Edit network")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
msg = _("Network Devices are changed. Please reboot instance to activate.")
|
||||
messages.success(request, msg)
|
||||
if conn.get_status() != 5: messages.success(request, _("Network Devices are changed. Please reboot instance to activate."))
|
||||
return HttpResponseRedirect(request.get_full_path() + '#network')
|
||||
|
||||
if 'add_network' in request.POST:
|
||||
msg = _("Add network")
|
||||
mac = request.POST.get('add-net-mac')
|
||||
nwfilter = request.POST.get('add-net-nwfilter')
|
||||
(source, source_type) = get_network_tuple(request.POST.get('add-net-network'))
|
||||
|
||||
conn.add_network(mac, source, source_type)
|
||||
msg = _("Edit network")
|
||||
conn.add_network(mac, source, source_type, nwfilter=nwfilter)
|
||||
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
msg = _("Network Devices are changed. Please reboot instance to activate.")
|
||||
messages.success(request, msg)
|
||||
if conn.get_status() != 5: messages.success(request, _("Network Device is added. Please reboot instance to activate."))
|
||||
return HttpResponseRedirect(request.get_full_path() + '#network')
|
||||
|
||||
|
||||
if 'delete_network' in request.POST:
|
||||
msg = _("Delete network")
|
||||
mac_address = request.POST.get('delete_network', '')
|
||||
|
||||
conn.delete_network(mac_address)
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
if conn.get_status() != 5: messages.success(request, _("Network Device is deleted. Please reboot instance to activate."))
|
||||
return HttpResponseRedirect(request.get_full_path() + '#network')
|
||||
|
||||
if 'add_owner' in request.POST:
|
||||
user_id = int(request.POST.get('user_id', ''))
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -19,6 +22,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -64,4 +70,4 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -21,6 +24,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> {% trans "Interfaces" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -91,4 +97,4 @@
|
|||
}).change();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -19,6 +22,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -184,4 +190,4 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -77,4 +83,4 @@
|
|||
}).change();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
0
nwfilters/__init__.py
Normal file
0
nwfilters/__init__.py
Normal file
6
nwfilters/admin.py
Normal file
6
nwfilters/admin.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
8
nwfilters/apps.py
Normal file
8
nwfilters/apps.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class NwfiltersConfig(AppConfig):
|
||||
name = 'nwfilters'
|
||||
0
nwfilters/migrations/__init__.py
Normal file
0
nwfilters/migrations/__init__.py
Normal file
6
nwfilters/models.py
Normal file
6
nwfilters/models.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
32
nwfilters/templates/create_nwfilter_block.html
Normal file
32
nwfilters/templates/create_nwfilter_block.html
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{% load i18n %}
|
||||
{% if request.user.is_superuser %}
|
||||
<a href="#AddNWFilter" type="button" class="btn btn-success pull-right" data-toggle="modal">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
</a>
|
||||
|
||||
<!-- Modal Secret -->
|
||||
<div class="modal fade" id="AddNWFilter" tabindex="-1" role="dialog" aria-labelledby="AddNWFilter" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">{% trans "Create New NWFilter" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12" id="xmlheight">
|
||||
<input type="hidden" name="nwfilter_xml"/>
|
||||
<textarea id="editor" name="from_xml"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="create_nwfilter">{% trans "Create" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div>
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
{% endif %}
|
||||
220
nwfilters/templates/nwfilter.html
Normal file
220
nwfilters/templates/nwfilter.html
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% include 'create_nwfilter_block.html' %}
|
||||
<h1 class="page-header">{% trans "NWFilter:" %} {{ name }}</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
{% include 'errors_block.html' %}
|
||||
{% include 'messages_block.html' %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-6 col-sm-4">
|
||||
<p>{% trans "UUID:" %}</p>
|
||||
<p>{% trans "Name:" %}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-6 col-sm-8">
|
||||
<p>{{ uuid }}</p>
|
||||
<p>{{ name }}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<p><strong>{% trans "XML:" %}</strong></p>
|
||||
<form class="form-inline" method="post" role="form">{% csrf_token %}
|
||||
<div class="col-xs-12" id="xmlheight">
|
||||
<input type="hidden" name="edit_xml"/>
|
||||
<textarea id="edit_editor">{{ xml }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary pull-right" name="edit_nwfilter">
|
||||
{% trans "Edit" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p><strong>{% trans "Filter References:" %}</strong></p>
|
||||
<form class="form-inline pull-right" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label>{% trans "Filter:" %}</label>
|
||||
<select id="nwfilter_select" name="nwfilters_select" class="form-control">
|
||||
<option value="" selected>None</option>
|
||||
{% for nwf in nwfilters_all %}
|
||||
<option value="{{ nwf.name }}">{{ nwf.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<button type="submit" class="btn btn-success pull-right" name="add_nwfilter_ref">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 45px;">#</th>
|
||||
<th>{% trans "Reference" %}</th>
|
||||
<th colspan="3">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for ref in refs %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ ref }}</td>
|
||||
<td style="width:30px;">
|
||||
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="ref" value="{{ ref }}">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="del_nwfilter_ref" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<p><strong>{% trans "Rules:" %}</strong></p>
|
||||
<a href="#AddNWFilterRule" type="button" class="btn btn-success pull-right" data-toggle="modal">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||
</a>
|
||||
|
||||
<!-- Modal Secret -->
|
||||
<div class="modal fade" id="AddNWFilterRule" tabindex="-1" role="dialog" aria-labelledby="AddNWFilterRule" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">{% trans "Add New NWFilter Rule" %}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<div class="col-xs-12" id="xmlheight">
|
||||
<input type="hidden" name="nwfilterrule_xml"/>
|
||||
<textarea id="rule_editor"></textarea>
|
||||
</div>
|
||||
<p><small><em>If there is a rule which has same attributes it replaces that rule...</em></small></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_nwfilter_rule">{% trans "Add" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div>
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 45px;">{% trans "Rule" %}</th>
|
||||
<th>{% trans "ActionType" %}</th>
|
||||
<th>{% trans "Direction" %}</th>
|
||||
<th>{% trans "Priority" %}</th>
|
||||
<th>{% trans "Statematch" %}</th>
|
||||
<th>{% trans "Directives" %}</th>
|
||||
<th colspan="3">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for rule in rules %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ rule.action }}</td>
|
||||
<td>{{ rule.direction }}</td>
|
||||
<td>{{ rule.priority }}</td>
|
||||
<td>{{ rule.statematch }}</td>
|
||||
<td>{{ rule.directives }}</td>
|
||||
<td style="width:30px;">
|
||||
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="action" value="{{ rule.action }}">
|
||||
<input type="hidden" name="direction" value="{{ rule.direction }}">
|
||||
<input type="hidden" name="priority" value="{{ rule.priority }}">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="del_nwfilter_rule" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
(function ($) {
|
||||
$('#filter').keyup(function () {
|
||||
var rex = new RegExp($(this).val(), 'i');
|
||||
$('.searchable tr').hide();
|
||||
$('.searchable tr').filter(function () {
|
||||
return rex.test($(this).text());
|
||||
}).show();
|
||||
})
|
||||
}(jQuery));
|
||||
});
|
||||
</script>
|
||||
<script src="{% static "js/ace.js" %}"></script>
|
||||
<script>
|
||||
var editor = ace.edit("edit_editor");
|
||||
editor.getSession().setMode("ace/mode/xml");
|
||||
|
||||
var edit_input = $('input[name="edit_xml"]');
|
||||
editor.getSession().on("change",function () {
|
||||
edit_input.val(editor.getSession().getValue());
|
||||
})
|
||||
|
||||
var rule_editor = ace.edit("rule_editor");
|
||||
rule_editor.getSession().setMode("ace/mode/xml");
|
||||
|
||||
var rule_input = $('input[name="nwfilterrule_xml"]');
|
||||
rule_editor.getSession().on("change",function () {
|
||||
rule_input.val(rule_editor.getSession().getValue());
|
||||
})
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
171
nwfilters/templates/nwfilters.html
Normal file
171
nwfilters/templates/nwfilters.html
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% include 'create_nwfilter_block.html' %}
|
||||
<h1 class="page-header">{{ compute.name }}</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> {% trans "NWFilters" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
{% include 'errors_block.html' %}
|
||||
{% include 'messages_block.html' %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="row">
|
||||
<div class="pull-right">
|
||||
<input id="filter" class="form-control" type="text" placeholder="Search">
|
||||
</div>
|
||||
<h3 class="page-header">{% trans "NWFilters" %}</h3>
|
||||
|
||||
</div>
|
||||
{% if nwfilters %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 45px;">#</th>
|
||||
<th>{% trans "UUID" %}</th>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th data-sortable="false" colspan="3">{% trans "Action" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="searchable">
|
||||
{% for nwfilter in nwfilters %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td><a href="{% url "nwfilter" compute.id nwfilter.name %} ">{{ nwfilter.uuid }}</a></td>
|
||||
<td>{{ nwfilter.name }}</td>
|
||||
<td style="width:30px;">
|
||||
<div class="modal fade" id="Show{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="showNWFilter" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">{% trans "Details of NWFilter" %}: <span class="text-danger">{{ nwfilter.name }}</span></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<pre>
|
||||
<code>
|
||||
{{ nwfilter.xml }}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
<a data-toggle="modal" href="#Show{{ forloop.counter }}" class="btn btn-sm btn-default" title="{% trans "Show" %}"><i class="fa fa-eye"></i></a>
|
||||
</td>
|
||||
<td style="width:30px;">
|
||||
<div class="modal fade" id="Clone{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addNwFilterLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">{% trans "Clone nwfilter" %} <span class="text-danger">{{ nwfilter.name }}</span></h4>
|
||||
</div>
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="cln_name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
|
||||
<input type="hidden" name="nwfiltername" value="{{ nwfilter.name }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="cln_nwfilter">{% trans "Clone" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
<a data-toggle="modal" href="#Clone{{ forloop.counter }}" class="btn btn-sm btn-default" title="{% trans "Clone" %}"><i class="fa fa-files-o"></i></a>
|
||||
</td>
|
||||
<td style="width:30px;">
|
||||
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="nwfiltername" value="{{ nwfilter.name }}">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="del_nwfilter" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-lg-12">
|
||||
<div class="alert alert-warning alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any NWFilters" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
(function ($) {
|
||||
$('#filter').keyup(function () {
|
||||
var rex = new RegExp($(this).val(), 'i');
|
||||
$('.searchable tr').hide();
|
||||
$('.searchable tr').filter(function () {
|
||||
return rex.test($(this).text());
|
||||
}).show();
|
||||
})
|
||||
}(jQuery));
|
||||
});
|
||||
</script>
|
||||
|
||||
<script src="{% static "js/ace.js" %}"></script>
|
||||
<script>
|
||||
var editor = ace.edit("editor");
|
||||
editor.getSession().setMode("ace/mode/xml");
|
||||
|
||||
var input = $('input[name="nwfilter_xml"]');
|
||||
editor.getSession().on("change",function () {
|
||||
input.val(editor.getSession().getValue());
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
6
nwfilters/tests.py
Normal file
6
nwfilters/tests.py
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
211
nwfilters/views.py
Normal file
211
nwfilters/views.py
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from computes.models import Compute
|
||||
from vrtManager import util
|
||||
from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
|
||||
from vrtManager.instance import wvmInstances, wvmInstance
|
||||
from libvirt import libvirtError
|
||||
from logs.views import addlogmsg
|
||||
|
||||
|
||||
@login_required
|
||||
def nwfilters(request, compute_id):
|
||||
"""
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
|
||||
if not request.user.is_superuser:
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
error_messages = []
|
||||
nwfilters_all = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
|
||||
try:
|
||||
conn = wvmNWFilters(compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
|
||||
if request.method == 'POST':
|
||||
if 'create_nwfilter' in request.POST:
|
||||
xml = request.POST.get('nwfilter_xml', '')
|
||||
if xml:
|
||||
try:
|
||||
util.etree.fromstring(xml)
|
||||
name = util.get_xml_path(xml, '/filter/@name')
|
||||
uuid = util.get_xml_path(xml, '/filter/uuid')
|
||||
except util.etree.ParseError:
|
||||
name = None
|
||||
|
||||
for nwf in nwfilters:
|
||||
if name == nwf.name():
|
||||
error_msg = _("A network filter with this name already exists")
|
||||
raise Exception(error_msg)
|
||||
if uuid == nwf.UUIDString():
|
||||
error_msg = _("A network filter with this uuid already exists")
|
||||
raise Exception(error_msg)
|
||||
else:
|
||||
try:
|
||||
msg = _("Creating NWFilter: %s" % name)
|
||||
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)
|
||||
|
||||
if 'del_nwfilter' in request.POST:
|
||||
name = request.POST.get('nwfiltername','')
|
||||
msg = _("Deleting NWFilter: %s" % name)
|
||||
in_use = False
|
||||
nwfilter = conn.get_nwfilter(name)
|
||||
|
||||
is_conn = wvmInstances(compute.hostname, compute.login, compute.password, compute.type)
|
||||
instances = is_conn.get_instances()
|
||||
for inst in instances:
|
||||
# if in_use: break
|
||||
i_conn = wvmInstance(compute.hostname, compute.login, compute.password, compute.type, inst)
|
||||
dom_filterrefs = i_conn.get_filterrefs()
|
||||
|
||||
if name in dom_filterrefs:
|
||||
in_use = True
|
||||
msg = _("NWFilter is in use by %s. Cannot be deleted." % inst)
|
||||
error_messages.append(msg)
|
||||
addlogmsg(request.user.username, compute.hostname, msg)
|
||||
i_conn.close()
|
||||
break
|
||||
|
||||
is_conn.close()
|
||||
if nwfilter and not in_use:
|
||||
nwfilter.undefine()
|
||||
addlogmsg(request.user.username, compute.hostname, msg)
|
||||
|
||||
if 'cln_nwfilter' in request.POST:
|
||||
|
||||
name = request.POST.get('nwfiltername','')
|
||||
cln_name = request.POST.get('cln_name', name + '-clone')
|
||||
|
||||
conn.clone_nwfilter(name,cln_name)
|
||||
msg = _("Cloning NWFilter %s as %s" % (name, cln_name))
|
||||
addlogmsg(request.user.username, compute.hostname, msg)
|
||||
|
||||
for nwf in conn.get_nwfilters():
|
||||
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
||||
|
||||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
addlogmsg(request.user.username, compute.hostname, lib_err)
|
||||
except Exception as err:
|
||||
error_messages.append(err)
|
||||
addlogmsg(request.user.username, compute.hostname, err)
|
||||
|
||||
return render(request, 'nwfilters.html', {'error_messages': error_messages,
|
||||
'nwfilters': nwfilters_all,
|
||||
'compute': compute})
|
||||
|
||||
|
||||
@login_required
|
||||
def nwfilter(request, compute_id, nwfltr):
|
||||
|
||||
error_messages = []
|
||||
nwfilters_all = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
|
||||
try:
|
||||
nwfilter = wvmNWFilter(compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type,
|
||||
nwfltr)
|
||||
conn = wvmNWFilters(compute.hostname,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
|
||||
for nwf in conn.get_nwfilters():
|
||||
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
||||
|
||||
uuid = nwfilter.get_uuid()
|
||||
name = nwfilter.get_name()
|
||||
xml = nwfilter.get_xml()
|
||||
rules = nwfilter.get_rules()
|
||||
refs = nwfilter.get_filter_refs()
|
||||
|
||||
if request.method == 'POST':
|
||||
|
||||
if 'edit_nwfilter' in request.POST:
|
||||
new_xml = request.POST.get('edit_xml', '')
|
||||
|
||||
if new_xml:
|
||||
nwfilter.delete()
|
||||
try:
|
||||
conn.create_nwfilter(new_xml)
|
||||
except libvirtError as lib_err:
|
||||
conn.create_nwfilter(xml)
|
||||
raise libvirtError(lib_err)
|
||||
|
||||
if 'del_nwfilter_rule' in request.POST:
|
||||
action = request.POST.get('action', '')
|
||||
direction = request.POST.get('direction', '')
|
||||
priority = request.POST.get('priority', '')
|
||||
|
||||
new_xml = nwfilter.delete_rule(action, direction, priority)
|
||||
nwfilter.delete()
|
||||
try:
|
||||
conn.create_nwfilter(new_xml)
|
||||
except libvirtError as lib_err:
|
||||
conn.create_nwfilter(xml)
|
||||
raise libvirtError(lib_err)
|
||||
|
||||
if 'del_nwfilter_ref' in request.POST:
|
||||
ref_name = request.POST.get('ref')
|
||||
new_xml = nwfilter.delete_ref(ref_name)
|
||||
nwfilter.delete()
|
||||
try:
|
||||
conn.create_nwfilter(new_xml)
|
||||
except libvirtError as lib_err:
|
||||
conn.create_nwfilter(xml)
|
||||
raise libvirtError(lib_err)
|
||||
|
||||
if 'add_nwfilter_rule' in request.POST:
|
||||
rule_xml = request.POST.get('nwfilterrule_xml', '')
|
||||
if not rule_xml:
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
new_xml = nwfilter.add_rule(rule_xml)
|
||||
nwfilter.delete()
|
||||
try:
|
||||
conn.create_nwfilter(new_xml)
|
||||
except libvirtError as lib_err:
|
||||
conn.create_nwfilter(xml)
|
||||
raise libvirtError(lib_err)
|
||||
|
||||
if 'add_nwfilter_ref' in request.POST:
|
||||
ref_name = request.POST.get('nwfilters_select', '')
|
||||
if not ref_name:
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
new_xml = nwfilter.add_ref(ref_name)
|
||||
nwfilter.delete()
|
||||
try:
|
||||
conn.create_nwfilter(new_xml)
|
||||
except libvirtError as lib_err:
|
||||
conn.create_nwfilter(xml)
|
||||
raise libvirtError(lib_err)
|
||||
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
conn.close()
|
||||
nwfilter.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
except Exception as error_msg:
|
||||
error_messages.append(error_msg)
|
||||
|
||||
return render(request, 'nwfilter.html', locals())
|
||||
|
|
@ -10,11 +10,14 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% include 'create_secret_block.html' %}
|
||||
<h1 class="page-header">{% trans "Secrets" %}</h1>
|
||||
<h1 class="page-header">{{ compute.name }}</h1>
|
||||
<ol class="breadcrumb">
|
||||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -24,6 +27,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> {% trans "Secrets" %}
|
||||
</li>
|
||||
|
|
@ -119,4 +125,4 @@
|
|||
{% endblock %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
2
static/js/sortable.min.js
vendored
2
static/js/sortable.min.js
vendored
|
|
@ -1,2 +1,2 @@
|
|||
/*! sortable.js 0.8.0 */
|
||||
(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
|
||||
(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
|
@ -25,6 +28,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -42,7 +48,7 @@
|
|||
<p>{% trans "Pool type:" %}</p>
|
||||
<p>{% trans "Pool path:" %}</p>
|
||||
<p>{% trans "Pool status:" %}</p>
|
||||
<p>{% trans "Size:" %} ({{ size|filesizeformat }} / {{ used|filesizeformat }})</p>
|
||||
<p>{% trans "Size:" %} (Used: {{ used|filesizeformat }} / From total: {{ size|filesizeformat }})</p>
|
||||
<p>{% trans "State:" %}</p>
|
||||
<p>{% trans "Autostart:" %}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<i class="fa fa-server"></i> <a href="{% url 'compute_instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> {% trans "Storages" %}
|
||||
</li>
|
||||
|
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
@ -64,4 +70,4 @@
|
|||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ def storage(request, compute_id, pool):
|
|||
meta_prealloc = True
|
||||
try:
|
||||
conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc)
|
||||
messages.success("Image file {} is created successfully".format(data['name']+".img"))
|
||||
messages.success(request, _("Image file {} is created successfully".format(data['name']+".img")))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
|
@ -167,7 +167,7 @@ def storage(request, compute_id, pool):
|
|||
try:
|
||||
vol = conn.get_volume(volname)
|
||||
vol.delete(0)
|
||||
messages.success(request,_('Volume: {} is deleted.'.format(volname)))
|
||||
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)
|
||||
|
|
@ -197,8 +197,7 @@ def storage(request, compute_id, pool):
|
|||
format = None
|
||||
try:
|
||||
conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
|
||||
messages.success(request, _("{} image cloned as {} successfully".format(data['image'],
|
||||
data['name'] + ".img")))
|
||||
messages.success(request, _("{} image cloned as {} successfully".format(data['image'], data['name'] + ".img")))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
|
|
|||
|
|
@ -389,6 +389,12 @@ class wvmConnect(object):
|
|||
interface.append(inface)
|
||||
return interface
|
||||
|
||||
def get_nwfilters(self):
|
||||
nwfilters = []
|
||||
for nwfilter in self.wvm.listNWFilters():
|
||||
nwfilters.append(nwfilter)
|
||||
return nwfilters
|
||||
|
||||
def get_cache_modes(self):
|
||||
"""Get cache available modes"""
|
||||
return {
|
||||
|
|
@ -443,7 +449,6 @@ class wvmConnect(object):
|
|||
|
||||
def get_video(self):
|
||||
""" Get available graphics video types """
|
||||
|
||||
def get_video_list(ctx):
|
||||
result = []
|
||||
for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'):
|
||||
|
|
@ -470,6 +475,9 @@ class wvmConnect(object):
|
|||
def get_network(self, net):
|
||||
return self.wvm.networkLookupByName(net)
|
||||
|
||||
def get_nwfilter(self, name):
|
||||
return self.wvm.nwfilterLookupByName(name)
|
||||
|
||||
def get_instance(self, name):
|
||||
return self.wvm.lookupByName(name)
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,19 @@ class wvmInstance(wvmConnect):
|
|||
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
|
||||
return title if title else ''
|
||||
|
||||
def get_filterrefs(self):
|
||||
|
||||
def filterrefs(ctx):
|
||||
result = []
|
||||
for net in ctx.xpath('/domain/devices/interface'):
|
||||
filterref = net.xpath('filterref/@filter')
|
||||
if filterref:
|
||||
result.append(filterref[0])
|
||||
return result
|
||||
|
||||
return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
|
||||
|
||||
|
||||
def get_description(self):
|
||||
description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
|
||||
return description if description else ''
|
||||
|
|
@ -219,12 +232,13 @@ class wvmInstance(wvmConnect):
|
|||
mac_host = net.xpath('mac/@address')[0]
|
||||
network_host = net.xpath('source/@network|source/@bridge|source/@dev')[0]
|
||||
target_host = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0]
|
||||
filterref_host = '' if not net.xpath('filterref/@filter') else net.xpath('filterref/@filter')[0]
|
||||
try:
|
||||
net = self.get_network(network_host)
|
||||
ip = get_mac_ipaddr(net, mac_host)
|
||||
except libvirtError as e:
|
||||
ip = None
|
||||
result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip})
|
||||
result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip, 'filterref': filterref_host})
|
||||
return result
|
||||
|
||||
return util.get_xml_path(self._XMLDesc(0), func=networks)
|
||||
|
|
@ -487,8 +501,7 @@ class wvmInstance(wvmConnect):
|
|||
return self._defineXML(newxml)
|
||||
|
||||
def get_console_socket(self):
|
||||
socket = util.get_xml_path(self._XMLDesc(0),
|
||||
"/domain/devices/graphics/@socket")
|
||||
socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket")
|
||||
return socket
|
||||
|
||||
def get_console_type(self):
|
||||
|
|
@ -700,13 +713,11 @@ class wvmInstance(wvmConnect):
|
|||
source_file = elm.get('file')
|
||||
if source_file:
|
||||
clone_dev_path.append(source_file)
|
||||
clone_path = os.path.join(os.path.dirname(source_file),
|
||||
target_file)
|
||||
clone_path = os.path.join(os.path.dirname(source_file), target_file)
|
||||
elm.set('file', clone_path)
|
||||
|
||||
vol = self.get_volume_by_path(source_file)
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0),
|
||||
"/volume/target/format/@type")
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
||||
|
||||
if vol_format == 'qcow2' and meta_prealloc:
|
||||
meta_prealloc = True
|
||||
|
|
@ -729,8 +740,7 @@ class wvmInstance(wvmConnect):
|
|||
elm.set('name', clone_name)
|
||||
|
||||
vol = self.get_volume_by_path(source_name)
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0),
|
||||
"/volume/target/format/@type")
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
||||
|
||||
vol_clone_xml = """
|
||||
<volume type='network'>
|
||||
|
|
@ -776,7 +786,7 @@ class wvmInstance(wvmConnect):
|
|||
bridge_name = net.bridgeName()
|
||||
return bridge_name
|
||||
|
||||
def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio'):
|
||||
def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio', nwfilter=None):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
bridge_name = self.get_bridge_name(source, source_type)
|
||||
xml_interface = """
|
||||
|
|
@ -784,8 +794,13 @@ class wvmInstance(wvmConnect):
|
|||
<mac address='%s'/>
|
||||
<source bridge='%s'/>
|
||||
<model type='%s'/>
|
||||
</interface>
|
||||
""" % (interface_type, mac_address, bridge_name, model)
|
||||
""" % (interface_type, mac_address, bridge_name, model)
|
||||
if nwfilter:
|
||||
xml_interface += """
|
||||
<filterref filter='%s'/>
|
||||
""" % nwfilter
|
||||
xml_interface += """</interface>"""
|
||||
|
||||
if self.get_status() == 5:
|
||||
devices = tree.find('devices')
|
||||
elm_interface = ElementTree.fromstring(xml_interface)
|
||||
|
|
@ -793,20 +808,42 @@ class wvmInstance(wvmConnect):
|
|||
xmldom = ElementTree.tostring(tree)
|
||||
self._defineXML(xmldom)
|
||||
|
||||
def delete_network(self, mac_address):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
devices = tree.find('devices')
|
||||
for interface in tree.findall('devices/interface'):
|
||||
source = interface.find('mac')
|
||||
if source.get('address', '') == mac_address:
|
||||
source = None
|
||||
devices.remove(interface)
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def change_network(self, network_data):
|
||||
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
tree = ElementTree.fromstring(xml)
|
||||
|
||||
for num, interface in enumerate(tree.findall('devices/interface')):
|
||||
net_source = network_data['net-source-' + str(num)]
|
||||
net_source_type = network_data['net-source-' + str(num) + '-type']
|
||||
net_mac = network_data['net-mac-' + str(num)]
|
||||
net_filter = network_data['net-nwfilter-' + str(num)]
|
||||
bridge_name = self.get_bridge_name(net_source, net_source_type)
|
||||
if interface.get('type') == 'bridge':
|
||||
source = interface.find('mac')
|
||||
source.set('address', net_mac)
|
||||
source = interface.find('source')
|
||||
source.set('bridge', bridge_name)
|
||||
source = interface.find('filterref')
|
||||
|
||||
if net_filter:
|
||||
if source is not None: source.set('filter', net_filter)
|
||||
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)
|
||||
self._defineXML(new_xml)
|
||||
|
||||
|
|
|
|||
125
vrtManager/nwfilters.py
Normal file
125
vrtManager/nwfilters.py
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
from vrtManager.connection import wvmConnect
|
||||
from xml.etree import ElementTree
|
||||
|
||||
|
||||
class wvmNWFilters(wvmConnect):
|
||||
def get_nwfilter_info(self, name):
|
||||
nwfilter = self.get_nwfilter(name)
|
||||
xml = nwfilter.XMLDesc(0)
|
||||
uuid = nwfilter.UUIDString()
|
||||
return {'name': name, 'uuid': uuid, 'xml': xml}
|
||||
|
||||
def create_nwfilter(self, xml):
|
||||
self.wvm.nwfilterDefineXML(xml)
|
||||
|
||||
def clone_nwfilter(self,name, cln_name):
|
||||
nwfilter = self.get_nwfilter(name)
|
||||
if nwfilter:
|
||||
tree = ElementTree.fromstring(nwfilter.XMLDesc(0))
|
||||
tree.set('name', cln_name)
|
||||
uuid = tree.find('uuid')
|
||||
tree.remove(uuid)
|
||||
self.create_nwfilter(ElementTree.tostring(tree))
|
||||
|
||||
|
||||
class wvmNWFilter(wvmConnect):
|
||||
def __init__(self, host, login, passwd, conn, nwfiltername):
|
||||
wvmConnect.__init__(self, host, login, passwd, conn)
|
||||
self.nwfilter = self.get_nwfilter(nwfiltername)
|
||||
|
||||
def _XMLDesc(self, flags):
|
||||
return self.nwfilter.XMLDesc(flags)
|
||||
|
||||
def get_uuid(self):
|
||||
return self.nwfilter.UUIDString()
|
||||
|
||||
def get_name(self):
|
||||
return self.nwfilter.name()
|
||||
|
||||
def delete(self):
|
||||
self.nwfilter.undefine()
|
||||
|
||||
def get_xml(self):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
uuid = tree.find('uuid')
|
||||
tree.remove(uuid)
|
||||
return ElementTree.tostring(tree)
|
||||
|
||||
def get_filter_refs(self):
|
||||
refs = []
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
for ref in tree.findall("./filterref"):
|
||||
refs.append(ref.get('filter'))
|
||||
return refs
|
||||
|
||||
def get_rules(self):
|
||||
rules = []
|
||||
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
for r in tree.findall("./rule"):
|
||||
rule_action = r.get('action')
|
||||
rule_direction = r.get('direction')
|
||||
rule_priority = r.get('priority')
|
||||
rule_statematch = r.get('statematch')
|
||||
|
||||
rule_directives = r.find("./")
|
||||
if rule_directives is not None:
|
||||
rule_directives = ElementTree.tostring(rule_directives)
|
||||
|
||||
rule_info = {
|
||||
"action": rule_action,
|
||||
"direction": rule_direction,
|
||||
"priority": rule_priority,
|
||||
"statematch": rule_statematch,
|
||||
"directives": rule_directives
|
||||
}
|
||||
|
||||
rules.append(rule_info)
|
||||
|
||||
return rules
|
||||
|
||||
def delete_ref(self, name):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
for ref in tree.findall("./filterref"):
|
||||
if name == ref.get('filter'):
|
||||
tree.remove(ref)
|
||||
break
|
||||
return ElementTree.tostring(tree)
|
||||
|
||||
def delete_rule(self, action, direction, priority):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
|
||||
rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (action, direction, priority))
|
||||
if rule_tree:
|
||||
tree.remove(rule_tree[0])
|
||||
|
||||
return ElementTree.tostring(tree)
|
||||
|
||||
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)
|
||||
|
||||
def add_rule(self, xml):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
rule = ElementTree.fromstring(xml)
|
||||
|
||||
rule_action = rule.get('action')
|
||||
rule_direction = rule.get('direction')
|
||||
rule_priority = rule.get('priority')
|
||||
rule_directives = rule.find("./")
|
||||
rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (rule_action, rule_direction, rule_priority))
|
||||
|
||||
if rule_tree:
|
||||
rule_tree[0].append(rule_directives)
|
||||
else:
|
||||
element = ElementTree.Element("rule")
|
||||
element.attrib['action'] = rule_action
|
||||
element.attrib['direction'] = rule_direction
|
||||
element.attrib['priority'] = rule_priority
|
||||
element.append(rule_directives)
|
||||
tree.append(element)
|
||||
|
||||
return ElementTree.tostring(tree)
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
from vrtManager import util
|
||||
from vrtManager.connection import wvmConnect
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
|
||||
|
||||
|
||||
class wvmStorages(wvmConnect):
|
||||
|
|
@ -205,7 +206,7 @@ class wvmStorage(wvmConnect):
|
|||
)
|
||||
return vol_list
|
||||
|
||||
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False):
|
||||
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, owner=owner):
|
||||
size = int(size) * 1073741824
|
||||
storage_type = self.get_type()
|
||||
alloc = size
|
||||
|
|
@ -222,8 +223,8 @@ class wvmStorage(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
|
|
@ -232,10 +233,10 @@ class wvmStorage(wvmConnect):
|
|||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (name, size, alloc, vol_fmt)
|
||||
</volume>""" % (name, size, alloc, vol_fmt, owner, owner)
|
||||
self._createXML(xml, metadata)
|
||||
|
||||
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False):
|
||||
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, owner=owner):
|
||||
storage_type = self.get_type()
|
||||
if storage_type == 'dir':
|
||||
target_file += '.img'
|
||||
|
|
@ -250,8 +251,8 @@ class wvmStorage(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
|
|
@ -260,5 +261,5 @@ class wvmStorage(wvmConnect):
|
|||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (target_file, vol_fmt)
|
||||
</volume>""" % (target_file, vol_fmt, owner,owner)
|
||||
self._createXMLFrom(xml, vol, metadata)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ INSTALLED_APPS = (
|
|||
'networks',
|
||||
'storages',
|
||||
'interfaces',
|
||||
'nwfilters',
|
||||
'instances',
|
||||
'secrets',
|
||||
'logs',
|
||||
|
|
@ -105,6 +106,13 @@ WS_HOST = '0.0.0.0'
|
|||
# Websock public port
|
||||
WS_PUBLIC_HOST = None
|
||||
|
||||
# Websock public port - default is 80 - uncomment 443 and comment 80 if you want to use vnc over https
|
||||
WS_PUBLIC_PORT = 80
|
||||
#WS_PUBLIC_PORT = 443
|
||||
|
||||
# Websock pubic path
|
||||
WS_PUBLIC_PATH = '/novncd/'
|
||||
|
||||
# Websock SSL connection
|
||||
WS_CERT = None
|
||||
|
||||
|
|
@ -151,3 +159,5 @@ VIEW_INSTANCES_LIST_STYLE = 'grouped'
|
|||
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
|
||||
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
|
||||
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
|
||||
# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
|
||||
INSTANCE_VOLUME_DEFAULT_OWNER = 0
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from secrets.views import secrets
|
|||
from create.views import create_instance
|
||||
from interfaces.views import interfaces, interface
|
||||
from console.views import console
|
||||
from nwfilters.views import nwfilters, nwfilter
|
||||
from computes.views import computes
|
||||
# from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
|
|
@ -14,8 +16,13 @@ urlpatterns = [
|
|||
url(r'^instances/$', instances, name='instances'),
|
||||
|
||||
url(r'^instance/', include('instances.urls')),
|
||||
url(r'^instances/$', instances, name='instances'),
|
||||
|
||||
url(r'^accounts/', include('accounts.urls')),
|
||||
|
||||
url(r'^computes/', include('computes.urls')),
|
||||
url(r'^computes/', computes, name='computes'),
|
||||
|
||||
url(r'^logs/', include('logs.urls')),
|
||||
url(r'^datasource/', include('datasource.urls')),
|
||||
|
||||
|
|
@ -25,9 +32,12 @@ urlpatterns = [
|
|||
url(r'^compute/(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
|
||||
url(r'^compute/(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
|
||||
|
||||
|
||||
url(r'^console/$', console, name='console'),
|
||||
# (r'^admin/', include(admin.site.urls)),
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue