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:
catborise 2018-10-02 10:55:39 +00:00 committed by GitHub
commit 27aec79af5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 4295 additions and 969 deletions

View file

@ -17,7 +17,7 @@
<h2 class="form-signin-heading">{% trans "Sign In" %}</h2> <h2 class="form-signin-heading">{% trans "Sign In" %}</h2>
<input type="text" class="form-control" name="username" placeholder="Login" autocapitalize="none" autocorrect="off" autofocus> <input type="text" class="form-control" name="username" placeholder="Login" autocapitalize="none" autocorrect="off" autofocus>
<input type="password" class="form-control" name="password" placeholder="Password"> <input type="password" class="form-control" name="password" placeholder="Password">
<input name="next" id="next" type="hidden" value="{% url 'instances' %}"> <input name="next" id="next" type="hidden" value="{% url 'allinstances' %}">
<button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button> <button class="btn btn-lg btn-success btn-block" type="submit">{% trans "Sign In" %}</button>
</form> </form>
</div> </div>

View file

@ -11,6 +11,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> {% trans "Overview" %} <i class="fa fa-dashboard"></i> {% trans "Overview" %}
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -20,6 +23,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -1,9 +1,26 @@
from django.conf.urls import url 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, computes
from instances.views import instances
from nwfilters.views import nwfilter, nwfilters
urlpatterns = [ urlpatterns = [
url(r'^$', views.computes, name='computes'), url(r'^/', computes, name='computes'),
url(r'^overview/(?P<compute_id>[0-9]+)/$', views.overview, name='overview'), url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
url(r'^statistics/(?P<compute_id>[0-9]+)/$', url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
views.compute_graph, name='compute_graph'), url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='instances'),
url(r'^(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
url(r'^(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
url(r'^(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
url(r'^(?P<compute_id>[0-9]+)/network/(?P<pool>[\w\-\.]+)/$', network, name='network'),
url(r'^(?P<compute_id>[0-9]+)/interfaces/$', interfaces, name='interfaces'),
url(r'^(?P<compute_id>[0-9]+)/interface/(?P<iface>[\w\-\.\:]+)/$', interface, name='interface'),
url(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
url(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
] ]

View file

@ -38,6 +38,7 @@ class NewVMForm(forms.Form):
disk = forms.IntegerField(required=False) disk = forms.IntegerField(required=False)
memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')}) memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
networks = forms.CharField(error_messages={'required': _('No Network pool has been choice')}) 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) storage = forms.CharField(max_length=20, required=False)
template = forms.CharField(required=False) template = forms.CharField(required=False)
images = forms.CharField(required=False) images = forms.CharField(required=False)

View file

@ -107,6 +107,17 @@
{% endfor %} {% endfor %}
</select> </select>
</div> </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>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label> <label class="col-sm-3 control-label">{% trans "Video" %}</label>
@ -226,6 +237,17 @@
</select> </select>
</div> </div>
</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"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label> <label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
@ -379,6 +401,17 @@
</select> </select>
</div> </div>
</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"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "MAC" %}</label> <label class="col-sm-3 control-label">{% trans "MAC" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">

View file

@ -28,7 +28,6 @@ def create_instance(request, compute_id):
storages = [] storages = []
networks = [] networks = []
meta_prealloc = False meta_prealloc = False
#computes = Compute.objects.all()
compute = get_object_or_404(Compute, pk=compute_id) compute = get_object_or_404(Compute, pk=compute_id)
flavors = Flavor.objects.filter().order_by('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)) storages = sorted(conn.get_storages(only_actives=True))
networks = sorted(conn.get_networks()) networks = sorted(conn.get_networks())
nwfilters = conn.get_nwfilters()
instances = conn.get_instances() instances = conn.get_instances()
videos = conn.get_video() videos = conn.get_video()
cache_modes = sorted(conn.get_cache_modes().items()) cache_modes = sorted(conn.get_cache_modes().items())
@ -139,11 +139,11 @@ def create_instance(request, compute_id):
try: try:
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'], conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'], 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']) data['mac'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid) create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
create_instance.save() create_instance.save()
messages.success(request,"Instance is created.") messages.success(request, _("Instance is created."))
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']])) return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
except libvirtError as lib_err: except libvirtError as lib_err:
if data['hdd_size'] or volumes[clone_path]: if data['hdd_size'] or volumes[clone_path]:

View file

@ -33,12 +33,24 @@
</select> </select>
</div> </div>
</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>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button> <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> <button type="submit" class="btn btn-primary" name="add_network">{% trans "Add" %}</button>
</div> </div>
</form> </form>
</div>
</div> <!-- /.modal-content --> </div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog --> </div> <!-- /.modal-dialog -->
</div> <!-- /.modal --> </div> <!-- /.modal -->

View file

@ -0,0 +1,184 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
{% block title %}{% trans "Instances" %}{% 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">{% trans "Instances" %}</h1>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
<div class="row">
<div class="col-lg-12">
<div class="table-responsive">
{% if request.user.is_superuser %}
{% 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 "You don't have any Instance" %}
</div>
</div>
{% else %}
{% ifequal view_style "nongrouped" %}
{% include 'allinstances_index_nongrouped.html' %}
{% endifequal %}
{% ifequal view_style "grouped" %}
{% include 'allinstances_index_grouped.html' %}
{% endifequal %}
{% endif %}
{% else %}
{% if not all_user_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 "You don't have any Instance" %}
</div>
</div>
{% else %}
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>VCPU</th>
<th>Memory</th>
<th data-sortable="false" style="width: 165px;">Actions</th>
</tr>
</thead>
<tbody class="searchable">
{% for inst, vm in all_user_vms.items %}
<tr>
<td><a href="{% url 'instance' vm.compute_id vm.name %}">{{ vm.name }}</a><br><small><em>{{ vm.title }}</em></small></td>
<td>{% ifequal vm.status 1 %}
<span class="text-success">{% trans "Active" %}</span>
{% endifequal %}
{% ifequal vm.status 5 %}
<span class="text-danger">{% trans "Off" %}</span>
{% endifequal %}
{% ifequal vm.status 3 %}
<span class="text-warning">{% trans "Suspend" %}</span>
{% endifequal %}
</td>
<td>{{ vm.vcpu }}</td>
<td>{{ vm.memory }} {% trans "MB" %}</td>
<td><form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="name" value="{{ vm.name }}"/>
<input type="hidden" name="compute_id" value="{{ vm.compute_id }}"/>
{% ifequal vm.status 5 %}
{% if inst.instance.is_template %}
<button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ vm.compute_id }}, '{{ vm.name }}');">
<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 "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 vm.status 3 %}
<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 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/Spice Console" %}">
<span class="glyphicon glyphicon-eye-open"></span>
</button>
{% endifequal %}
{% ifequal vm.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="poweroff" title="{% trans "Power Off" %}">
<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("{{ vm.compute_id }}-{{ vm.uuid }}")' title="{% trans "Console" %}">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
{% endifequal %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endif %}
</div>
</div>
</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 = "/instances/" + compute + "/" + instance + "/#clone";
}
</script>
{% if request.user.is_superuser %}
<script>
function goto_compute() {
var compute = $("#compute_select").val();
window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);
}
</script>
{% endif %}
{% endblock %}

View file

@ -833,39 +833,63 @@
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
<p> <p>
{% trans "Assign network device to bridge" %} {% trans "Assign network device to bridge" %}
{% include 'add_instance_network_block.html' %} {% include 'add_instance_network_block.html' %}
</p> </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 %} {% for network in networks %}
<div class="form-group"> <div class="panel panel-default">
<label class="col-sm-3 control-label" style="font-weight:normal;">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label> <div class="panel-heading">
<div class="col-sm-3"> <label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
<input type="text" class="form-control" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
</div> </div>
<div class="col-sm-3"> <div class="panel-body">
<input type="text" class="form-control" name="net-source-{{ forloop.counter0 }}" value="{{ network.nic }}" disabled/> <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>
<div class="col-sm-3">
<select name="net-source-{{ forloop.counter0 }}" class="form-control" id="network_select" onchange="network_select_enable()"> <div class="panel-footer">
{% for c_nets in compute_networks %} <button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<option value="net:{{ c_nets }}" {% if c_nets == network.nic %}selected{% endif %}>Network {{ c_nets }}</option> <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>
{% 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> </div>
</div> </div>
{% endfor %} {% 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> </form>
</div>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% endif %} {% endif %}
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %} {% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
@ -1237,7 +1261,7 @@
</script> </script>
<script> <script>
function random_mac(net) { function random_mac(net) {
$.getJSON('/instance/random_mac_address/', function(data) { $.getJSON('{% url 'random_mac_address' %}', function (data) {
$('input[name="'+net+'"]').val(data['mac']); $('input[name="'+net+'"]').val(data['mac']);
}); });
}; };
@ -1254,14 +1278,15 @@
<script> <script>
function guess_mac_address(src_elem, net) { function guess_mac_address(src_elem, net) {
new_vname = $(src_elem).val(); new_vname = $(src_elem).val();
$.getJSON('/instance/guess_mac_address/' + new_vname + '/', function(data) { guess_mac_address_url = "{% url 'guess_mac_address' 1 %}".replace(1, new_vname);
$.getJSON(guess_mac_address_url, function(data) {
$('input[name="clone-net-mac-'+net+'"]').val(data['mac']); $('input[name="clone-net-mac-'+net+'"]').val(data['mac']);
}); });
} }
</script> </script>
<script> <script>
function guess_clone_name() { function guess_clone_name() {
$.getJSON('/instance/guess_clone_name/', function(data) { $.getJSON('{% url 'guess_clone_name' %}', function(data) {
guessed_name = data['name'].split(".")[0]; guessed_name = data['name'].split(".")[0];
$('#clone_name').val(guessed_name); $('#clone_name').val(guessed_name);
update_clone_disk_name(guessed_name); update_clone_disk_name(guessed_name);
@ -1339,7 +1364,8 @@
}); });
$(document).ready(function () { $(document).ready(function () {
// set vdi url // set vdi url
$.get("/datasource/vdi/{{ vname }}/", function(data) {
$.get("{% url 'vdi_url' vname %}", function(data) {
$("#vdi_url_input").attr("value", data); $("#vdi_url_input").attr("value", data);
$("#vdi_url").attr("href", data); $("#vdi_url").attr("href", data);
}); });
@ -1370,12 +1396,6 @@
}); });
}); });
</script> </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 src="{% static "js/Chart.min.js" %}"></script>
<script> <script>
$('#chartgraphs').on('shown.bs.tab', function (event) { $('#chartgraphs').on('shown.bs.tab', function (event) {
@ -1574,7 +1594,8 @@
</script> </script>
<script> <script>
function update_logs_table(vname) { function update_logs_table(vname) {
$.getJSON('/logs/vm_logs/'+vname+'/', function(data) { logurl = "{% url 'vm_logs' 1 %}".replace(1, vname);
$.getJSON(logurl, function(data) {
var logs = ""; var logs = "";
$.each(data, function(id) { $.each(data, function(id) {
row = data[id]; row = data[id];

View file

@ -1,12 +1,12 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load i18n %} {% load i18n %}
{% load staticfiles %} {% load staticfiles %}
{% block title %}{% trans "Instances" %}{% endblock %} {% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
{% block style %} {% block style %}
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" /> <link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<!-- Page Heading --> <!-- Page Heading -->
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
@ -17,129 +17,144 @@
<input id="filter" class="form-control" type="text" placeholder="Search"> <input id="filter" class="form-control" type="text" placeholder="Search">
</div> </div>
{% endif %} {% endif %}
<h1 class="page-header">{% trans "Instances" %}</h1> <h1 class="page-header">{{ compute.name }}</h1>
</div> </div>
</div> </div>
<!-- /.row --> <div class="row">
<div class="col-lg-12">
{% include 'errors_block.html' %} <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 -->
<div class="row"> {% include 'errors_block.html' %}
<div class="row">
{% if not all_host_vms %}
<div class="col-lg-12"> <div class="col-lg-12">
<div class="table-responsive"> <div class="alert alert-warning alert-dismissable">
{% if request.user.is_superuser %} <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
{% if not all_host_vms %} <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
<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 "You don't have any Instance" %}
</div>
</div>
{% else %}
{% ifequal view_style "nongrouped" %}
{% include 'instances_nongrouped.html' %}
{% endifequal %}
{% ifequal view_style "grouped" %}
{% include 'instances_grouped.html' %}
{% endifequal %}
{% endif %}
{% else %}
{% if not all_user_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 "You don't have any Instance" %}
</div>
</div>
{% else %}
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>VCPU</th>
<th>Memory</th>
<th data-sortable="false" style="width: 165px;">Actions</th>
</tr>
</thead>
<tbody class="searchable">
{% for inst, vm in all_user_vms.items %}
<tr>
<td><a href="{% url 'instance' vm.compute_id vm.name %}">{{ vm.name }}</a><br><small><em>{{ vm.title }}</em></small></td>
<td>{% ifequal vm.status 1 %}
<span class="text-success">{% trans "Active" %}</span>
{% endifequal %}
{% ifequal vm.status 5 %}
<span class="text-danger">{% trans "Off" %}</span>
{% endifequal %}
{% ifequal vm.status 3 %}
<span class="text-warning">{% trans "Suspend" %}</span>
{% endifequal %}
</td>
<td>{{ vm.vcpu }}</td>
<td>{{ vm.memory }} {% trans "MB" %}</td>
<td><form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="name" value="{{ vm.name }}"/>
<input type="hidden" name="compute_id" value="{{ vm.compute_id }}"/>
{% ifequal vm.status 5 %}
{% if inst.instance.is_template %}
<button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ vm.compute_id }}, '{{ vm.name }}');">
<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 "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 vm.status 3 %}
<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 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/Spice Console" %}">
<span class="glyphicon glyphicon-eye-open"></span>
</button>
{% endifequal %}
{% ifequal vm.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="poweroff" title="{% trans "Power Off" %}">
<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("{{ vm.compute_id }}-{{ vm.uuid }}")' title="{% trans "Console" %}">
<span class="glyphicon glyphicon-eye-open"></span>
</a>
{% endifequal %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endif %}
</div> </div>
</div> </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 %} {% endblock %}
{% block script %} {% block script %}
<script src="{% static "js/sortable.min.js" %}"></script> <script src="{% static "js/sortable.min.js" %}"></script>
@ -170,15 +185,15 @@
</script> </script>
<script> <script>
function goto_instance_clone(compute, instance) { function goto_instance_clone(compute, instance) {
window.location = "/instance/" + compute + "/" + instance + "/#clone"; window.location = "/instances/" + compute + "/" + instance + "/#clone";
} }
</script> </script>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<script> <script>
function goto_compute() { function goto_compute() {
var compute = $("#compute_select").val(); var compute = $("#compute_select").val();
window.location = "/compute/" + compute + "/create/"; window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);
} }
</script> </script>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -2,6 +2,7 @@ from django.conf.urls import url
from . import views from . import views
urlpatterns = [ urlpatterns = [
url(r'^$', views.allinstances, name='allinstances'),
url(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'), url(r'^(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.instance, name='instance'),
url(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'), url(r'^statistics/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_graph, name='inst_graph'),
url(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'), url(r'^status/(?P<compute_id>[0-9]+)/(?P<vname>[\w\-\.]+)/$', views.inst_status, name='inst_status'),

View file

@ -26,175 +26,72 @@ from logs.views import addlogmsg
from django.conf import settings from django.conf import settings
from django.contrib import messages from django.contrib import messages
@login_required @login_required
def index(request): def index(request):
""" """
:param request: :param request:
:return: :return:
""" """
return HttpResponseRedirect(reverse('allinstances'))
return HttpResponseRedirect(reverse('instances'))
@login_required @login_required
def instances(request): def allinstances(request):
"""
INSTANCES LIST FOR ALL HOSTS
:param request:
:return:
"""
all_host_vms = {}
error_messages = []
computes = Compute.objects.all().order_by("name")
if not request.user.is_superuser:
all_user_vms = get_user_instances(request)
else:
for comp in computes:
try:
all_host_vms.update(get_host_instances(request,comp))
except libvirtError as lib_err:
error_messages.append(lib_err)
if request.method == 'POST':
try:
instances_actions(request)
except libvirtError as lib_err:
error_messages.append(lib_err)
addlogmsg(request.user.username, instance.name, lib_err.message)
view_style = settings.VIEW_INSTANCES_LIST_STYLE
return render(request, 'allinstances.html', locals())
@login_required
def instances(request, compute_id):
""" """
:param request: :param request:
:return: :return:
""" """
error_messages = [] error_messages = []
all_host_vms = {} compute = get_object_or_404(Compute, pk=compute_id)
all_user_vms = {}
computes = Compute.objects.all().order_by("name")
def get_userinstances_info(instance):
info = {}
uis = UserInstance.objects.filter(instance=instance)
info['count'] = uis.count()
if info['count'] > 0:
info['first_user'] = uis[0]
else:
info['first_user'] = None
return info
def refresh_instance_database(comp, vm, info):
instances = Instance.objects.filter(name=vm)
if instances.count() > 1:
for i in instances:
user_instances_count = UserInstance.objects.filter(instance=i).count()
if user_instances_count == 0:
addlogmsg(request.user.username, i.name, _("Deleting due to multiple records."))
i.delete()
try:
check_uuid = Instance.objects.get(compute_id=comp["id"], name=vm)
if check_uuid.uuid != info['uuid']:
check_uuid.save()
all_host_vms[comp["id"],
comp["name"],
comp["status"],
comp["cpu"],
comp["mem_size"],
comp["mem_perc"]][vm]['is_template'] = check_uuid.is_template
all_host_vms[comp["id"],
comp["name"],
comp["status"],
comp["cpu"],
comp["mem_size"],
comp["mem_perc"]][vm]['userinstances'] = get_userinstances_info(check_uuid)
except Instance.DoesNotExist:
check_uuid = Instance(compute_id=comp["id"], name=vm, uuid=info['uuid'])
check_uuid.save()
if not request.user.is_superuser: if not request.user.is_superuser:
user_instances = UserInstance.objects.filter(user_id=request.user.id) all_user_vms = get_user_instances(request)
for usr_inst in user_instances:
if connection_manager.host_is_up(usr_inst.instance.compute.type,
usr_inst.instance.compute.hostname):
conn = wvmHostDetails(usr_inst.instance.compute,
usr_inst.instance.compute.login,
usr_inst.instance.compute.password,
usr_inst.instance.compute.type)
all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name)
all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
else: else:
for comp in computes: try:
status = connection_manager.host_is_up(comp.type, comp.hostname) all_host_vms = get_host_instances(request, compute)
if status: except libvirtError as lib_err:
try: error_messages.append(lib_err)
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
comp_node_info = conn.get_node_info()
comp_mem = conn.get_memory_usage()
comp_instances = conn.get_host_instances(True)
if comp_instances:
comp_info= {
"id": comp.id,
"name": comp.name,
"status": status,
"cpu": comp_node_info[3],
"mem_size": comp_node_info[2],
"mem_perc": comp_mem['percent']
}
all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"],
comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances
for vm, info in comp_instances.items():
refresh_instance_database(comp_info, vm, info)
conn.close()
except libvirtError as lib_err:
error_messages.append(lib_err)
if request.method == 'POST': if request.method == 'POST':
name = request.POST.get('name', '')
compute_id = request.POST.get('compute_id', '')
instance = Instance.objects.get(compute_id=compute_id, name=name)
try: try:
conn = wvmInstances(instance.compute.hostname, instances_actions(request)
instance.compute.login,
instance.compute.password,
instance.compute.type)
if 'poweron' in request.POST:
msg = _("Power On")
addlogmsg(request.user.username, instance.name, msg)
conn.start(name)
return HttpResponseRedirect(request.get_full_path())
if 'poweroff' in request.POST:
msg = _("Power Off")
addlogmsg(request.user.username, instance.name, msg)
conn.shutdown(name)
return HttpResponseRedirect(request.get_full_path())
if 'powercycle' in request.POST:
msg = _("Power Cycle")
conn.force_shutdown(name)
conn.start(name)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path())
if 'getvvfile' in request.POST:
msg = _("Send console.vv file")
addlogmsg(request.user.username, instance.name, msg)
response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None, charset='utf-8')
response.writelines('[virt-viewer]\n')
response.writelines('type=' + conn.graphics_type(name) + '\n')
response.writelines('host=' + conn.graphics_listen(name) + '\n')
response.writelines('port=' + conn.graphics_port(name) + '\n')
response.writelines('title=' + conn.domain_name(name) + '\n')
response.writelines('password=' + conn.graphics_passwd(name) + '\n')
response.writelines('enable-usbredir=1\n')
response.writelines('disable-effects=all\n')
response.writelines('secure-attention=ctrl+alt+ins\n')
response.writelines('release-cursor=ctrl+alt\n')
response.writelines('fullscreen=1\n')
response.writelines('delete-this-file=1\n')
response['Content-Disposition'] = 'attachment; filename="console.vv"'
return response
if request.user.is_superuser:
if 'suspend' in request.POST:
msg = _("Suspend")
addlogmsg(request.user.username, instance.name, msg)
conn.suspend(name)
return HttpResponseRedirect(request.get_full_path())
if 'resume' in request.POST:
msg = _("Resume")
addlogmsg(request.user.username, instance.name, msg)
conn.resume(name)
return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)
addlogmsg(request.user.username, instance.name, lib_err.message) addlogmsg(request.user.username, instance.name, lib_err.message)
view_style = settings.VIEW_INSTANCES_LIST_STYLE
return render(request, 'instances.html', locals()) return render(request, 'instances.html', locals())
@ -206,7 +103,7 @@ def instance(request, compute_id, vname):
""" """
error_messages = [] error_messages = []
#messages = [] # messages = []
compute = get_object_or_404(Compute, pk=compute_id) compute = get_object_or_404(Compute, pk=compute_id)
computes = Compute.objects.all().order_by('name') computes = Compute.objects.all().order_by('name')
computes_count = computes.count() computes_count = computes.count()
@ -217,8 +114,8 @@ def instance(request, compute_id, vname):
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
try: try:
userinstance = UserInstance.objects.get(instance__compute_id=compute_id, userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
instance__name=vname, instance__name=vname,
user__id=request.user.id) user__id=request.user.id)
except UserInstance.DoesNotExist: except UserInstance.DoesNotExist:
userinstance = None userinstance = None
@ -231,15 +128,15 @@ def instance(request, compute_id, vname):
return 0 return 0
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B") size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
if 'K' == size_str[-1]: if 'K' == size_str[-1]:
return long(float(size_str[:-1]))<<10 return long(float(size_str[:-1])) << 10
elif 'M' == size_str[-1]: elif 'M' == size_str[-1]:
return long(float(size_str[:-1]))<<20 return long(float(size_str[:-1])) << 20
elif 'G' == size_str[-1]: elif 'G' == size_str[-1]:
return long(float(size_str[:-1]))<<30 return long(float(size_str[:-1])) << 30
elif 'T' == size_str[-1]: elif 'T' == size_str[-1]:
return long(float(size_str[:-1]))<<40 return long(float(size_str[:-1])) << 40
elif 'P' == size_str[-1]: elif 'P' == size_str[-1]:
return long(float(size_str[:-1]))<<50 return long(float(size_str[:-1])) << 50
else: else:
return long(float(size_str)) return long(float(size_str))
@ -268,16 +165,16 @@ def instance(request, compute_id, vname):
if connection_manager.host_is_up(usr_inst.instance.compute.type, if connection_manager.host_is_up(usr_inst.instance.compute.type,
usr_inst.instance.compute.hostname): usr_inst.instance.compute.hostname):
conn = wvmInstance(usr_inst.instance.compute, conn = wvmInstance(usr_inst.instance.compute,
usr_inst.instance.compute.login, usr_inst.instance.compute.login,
usr_inst.instance.compute.password, usr_inst.instance.compute.password,
usr_inst.instance.compute.type, usr_inst.instance.compute.type,
usr_inst.instance.name) usr_inst.instance.name)
cpu += int(conn.get_vcpu()) cpu += int(conn.get_vcpu())
memory += int(conn.get_memory()) memory += int(conn.get_memory())
for disk in conn.get_disk_device(): for disk in conn.get_disk_device():
if disk['size']: if disk['size']:
disk_size += int(disk['size'])>>30 disk_size += int(disk['size']) >> 30
if ua.max_instances > 0 and instance > ua.max_instances: if ua.max_instances > 0 and instance > ua.max_instances:
msg = "instance" msg = "instance"
if settings.QUOTA_DEBUG: if settings.QUOTA_DEBUG:
@ -301,7 +198,7 @@ def instance(request, compute_id, vname):
dev_base = "vd" dev_base = "vd"
else: else:
dev_base = "sd" dev_base = "sd"
existing_devs = [ disk['dev'] for disk in disks ] existing_devs = [disk['dev'] for disk in disks]
for l in string.lowercase: for l in string.lowercase:
dev = dev_base + l dev = dev_base + l
if dev not in existing_devs: if dev not in existing_devs:
@ -348,6 +245,7 @@ def instance(request, compute_id, vname):
vname) vname)
compute_networks = sorted(conn.get_networks()) compute_networks = sorted(conn.get_networks())
compute_interfaces = sorted(conn.get_ifaces()) compute_interfaces = sorted(conn.get_ifaces())
compute_nwfilters = conn.get_nwfilters()
status = conn.get_status() status = conn.get_status()
autostart = conn.get_autostart() autostart = conn.get_autostart()
vcpu = conn.get_vcpu() vcpu = conn.get_vcpu()
@ -364,6 +262,7 @@ def instance(request, compute_id, vname):
else: else:
media_iso = [] media_iso = []
networks = conn.get_net_device() networks = conn.get_net_device()
vcpu_range = conn.get_max_cpus() vcpu_range = conn.get_max_cpus()
memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384] memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384]
if memory not in memory_range: if memory not in memory_range:
@ -377,7 +276,7 @@ def instance(request, compute_id, vname):
console_port = conn.get_console_port() console_port = conn.get_console_port()
console_keymap = conn.get_console_keymap() console_keymap = conn.get_console_keymap()
console_listen_address = conn.get_console_listen_addr() console_listen_address = conn.get_console_listen_addr()
snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k:k['date']) snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k: k['date'])
inst_xml = conn._XMLDesc(VIR_DOMAIN_XML_SECURE) inst_xml = conn._XMLDesc(VIR_DOMAIN_XML_SECURE)
has_managed_save_image = conn.get_managed_save_image() has_managed_save_image = conn.get_managed_save_image()
console_passwd = conn.get_console_passwd() console_passwd = conn.get_console_passwd()
@ -387,6 +286,7 @@ def instance(request, compute_id, vname):
cache_modes = sorted(conn.get_cache_modes().items()) cache_modes = sorted(conn.get_cache_modes().items())
default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE
default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT
default_owner = settings.INSTANCE_VOLUME_DEFAULT_OWNER
formats = conn.get_image_formats() formats = conn.get_image_formats()
busses = conn.get_busses() busses = conn.get_busses()
@ -450,7 +350,8 @@ def instance(request, compute_id, vname):
instance.delete() instance.delete()
try: try:
del_userinstance = UserInstance.objects.filter(instance__compute_id=compute_id, instance__name=vname) del_userinstance = UserInstance.objects.filter(instance__compute_id=compute_id,
instance__name=vname)
del_userinstance.delete() del_userinstance.delete()
except UserInstance.DoesNotExist: except UserInstance.DoesNotExist:
pass pass
@ -458,7 +359,7 @@ def instance(request, compute_id, vname):
msg = _("Destroy") msg = _("Destroy")
addlogmsg(request.user.username, instance_name, msg) addlogmsg(request.user.username, instance_name, msg)
return HttpResponseRedirect(reverse('instances')) return HttpResponseRedirect(reverse('allinstances'))
if 'rootpasswd' in request.POST: if 'rootpasswd' in request.POST:
passwd = request.POST.get('passwd', '') passwd = request.POST.get('passwd', '')
@ -504,7 +405,8 @@ def instance(request, compute_id, vname):
msg = _("Please shutdown down your instance and then try again") msg = _("Please shutdown down your instance and then try again")
error_messages.append(msg) error_messages.append(msg)
if 'resize' in request.POST and (request.user.is_superuser or request.user.is_staff or userinstance.is_change): if 'resize' in request.POST and (
request.user.is_superuser or request.user.is_staff or userinstance.is_change):
new_vcpu = request.POST.get('vcpu', '') new_vcpu = request.POST.get('vcpu', '')
new_cur_vcpu = request.POST.get('cur_vcpu', '') new_cur_vcpu = request.POST.get('cur_vcpu', '')
new_memory = request.POST.get('memory', '') new_memory = request.POST.get('memory', '')
@ -518,13 +420,13 @@ def instance(request, compute_id, vname):
disks_new = [] disks_new = []
for disk in disks: for disk in disks:
input_disk_size = filesizefstr(request.POST.get('disk_size_' + disk['dev'], '')) input_disk_size = filesizefstr(request.POST.get('disk_size_' + disk['dev'], ''))
if input_disk_size > disk['size']+(64<<20): if input_disk_size > disk['size'] + (64 << 20):
disk['size_new'] = input_disk_size disk['size_new'] = input_disk_size
disks_new.append(disk) disks_new.append(disk)
disk_sum = sum([disk['size']>>30 for disk in disks_new]) disk_sum = sum([disk['size'] >> 30 for disk in disks_new])
disk_new_sum = sum([disk['size_new']>>30 for disk in disks_new]) disk_new_sum = sum([disk['size_new'] >> 30 for disk in disks_new])
quota_msg = check_user_quota(0, int(new_vcpu)-vcpu, int(new_memory)-memory, disk_new_sum-disk_sum) quota_msg = check_user_quota(0, int(new_vcpu) - vcpu, int(new_memory) - memory, disk_new_sum - disk_sum)
if not request.user.is_superuser and quota_msg: if not request.user.is_superuser and quota_msg:
msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name)) msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name))
error_messages.append(msg) error_messages.append(msg)
else: else:
@ -539,19 +441,19 @@ def instance(request, compute_id, vname):
if 'addvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): if 'addvolume' in request.POST and (request.user.is_superuser or userinstance.is_change):
connCreate = wvmCreate(compute.hostname, connCreate = wvmCreate(compute.hostname,
compute.login, compute.login,
compute.password, compute.password,
compute.type) compute.type)
storage = request.POST.get('storage', '') storage = request.POST.get('storage', '')
name = request.POST.get('name', '') name = request.POST.get('name', '')
format = request.POST.get('format', '') format = request.POST.get('format', default_format)
size = request.POST.get('size', 0) size = request.POST.get('size', 0)
meta_prealloc = request.POST.get('meta_prealloc', False) meta_prealloc = request.POST.get('meta_prealloc', False)
bus = request.POST.get('bus', '') bus = request.POST.get('bus', default_bus)
cache = request.POST.get('cache', '') cache = request.POST.get('cache', default_cache)
target = get_new_disk_dev(disks, bus) 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) conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus)
msg = _('Attach new disk') msg = _('Attach new disk')
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
@ -667,7 +569,7 @@ def instance(request, compute_id, vname):
msg = _("Set VNC type") msg = _("Set VNC type")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#vncsettings') return HttpResponseRedirect(request.get_full_path() + '#vncsettings')
if 'set_console_listen_address' in request.POST: if 'set_console_listen_address' in request.POST:
console_type = request.POST.get('console_listen_address', '') console_type = request.POST.get('console_listen_address', '')
conn.set_console_listen_addr(console_type) conn.set_console_listen_addr(console_type)
@ -689,6 +591,7 @@ def instance(request, compute_id, vname):
return HttpResponseRedirect(reverse('instance', args=[new_compute.id, vname])) return HttpResponseRedirect(reverse('instance', args=[new_compute.id, vname]))
if 'change_network' in request.POST: if 'change_network' in request.POST:
msg = _("Change network")
network_data = {} network_data = {}
for post in request.POST: for post in request.POST:
@ -700,31 +603,43 @@ def instance(request, compute_id, vname):
network_data[post] = request.POST.get(post, '') network_data[post] = request.POST.get(post, '')
conn.change_network(network_data) conn.change_network(network_data)
msg = _("Edit network")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
msg = _("Network Devices are changed. Please reboot instance to activate.") msg = _("Network Devices are changed. Please reboot instance to activate.")
messages.success(request, msg) if conn.get_status() != 5: messages.success(request, msg)
return HttpResponseRedirect(request.get_full_path() + '#network') return HttpResponseRedirect(request.get_full_path() + '#network')
if 'add_network' in request.POST: if 'add_network' in request.POST:
msg = _("Add network")
mac = request.POST.get('add-net-mac') 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')) (source, source_type) = get_network_tuple(request.POST.get('add-net-network'))
conn.add_network(mac, source, source_type) conn.add_network(mac, source, source_type, nwfilter=nwfilter)
msg = _("Edit network")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
msg = _("Network Devices are changed. Please reboot instance to activate.") msg = _("Network Device is added. Please reboot instance to activate.")
messages.success(request, msg) if conn.get_status() != 5: messages.success(request, msg)
return HttpResponseRedirect(request.get_full_path() + '#network') 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)
msg = _("Network Device is deleted. Please reboot instance to activate.")
if conn.get_status() != 5: messages.success(request, msg)
return HttpResponseRedirect(request.get_full_path() + '#network')
if 'add_owner' in request.POST: if 'add_owner' in request.POST:
user_id = int(request.POST.get('user_id', '')) user_id = int(request.POST.get('user_id', ''))
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER: if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
check_inst = UserInstance.objects.filter(instance=instance, user_id=user_id) check_inst = UserInstance.objects.filter(instance=instance, user_id=user_id)
else: else:
check_inst = UserInstance.objects.filter(instance=instance) check_inst = UserInstance.objects.filter(instance=instance)
if check_inst: if check_inst:
msg = _("Owner already added") msg = _("Owner already added")
error_messages.append(msg) error_messages.append(msg)
@ -743,19 +658,18 @@ def instance(request, compute_id, vname):
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#users') return HttpResponseRedirect(request.get_full_path() + '#users')
if request.user.is_superuser or request.user.userattributes.can_clone_instances: if request.user.is_superuser or request.user.userattributes.can_clone_instances:
if 'clone' in request.POST: if 'clone' in request.POST:
clone_data = {} clone_data = {}
clone_data['name'] = request.POST.get('name', '') clone_data['name'] = request.POST.get('name', '')
disk_sum = sum([disk['size']>>30 for disk in disks]) disk_sum = sum([disk['size'] >> 30 for disk in disks])
quota_msg = check_user_quota(1, vcpu, memory, disk_sum) quota_msg = check_user_quota(1, vcpu, memory, disk_sum)
check_instance = Instance.objects.filter(name=clone_data['name']) check_instance = Instance.objects.filter(name=clone_data['name'])
for post in request.POST: for post in request.POST:
clone_data[post] = request.POST.get(post, '').strip() clone_data[post] = request.POST.get(post, '').strip()
if clone_instance_auto_name and not clone_data['name']: if clone_instance_auto_name and not clone_data['name']:
auto_vname = clone_free_names[0] auto_vname = clone_free_names[0]
clone_data['name'] = auto_vname clone_data['name'] = auto_vname
@ -764,7 +678,7 @@ def instance(request, compute_id, vname):
disk_dev = "disk-{}".format(disk['dev']) disk_dev = "disk-{}".format(disk['dev'])
disk_name = get_clone_disk_name(disk, vname, auto_vname) disk_name = get_clone_disk_name(disk, vname, auto_vname)
clone_data[disk_dev] = disk_name clone_data[disk_dev] = disk_name
if not request.user.is_superuser and quota_msg: if not request.user.is_superuser and quota_msg:
msg = _("User %s quota reached, cannot create '%s'!" % (quota_msg, clone_data['name'])) msg = _("User %s quota reached, cannot create '%s'!" % (quota_msg, clone_data['name']))
error_messages.append(msg) error_messages.append(msg)
@ -774,14 +688,16 @@ def instance(request, compute_id, vname):
elif not re.match(r'^[a-zA-Z0-9-]+$', clone_data['name']): elif not re.match(r'^[a-zA-Z0-9-]+$', clone_data['name']):
msg = _("Instance name '%s' contains invalid characters!" % clone_data['name']) msg = _("Instance name '%s' contains invalid characters!" % clone_data['name'])
error_messages.append(msg) error_messages.append(msg)
elif not re.match(r'^([0-9A-F]{2})(\:?[0-9A-F]{2}){5}$', clone_data['clone-net-mac-0'], re.IGNORECASE): elif not re.match(r'^([0-9A-F]{2})(\:?[0-9A-F]{2}){5}$', clone_data['clone-net-mac-0'],
re.IGNORECASE):
msg = _("Instance mac '%s' invalid format!" % clone_data['clone-net-mac-0']) msg = _("Instance mac '%s' invalid format!" % clone_data['clone-net-mac-0'])
error_messages.append(msg) error_messages.append(msg)
else: else:
new_uuid = conn.clone_instance(clone_data) new_uuid = conn.clone_instance(clone_data)
new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid) new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid)
new_instance.save() new_instance.save()
userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id, is_delete=True) userinstance = UserInstance(instance_id=new_instance.id, user_id=request.user.id,
is_delete=True)
userinstance.save() userinstance.save()
msg = _("Clone of '%s'" % instance.name) msg = _("Clone of '%s'" % instance.name)
@ -789,18 +705,19 @@ def instance(request, compute_id, vname):
if settings.CLONE_INSTANCE_AUTO_MIGRATE: if settings.CLONE_INSTANCE_AUTO_MIGRATE:
new_compute = Compute.objects.order_by('?').first() new_compute = Compute.objects.order_by('?').first()
migrate_instance(new_compute, new_instance, xml_del=True, offline=True) migrate_instance(new_compute, new_instance, xml_del=True, offline=True)
return HttpResponseRedirect(reverse('instance', args=[new_instance.compute.id, new_instance.name])) return HttpResponseRedirect(
reverse('instance', args=[new_instance.compute.id, new_instance.name]))
if 'change_options' in request.POST: if 'change_options' in request.POST:
instance.is_template = request.POST.get('is_template', False) instance.is_template = request.POST.get('is_template', False)
instance.save() instance.save()
options = {} options = {}
for post in request.POST: for post in request.POST:
if post in ['title', 'description']: if post in ['title', 'description']:
options[post] = request.POST.get(post, '') options[post] = request.POST.get(post, '')
conn.set_options(options) conn.set_options(options)
msg = _("Edit options") msg = _("Edit options")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#options') return HttpResponseRedirect(request.get_full_path() + '#options')
@ -839,6 +756,154 @@ def inst_status(request, compute_id, vname):
return response return response
def get_host_instances(request,comp):
def refresh_instance_database(comp, inst_name, info):
def get_userinstances_info(instance):
info = {}
uis = UserInstance.objects.filter(instance=instance)
info['count'] = uis.count()
if info['count'] > 0:
info['first_user'] = uis[0]
else:
info['first_user'] = None
return info
instances = Instance.objects.filter(name=inst_name)
if instances.count() > 1:
for i in instances:
user_instances_count = UserInstance.objects.filter(instance=i).count()
if user_instances_count == 0:
addlogmsg(request.user.username, i.name, _("Deleting due to multiple records."))
i.delete()
try:
inst_on_db = Instance.objects.get(compute_id=comp["id"], name=inst_name)
if inst_on_db.uuid != info['uuid']:
inst_on_db.save()
all_host_vms[comp["id"],
comp["name"],
comp["status"],
comp["cpu"],
comp["mem_size"],
comp["mem_perc"]][inst_name]['is_template'] = inst_on_db.is_template
all_host_vms[comp["id"],
comp["name"],
comp["status"],
comp["cpu"],
comp["mem_size"],
comp["mem_perc"]][inst_name]['userinstances'] = get_userinstances_info(inst_on_db)
except Instance.DoesNotExist:
inst_on_db = Instance(compute_id=comp["id"], name=inst_name, uuid=info['uuid'])
inst_on_db.save()
all_host_vms = {}
status = connection_manager.host_is_up(comp.type, comp.hostname)
if status:
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
comp_node_info = conn.get_node_info()
comp_mem = conn.get_memory_usage()
comp_instances = conn.get_host_instances(True)
if comp_instances:
comp_info = {
"id": comp.id,
"name": comp.name,
"status": status,
"cpu": comp_node_info[3],
"mem_size": comp_node_info[2],
"mem_perc": comp_mem['percent']
}
all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"],
comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances
for vm, info in comp_instances.items():
refresh_instance_database(comp_info, vm, info)
conn.close()
return all_host_vms
def get_user_instances(request):
all_user_vms = {}
user_instances = UserInstance.objects.filter(user_id=request.user.id)
for usr_inst in user_instances:
if connection_manager.host_is_up(usr_inst.instance.compute.type,
usr_inst.instance.compute.hostname):
conn = wvmHostDetails(usr_inst.instance.compute,
usr_inst.instance.compute.login,
usr_inst.instance.compute.password,
usr_inst.instance.compute.type)
all_user_vms[usr_inst] = conn.get_user_instances(usr_inst.instance.name)
all_user_vms[usr_inst].update({'compute_id': usr_inst.instance.compute.id})
return all_user_vms
def instances_actions(request):
name = request.POST.get('name', '')
compute_id = request.POST.get('compute_id', '')
instance = Instance.objects.get(compute_id=compute_id, name=name)
conn = wvmInstances(instance.compute.hostname,
instance.compute.login,
instance.compute.password,
instance.compute.type)
if 'poweron' in request.POST:
msg = _("Power On")
addlogmsg(request.user.username, instance.name, msg)
conn.start(name)
return HttpResponseRedirect(request.get_full_path())
if 'poweroff' in request.POST:
msg = _("Power Off")
addlogmsg(request.user.username, instance.name, msg)
conn.shutdown(name)
return HttpResponseRedirect(request.get_full_path())
if 'powercycle' in request.POST:
msg = _("Power Cycle")
conn.force_shutdown(name)
conn.start(name)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path())
if 'getvvfile' in request.POST:
msg = _("Send console.vv file")
addlogmsg(request.user.username, instance.name, msg)
response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None,
charset='utf-8')
response.writelines('[virt-viewer]\n')
response.writelines('type=' + conn.graphics_type(name) + '\n')
response.writelines('host=' + conn.graphics_listen(name) + '\n')
response.writelines('port=' + conn.graphics_port(name) + '\n')
response.writelines('title=' + conn.domain_name(name) + '\n')
response.writelines('password=' + conn.graphics_passwd(name) + '\n')
response.writelines('enable-usbredir=1\n')
response.writelines('disable-effects=all\n')
response.writelines('secure-attention=ctrl+alt+ins\n')
response.writelines('release-cursor=ctrl+alt\n')
response.writelines('fullscreen=1\n')
response.writelines('delete-this-file=1\n')
response['Content-Disposition'] = 'attachment; filename="console.vv"'
return response
if request.user.is_superuser:
if 'suspend' in request.POST:
msg = _("Suspend")
addlogmsg(request.user.username, instance.name, msg)
conn.suspend(name)
return HttpResponseRedirect(request.get_full_path())
if 'resume' in request.POST:
msg = _("Resume")
addlogmsg(request.user.username, instance.name, msg)
conn.resume(name)
return HttpResponseRedirect(request.get_full_path())
@login_required @login_required
def inst_graph(request, compute_id, vname): def inst_graph(request, compute_id, vname):
""" """

View file

@ -10,6 +10,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -19,6 +22,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -12,6 +12,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -21,6 +24,9 @@
<li> <li>
<i class="fa fa-wifi"></i> {% trans "Interfaces" %} <i class="fa fa-wifi"></i> {% trans "Interfaces" %}
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -10,6 +10,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -19,6 +22,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -11,6 +11,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -20,6 +23,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

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,223 @@
{% 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-server"></i> <a href="{% url '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> <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 '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

@ -15,6 +15,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -24,6 +27,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> {% trans "Secrets" %} <i class="fa fa-key"></i> {% trans "Secrets" %}
</li> </li>

4
static/css/font-awesome.min.css vendored Normal file → Executable file

File diff suppressed because one or more lines are too long

BIN
static/fonts/FontAwesome.otf Normal file → Executable file

Binary file not shown.

BIN
static/fonts/fontawesome-webfont.eot Normal file → Executable file

Binary file not shown.

3230
static/fonts/fontawesome-webfont.svg Normal file → Executable file

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 434 KiB

Before After
Before After

BIN
static/fonts/fontawesome-webfont.ttf Normal file → Executable file

Binary file not shown.

BIN
static/fonts/fontawesome-webfont.woff Normal file → Executable file

Binary file not shown.

BIN
static/fonts/fontawesome-webfont.woff2 Normal file → Executable file

Binary file not shown.

View file

@ -16,6 +16,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a> <i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li> </li>
@ -25,6 +28,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -11,6 +11,9 @@
<li class="active"> <li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a> <i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li> </li>
<li>
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
</li>
<li> <li>
<i class="fa fa-hdd-o"></i> {% trans "Storages" %} <i class="fa fa-hdd-o"></i> {% trans "Storages" %}
</li> </li>
@ -20,6 +23,9 @@
<li> <li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a> <i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li> </li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li> <li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a> <i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li> </li>

View file

@ -155,7 +155,7 @@ def storage(request, compute_id, pool):
meta_prealloc = True meta_prealloc = True
try: try:
conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc) 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()) return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)
@ -167,7 +167,7 @@ def storage(request, compute_id, pool):
try: try:
vol = conn.get_volume(volname) vol = conn.get_volume(volname)
vol.delete(0) 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()) return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err.message) error_messages.append(lib_err.message)
@ -197,8 +197,7 @@ def storage(request, compute_id, pool):
format = None format = None
try: try:
conn.clone_volume(data['image'], data['name'], format, meta_prealloc) conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
messages.success(request, _("{} image cloned as {} successfully".format(data['image'], messages.success(request, _("{} image cloned as {} successfully".format(data['image'], data['name'] + ".img")))
data['name'] + ".img")))
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)

View file

@ -15,7 +15,7 @@
<div id="navbar" class="navbar-collapse collapse"> <div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li {% class_active request "^/instance" %}> <li {% class_active request "^/instance" %}>
<a href="{% url 'instances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a> <a href="{% url 'allinstances' %}"><i class="fa fa-fw fa-desktop"></i> {% trans "Instances" %}</a>
</li> </li>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<li {% class_active request "^/compute" %}{% class_active request "^/create" %}> <li {% class_active request "^/compute" %}{% class_active request "^/create" %}>

View file

@ -389,6 +389,12 @@ class wvmConnect(object):
interface.append(inface) interface.append(inface)
return interface return interface
def get_nwfilters(self):
nwfilters = []
for nwfilter in self.wvm.listNWFilters():
nwfilters.append(nwfilter)
return nwfilters
def get_cache_modes(self): def get_cache_modes(self):
"""Get cache available modes""" """Get cache available modes"""
return { return {
@ -443,7 +449,6 @@ class wvmConnect(object):
def get_video(self): def get_video(self):
""" Get available graphics video types """ """ Get available graphics video types """
def get_video_list(ctx): def get_video_list(ctx):
result = [] result = []
for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'): for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'):
@ -470,6 +475,9 @@ class wvmConnect(object):
def get_network(self, net): def get_network(self, net):
return self.wvm.networkLookupByName(net) return self.wvm.networkLookupByName(net)
def get_nwfilter(self, name):
return self.wvm.nwfilterLookupByName(name)
def get_instance(self, name): def get_instance(self, name):
return self.wvm.lookupByName(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") title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
return title if title else '' 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): def get_description(self):
description = util.get_xml_path(self._XMLDesc(0), "/domain/description") description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
return description if description else '' return description if description else ''
@ -219,12 +232,13 @@ class wvmInstance(wvmConnect):
mac_host = net.xpath('mac/@address')[0] mac_host = net.xpath('mac/@address')[0]
network_host = net.xpath('source/@network|source/@bridge|source/@dev')[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] 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: try:
net = self.get_network(network_host) net = self.get_network(network_host)
ip = get_mac_ipaddr(net, mac_host) ip = get_mac_ipaddr(net, mac_host)
except libvirtError as e: except libvirtError as e:
ip = None 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 result
return util.get_xml_path(self._XMLDesc(0), func=networks) return util.get_xml_path(self._XMLDesc(0), func=networks)
@ -487,8 +501,7 @@ class wvmInstance(wvmConnect):
return self._defineXML(newxml) return self._defineXML(newxml)
def get_console_socket(self): def get_console_socket(self):
socket = util.get_xml_path(self._XMLDesc(0), socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket")
"/domain/devices/graphics/@socket")
return socket return socket
def get_console_type(self): def get_console_type(self):
@ -700,13 +713,11 @@ class wvmInstance(wvmConnect):
source_file = elm.get('file') source_file = elm.get('file')
if source_file: if source_file:
clone_dev_path.append(source_file) clone_dev_path.append(source_file)
clone_path = os.path.join(os.path.dirname(source_file), clone_path = os.path.join(os.path.dirname(source_file), target_file)
target_file)
elm.set('file', clone_path) elm.set('file', clone_path)
vol = self.get_volume_by_path(source_file) vol = self.get_volume_by_path(source_file)
vol_format = util.get_xml_path(vol.XMLDesc(0), vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
"/volume/target/format/@type")
if vol_format == 'qcow2' and meta_prealloc: if vol_format == 'qcow2' and meta_prealloc:
meta_prealloc = True meta_prealloc = True
@ -729,8 +740,7 @@ class wvmInstance(wvmConnect):
elm.set('name', clone_name) elm.set('name', clone_name)
vol = self.get_volume_by_path(source_name) vol = self.get_volume_by_path(source_name)
vol_format = util.get_xml_path(vol.XMLDesc(0), vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
"/volume/target/format/@type")
vol_clone_xml = """ vol_clone_xml = """
<volume type='network'> <volume type='network'>
@ -776,7 +786,7 @@ class wvmInstance(wvmConnect):
bridge_name = net.bridgeName() bridge_name = net.bridgeName()
return bridge_name 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)) tree = ElementTree.fromstring(self._XMLDesc(0))
bridge_name = self.get_bridge_name(source, source_type) bridge_name = self.get_bridge_name(source, source_type)
xml_interface = """ xml_interface = """
@ -784,8 +794,13 @@ class wvmInstance(wvmConnect):
<mac address='%s'/> <mac address='%s'/>
<source bridge='%s'/> <source bridge='%s'/>
<model type='%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: if self.get_status() == 5:
devices = tree.find('devices') devices = tree.find('devices')
elm_interface = ElementTree.fromstring(xml_interface) elm_interface = ElementTree.fromstring(xml_interface)
@ -793,20 +808,42 @@ class wvmInstance(wvmConnect):
xmldom = ElementTree.tostring(tree) xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom) 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): def change_network(self, network_data):
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE) xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml) tree = ElementTree.fromstring(xml)
for num, interface in enumerate(tree.findall('devices/interface')): for num, interface in enumerate(tree.findall('devices/interface')):
net_source = network_data['net-source-' + str(num)] net_source = network_data['net-source-' + str(num)]
net_source_type = network_data['net-source-' + str(num) + '-type'] net_source_type = network_data['net-source-' + str(num) + '-type']
net_mac = network_data['net-mac-' + str(num)] 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) bridge_name = self.get_bridge_name(net_source, net_source_type)
if interface.get('type') == 'bridge': if interface.get('type') == 'bridge':
source = interface.find('mac') source = interface.find('mac')
source.set('address', net_mac) source.set('address', net_mac)
source = interface.find('source') source = interface.find('source')
source.set('bridge', bridge_name) 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) new_xml = ElementTree.tostring(tree)
self._defineXML(new_xml) 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 import util
from vrtManager.connection import wvmConnect from vrtManager.connection import wvmConnect
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
class wvmStorages(wvmConnect): class wvmStorages(wvmConnect):
@ -205,7 +206,7 @@ class wvmStorage(wvmConnect):
) )
return vol_list 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 size = int(size) * 1073741824
storage_type = self.get_type() storage_type = self.get_type()
alloc = size alloc = size
@ -222,8 +223,8 @@ class wvmStorage(wvmConnect):
<target> <target>
<format type='%s'/> <format type='%s'/>
<permissions> <permissions>
<owner>107</owner> <owner>%s</owner>
<group>107</group> <group>%s</group>
<mode>0644</mode> <mode>0644</mode>
<label>virt_image_t</label> <label>virt_image_t</label>
</permissions> </permissions>
@ -232,10 +233,10 @@ class wvmStorage(wvmConnect):
<lazy_refcounts/> <lazy_refcounts/>
</features> </features>
</target> </target>
</volume>""" % (name, size, alloc, vol_fmt) </volume>""" % (name, size, alloc, vol_fmt, owner['uid'], owner['guid'])
self._createXML(xml, metadata) 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() storage_type = self.get_type()
if storage_type == 'dir': if storage_type == 'dir':
target_file += '.img' target_file += '.img'
@ -250,8 +251,8 @@ class wvmStorage(wvmConnect):
<target> <target>
<format type='%s'/> <format type='%s'/>
<permissions> <permissions>
<owner>107</owner> <owner>%s</owner>
<group>107</group> <group>%s</group>
<mode>0644</mode> <mode>0644</mode>
<label>virt_image_t</label> <label>virt_image_t</label>
</permissions> </permissions>
@ -260,5 +261,5 @@ class wvmStorage(wvmConnect):
<lazy_refcounts/> <lazy_refcounts/>
</features> </features>
</target> </target>
</volume>""" % (target_file, vol_fmt) </volume>""" % (target_file, vol_fmt, owner['uid'],owner['guid'])
self._createXMLFrom(xml, vol, metadata) self._createXMLFrom(xml, vol, metadata)

View file

@ -19,11 +19,13 @@ INSTALLED_APPS = (
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'dashboards',
'computes', 'computes',
'console', 'console',
'networks', 'networks',
'storages', 'storages',
'interfaces', 'interfaces',
'nwfilters',
'instances', 'instances',
'secrets', 'secrets',
'logs', 'logs',
@ -151,3 +153,5 @@ VIEW_INSTANCES_LIST_STYLE = 'grouped'
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2' INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio' INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync' INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
INSTANCE_VOLUME_DEFAULT_OWNER = {'uid': 0, 'guid': 0}

View file

@ -1,32 +1,18 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from instances.views import instances, instance, index from instances.views import index
from storages.views import storages, storage
from networks.views import networks, network
from secrets.views import secrets
from create.views import create_instance
from interfaces.views import interfaces, interface
from console.views import console from console.views import console
# from django.contrib import admin # from django.contrib import admin
urlpatterns = [ urlpatterns = [
url(r'^$', index, name='index'), url(r'^$', index, name='index'),
url(r'^instances/$', instances, name='instances'),
url(r'^instance/', include('instances.urls')), url(r'^instances/', include('instances.urls')),
url(r'^accounts/', include('accounts.urls')), url(r'^accounts/', include('accounts.urls')),
url(r'^computes/', include('computes.urls')), url(r'^computes/', include('computes.urls')),
url(r'^logs/', include('logs.urls')), url(r'^logs/', include('logs.urls')),
url(r'^datasource/', include('datasource.urls')), url(r'^datasource/', include('datasource.urls')),
url(r'^compute/(?P<compute_id>[0-9]+)/storages/$', storages, name='storages'),
url(r'^compute/(?P<compute_id>[0-9]+)/storage/(?P<pool>[\w\-\.\/]+)/$', storage, name='storage'),
url(r'^compute/(?P<compute_id>[0-9]+)/networks/$', networks, name='networks'),
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]+)/secrets/$', secrets, name='secrets'),
url(r'^compute/(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
url(r'^console/$', console, name='console'), url(r'^console/$', console, name='console'),
# (r'^admin/', include(admin.site.urls)), # (r'^admin/', include(admin.site.urls)),