1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-03-23 11:04:49 +00:00
This commit is contained in:
Bandic007 2018-09-27 09:39:50 +00:00 committed by GitHub
commit f072c2daa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1312 additions and 92 deletions

View file

@ -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>

View file

@ -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'),
]

View file

@ -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):

View file

@ -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 %}

View file

@ -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;
}

View file

@ -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)

View file

@ -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)

View file

@ -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">

View file

@ -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]:

View file

@ -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 -->

View 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">&times;</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 %}

View file

@ -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) {

View file

@ -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>

View file

@ -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', ''))

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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
View file

6
nwfilters/admin.py Normal file
View 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
View file

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.apps import AppConfig
class NwfiltersConfig(AppConfig):
name = 'nwfilters'

View file

6
nwfilters/models.py Normal file
View file

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.

View 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">&times;</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 %}

View 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">&times;</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 %}

View 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">&times;</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">&times;</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">&times;</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
View 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
View 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())

View file

@ -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 %}

View file

@ -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);

View file

@ -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>

View file

@ -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 %}

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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)

View file

@ -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)

View file

@ -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

View file

@ -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)),
]