1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 23:25:24 +00:00

Merge pull request #197 from catborise/master

NWFilters Addition & Instances Tab & Other Fixes
This commit is contained in:
Anatoliy Guskov 2018-10-30 09:08:46 +02:00 committed by GitHub
commit cd940c99e5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 5249 additions and 1545 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>
@ -43,7 +49,7 @@
<p>{% trans "Connection" %}</p> <p>{% trans "Connection" %}</p>
<p>{% trans "Details" %}</p> <p>{% trans "Details" %}</p>
</div> </div>
<div class="col-xs-8 col-sm-7"> <div class="col-xs-8 col-sm-9">
<p>{{ hostname }}</p> <p>{{ hostname }}</p>
<p>{% for arch, hpv in hypervisor.items %} <p>{% for arch, hpv in hypervisor.items %}
<span class="glyphicon glyphicon-chevron-right"></span> <span class="glyphicon glyphicon-chevron-right"></span>

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

@ -156,7 +156,7 @@ def overview(request, compute_id):
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info() hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
hypervisor = conn.hypervisor_type() hypervisor = conn.hypervisor_type()
mem_usage = conn.get_memory_usage() mem_usage = conn.get_memory_usage()
emulator = conn.emulator() emulator = conn.get_emulator(host_arch)
conn.close() conn.close()
except libvirtError as lib_err: except libvirtError as lib_err:
error_messages.append(lib_err) error_messages.append(lib_err)

View file

@ -89,7 +89,7 @@
</ul> </ul>
</div> </div>
</div> </div>
/nav> </nav>
<div id='main_container' class="container"> <div id='main_container' class="container">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>

View file

@ -100,6 +100,9 @@
host = document.getElementById("host").value; host = document.getElementById("host").value;
port = document.getElementById("port").value; port = document.getElementById("port").value;
if (window.location.protocol == 'https:') {
scheme = "wss://";
}
password = document.getElementById("password").value; password = document.getElementById("password").value;

View file

@ -108,7 +108,7 @@
function connect() function connect()
{ {
var host, port, password, scheme = "ws://", uri; var host, port, password, scheme = "ws://", uri;
console.log('>> connect');
// By default, use the host and port of server that served this file // By default, use the host and port of server that served this file
//host = spice_query_var('host', window.location.hostname); //host = spice_query_var('host', window.location.hostname);
host = '{{ ws_host| safe }}'; host = '{{ ws_host| safe }}';
@ -170,7 +170,7 @@
disconnect(); disconnect();
} }
console.log('<< connect')
} }
function disconnect() function disconnect()

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

@ -9,7 +9,6 @@
<!-- Page Heading --> <!-- Page Heading -->
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
{% include 'create_flav_block.html' %}
<h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1> <h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1>
</div> </div>
</div> </div>
@ -17,281 +16,35 @@
{% include 'errors_block.html' %} {% include 'errors_block.html' %}
{% include 'pleasewaitdialog.html' %} {% include 'pleasewaitdialog.html' %}
<div class="row"> <div class="row" id="max-width-page">
<div class="col-lg-12"> <div class="col-lg-12">
<p> <div role="tabpanel">
<a class="btn btn-success" data-toggle="collapse" href="#addCustom" aria-expanded="false" aria-controls="addCustom"> <!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#flavor" aria-controls="flavor" role="tab" data-toggle="tab">
{% trans "Flavor" %}
</a>
</li>
<li role="presentation">
<a href="#addCustom" aria-controls="addCustom" role="tab" data-toggle="tab">
{% trans "Custom" %} {% trans "Custom" %}
</a> </a>
<a class="btn btn-success" data-toggle="collapse" href="#addFromTemp" aria-expanded="false" aria-controls="addFromTemp"> </li>
<li role="presentation">
<a href="#addFromTemp" aria-controls="addFromTemp" role="tab" data-toggle="tab">
{% trans "Template" %} {% trans "Template" %}
</a> </a>
<a class="btn btn-success" data-toggle="collapse" href="#addFromXML" aria-expanded="false" aria-controls="addFromXML"> </li>
<li role="presentation">
<a href="#addFromXML" aria-controls="addFromXML" role="tab" data-toggle="tab">
{% trans "XML" %} {% trans "XML" %}
</a> </a>
</p> </li>
<div class="collapse" id="addCustom">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<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="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required pattern="[0-9]">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="host_model" value="true" checked>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="memory" value="512" maxlength="5" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "MB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<div class="col-sm-6">
<ul id="img-list">
<!-- populated from javascript -->
</ul> </ul>
<input id="images" name="images" type="hidden" value=""/> <!-- Tab panes -->
<select id="image-control" name="image-control" class="form-control" multiple="multiple"> <div class="tab-content">
{% if get_images %} <div role="tabpanel" class="tab-pane tab-pane-bordered active" id="flavor">
{% for name in get_images %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
</div>
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
<div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %}
<option value="{{ mode }}">{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<ul id="net-list">
<!-- populated from javascript -->
</ul>
<input id="networks" type="hidden" name="networks" value=""/>
<select id="network-control" name="network-control" class="form-control" multiple="multiple">
{% for network in networks %}
<option value="{{ network }}">{{ network }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
<option value="cirrus">cirrus</option>
{% endif %}
{% for video in videos %}
<option value="{{ video }}">{{ video }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
<div class="col-sm-6">
<select name="listener_addr" class="form-control">
{% for addr, label in listener_addr %}
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" onclick="showPleaseWaitDialog()" value="1">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</form>
</div>
</div>
<div class="collapse" id="addFromTemp">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<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="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required pattern="[0-9]">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="host_model" value="true" checked>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="memory" value="512" maxlength="5" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "MB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<div class="col-sm-6">
<select name="template" class="form-control">
{% if get_images %}
{% for name in get_images %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
</div>
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
<div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %}
<option value="{{ mode }}">{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<select name="networks" class="form-control">
{% for network in networks %}
<option value="{{ network }}">{{ network }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
<option value="cirrus">cirrus</option>
{% endif %}
{% for video in videos %}
<option value="{{ video }}">{{ video }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
<div class="col-sm-6">
<select name="listener_addr" class="form-control">
{% for addr, label in listener_addr %}
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" value="1" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</form>
</div>
</div>
<div class="collapse" id="addFromXML">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<input type="hidden" name="dom_xml"/>
<textarea id="editor"></textarea>
</div>
<button type="submit" class="btn btn-primary" name="create_xml" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
</form>
</div>
</div>
</div>
</div>
<div class="row">
{% if not flavors %} {% if not flavors %}
<div class="col-lg-12"> <div class="col-lg-12">
<div class="alert alert-warning alert-dismissable"> <div class="alert alert-warning alert-dismissable">
@ -300,7 +53,7 @@
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="col-lg-12"> {% include 'create_flav_block.html' %}
<h3 class="page-header">{% trans "Create from flavor" %}</h3> <h3 class="page-header">{% trans "Create from flavor" %}</h3>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-bordered table-hover"> <table class="table table-bordered table-hover">
@ -364,7 +117,8 @@
<div class="col-sm-6"> <div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="form-control"> <select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %} {% for mode, name in cache_modes %}
<option value="{{ mode }}">{% trans name %}</option> <option value="{{ mode }}" {% ifequal mode default_cache %}selected {% endifequal %}>
{% trans name %}</option>
{% endfor %} {% endfor %}
</select> </select>
</div> </div>
@ -379,6 +133,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">
@ -458,9 +223,301 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
{% endif %} {% endif %}
<div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addCustom">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<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="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required pattern="[0-9]">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="host_model" value="true" checked>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="memory" value="512" maxlength="5" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "MB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<div class="col-sm-6">
<ul id="img-list">
<!-- populated from javascript -->
</ul>
<input id="images" name="images" type="hidden" value=""/>
<select id="image-control" name="image-control" class="form-control" multiple="multiple">
{% if get_images %}
{% for name in get_images %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
</div>
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
<div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% ifequal mode default_cache %}selected {% endifequal %}>
{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<ul id="net-list">
<!-- populated from javascript -->
</ul>
<input id="networks" type="hidden" name="networks" value=""/>
<select id="network-control" name="network-control" class="form-control" multiple="multiple">
{% for network in networks %}
<option value="{{ network }}">{{ network }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
<div class="col-sm-6">
<select name="nwfilter" class="form-control">
<option value="">{% trans "None" %}</option>
{% for nwfilter in nwfilters %}
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
<option value="cirrus">cirrus</option>
{% endif %}
{% for video in videos %}
<option value="{{ video }}">{{ video }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
<div class="col-sm-6">
<select name="listener_addr" class="form-control">
{% for addr, label in listener_addr %}
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" onclick="showPleaseWaitDialog()" value="1">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</form>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromTemp">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<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="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required pattern="[0-9]">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="host_model" value="true" checked>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
<div class="col-sm-6">
<input type="text" class="form-control" name="memory" value="512" maxlength="5" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "MB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<div class="col-sm-6">
<select name="template" class="form-control">
{% if get_images %}
{% for name in get_images %}
<option value="{{ name }}">{{ name }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="meta_prealloc" title="Metadata preallocation" value="true">
</div>
<label class="col-lg-1 control-label">{% trans "Image" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD cache mode" %}</label>
<div class="col-sm-6">
<select id="cache_mode" name="cache_mode" class="form-control">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% ifequal mode default_cache %}selected {% endifequal %}>
{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<select name="networks" class="form-control">
{% for network in networks %}
<option value="{{ network }}">{{ network }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
<div class="col-sm-6">
<select name="nwfilter" class="form-control">
<option value="">{% trans "None" %}</option>
{% for nwfilter in nwfilters %}
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
<option value="cirrus">cirrus</option>
{% endif %}
{% for video in videos %}
<option value="{{ video }}">{{ video }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
<div class="col-sm-6">
<select name="listener_addr" class="form-control">
{% for addr, label in listener_addr %}
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
{% endfor %}
</select>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" value="1" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</form>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromXML">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<input type="hidden" name="dom_xml"/>
<textarea id="editor"></textarea>
</div>
<button type="submit" class="btn btn-primary" name="create_xml" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
</form>
</div>
<div class="clearfix"></div>
</div>
</div>
<!-- /Tab panes -->
</div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script src="{% static "js/bootstrap-multiselect.js" %}"></script> <script src="{% static "js/bootstrap-multiselect.js" %}"></script>

View file

@ -11,6 +11,7 @@ from vrtManager.create import wvmCreate
from vrtManager import util from vrtManager import util
from libvirt import libvirtError from libvirt import libvirtError
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_CACHE
from django.contrib import messages from django.contrib import messages
@login_required @login_required
@ -28,7 +29,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,9 +40,11 @@ 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())
default_cache = INSTANCE_VOLUME_DEFAULT_CACHE
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
mac_auto = util.randomMAC() mac_auto = util.randomMAC()
get_images = sorted(conn.get_storages_images()) get_images = sorted(conn.get_storages_images())
@ -99,6 +101,8 @@ def create_instance(request, compute_id):
if data['name'] in instances: if data['name'] in instances:
msg = _("A virtual machine with this name already exists") msg = _("A virtual machine with this name already exists")
error_messages.append(msg) error_messages.append(msg)
if Instance.objects.filter(name__exact=data['name']):
messages.warning(request,_("There is an instance with same name. Are you sure?"))
if not error_messages: if not error_messages:
if data['hdd_size']: if data['hdd_size']:
if not data['mac']: if not data['mac']:
@ -139,11 +143,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,159 @@
{% load i18n %}
{% if request.user.is_superuser %}
<a href="#addvol" type="button" class="btn btn-success pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
<!-- Modal pool -->
<div class="modal fade" id="addvol" tabindex="-1" role="dialog" aria-labelledby="addInstanceVolumeLabel" 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 Instance Volume" %}</h4>
</div>
<div class="modal-body">
<div class="row">
<ul class="nav nav-tabs">
<li role="presentation" class="active"><a href="#NewDisk" data-toggle="tab">{% trans 'New Disk' %}</a></li>
<li role="presentation"><a href="#ExistingDisk" data-toggle="tab">{% trans 'Existing Disk' %}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="NewDisk">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label>
<div class="col-sm-4">
<select name="storage" class="form-control image-format">
{% for storage in storages %}
<option value="{{ storage }}">{{ storage }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Name" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Format" %}</label>
<div class="col-sm-4">
<select name="format" class="form-control image-format">
{% for format in formats %}
<option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Size" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "GB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label>
<div class="col-sm-4">
<select name="bus" class="form-control image-format">
{% for bus in busses %}
<option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
<div class="col-sm-4">
<select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Metadata" %}</label>
<div class="col-sm-4">
<input type="checkbox" name="meta_prealloc" value="true">
</div>
</div>
<div class="modal-footer">
{% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="addnewvol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}
</div>
</form>
</div>
<div class="tab-pane" id="ExistingDisk">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="modal-body">
<p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label>
<div class="col-sm-4">
<div class="dropdown">
<button id="select_storage" class="btn btn-default dropdown-toggle form-control" type="button" data-toggle="dropdown">{% trans 'Select Pool...' %}
<span class="caret"></span></button>
<ul class="dropdown-menu">
{% for storage in storages %}
<li><a href="#" onclick="get_volumes({{ compute_id }}, '{{ storage }}')">{{ storage }}</a></li>
{% endfor %}
</ul>
<input id="selected_storage" name="selected_storage" hidden/>
</div>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Volume" %}</label>
<div class="col-sm-4">
<select id="vols" name="vols" class="form-control" disabled>
<option value="" selected>{% trans 'None' %}</option>
<!-- populate with javascript -->
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label>
<div class="col-sm-4">
<select name="bus" class="form-control image-format">
{% for bus in busses %}
<option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
<div class="col-sm-4">
<select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="modal-footer">
{% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="addexistingvol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}
</div>
</form>
</div>
</div>
</div> <!-- row -->
</div> <!-- /.modal-body -->
</div> <!-- /.modal-content -->
</div> <!-- /.modal-dialog -->
</div> <!-- /.modal -->
{% endif %}

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

@ -72,12 +72,14 @@
{% trans "Resize" %} {% trans "Resize" %}
</a> </a>
</li> </li>
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
<li role="presentation"> <li role="presentation">
<a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab"> <a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab">
<span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span> <span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span>
{% trans "Snapshots" %} {% trans "Snapshots" %}
</a> </a>
</li> </li>
{% endif %}
<li role="presentation"> <li role="presentation">
<a href="#settings" class="action-button" aria-controls="settings" role="tab" data-toggle="tab"> <a href="#settings" class="action-button" aria-controls="settings" role="tab" data-toggle="tab">
<span id="action-block" class="glyphicon glyphicon-cog" aria-hidden="true"></span> <span id="action-block" class="glyphicon glyphicon-cog" aria-hidden="true"></span>
@ -342,11 +344,6 @@
{% trans "Resize Instance" %} {% trans "Resize Instance" %}
</a> </a>
</li> </li>
<li role="presentation">
<a href="#addvolume" aria-controls="addvolume" role="tab" data-toggle="tab">
{% trans "Add New Volume" %}
</a>
</li>
</ul> </ul>
<!-- Tab panes --> <!-- Tab panes -->
<div class="tab-content"> <div class="tab-content">
@ -428,81 +425,7 @@
{% endif %} {% endif %}
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addvolume">
{% if request.user.is_superuser or userinstance.is_change %}
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label>
<div class="col-sm-4">
<select name="storage" class="form-control image-format">
{% for storage in storages %}
<option value="{{ storage }}">{{ storage }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Name" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Format" %}</label>
<div class="col-sm-4">
<select name="format" class="form-control image-format">
{% for format in formats %}
<option value="{{ format }}" {% if format == default_format %}selected{% endif %}>{% trans format %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Size" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+">
</div>
<label class="col-sm-1 control-label">{% trans "GB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label>
<div class="col-sm-4">
<select name="bus" class="form-control image-format">
{% for bus in busses %}
<option value="{{ bus }}" {% if bus == default_bus %}selected{% endif %}>{% trans bus %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
<div class="col-sm-4">
<select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %}
<option value="{{ mode }}" {% if mode == default_cache %}selected{% endif %}>{% trans name %}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Metadata" %}</label>
<div class="col-sm-4">
<input type="checkbox" name="meta_prealloc" value="true">
</div>
</div>
{% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="addvolume">{% trans "Add volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add volume" %}</button>
{% endifequal %}
</form>
{% else %}
{% trans "You don't have permission for resizing instance" %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add volume" %}</button>
{% endif %}
<div class="clearfix"></div>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -607,6 +530,11 @@
{% trans "Media" %} {% trans "Media" %}
</a> </a>
</li> </li>
<li role="presentation">
<a href="#disks" aria-controls="disks" role="tab" data-toggle="tab">
{% trans "Disk" %}
</a>
</li>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<li role="presentation"> <li role="presentation">
<a href="#autostart" aria-controls="autostart" role="tab" data-toggle="tab"> <a href="#autostart" aria-controls="autostart" role="tab" data-toggle="tab">
@ -614,6 +542,7 @@
</a> </a>
</li> </li>
{% endif %} {% endif %}
{% if request.user.is_superuser or userinstance.is_vnc %} {% if request.user.is_superuser or userinstance.is_vnc %}
<li role="presentation"> <li role="presentation">
<a href="#vncsettings" aria-controls="vncsettings" role="tab" data-toggle="tab"> <a href="#vncsettings" aria-controls="vncsettings" role="tab" data-toggle="tab">
@ -682,7 +611,7 @@
</select> </select>
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
{% if media_iso %} {% if media_iso and request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" name="mount_iso" value="{{ cd.dev }}" style="margin-top: 2px;">{% trans "Mount" %}</button> <button type="submit" class="btn btn-sm btn-success pull-left" name="mount_iso" value="{{ cd.dev }}" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% else %} {% else %}
<button class="btn btn-sm btn-success pull-left disabled" name="mount_iso" style="margin-top: 2px;">{% trans "Mount" %}</button> <button class="btn btn-sm btn-success pull-left disabled" name="mount_iso" style="margin-top: 2px;">{% trans "Mount" %}</button>
@ -694,7 +623,11 @@
</div> </div>
<div class="col-sm-2"> <div class="col-sm-2">
<input type="hidden" name="path" value="{{ cd.path }}"> <input type="hidden" name="path" value="{{ cd.path }}">
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
<button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button> <button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
{% endif %}
</div> </div>
{% endif %} {% endif %}
</div> </div>
@ -702,6 +635,73 @@
{% endfor %} {% endfor %}
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="disks">
<p style="font-weight:bold;">
{% trans "Instance Volumes" %}
{% include 'add_instance_volume.html' %}
</p>
<div class="col-xs-12 col-sm-12">
<table class="table table-hover">
<thead>
<th>{% trans "Device" %}</th>
<th>{% trans "Used" %}</th>
<th>{% trans "Capacity" %}</th>
<th>{% trans "Storage" %}</th>
<th>{% trans "Source" %}</th>
<th style="width:100px;">{% trans "Action" %}</th>
</thead>
<tbody>
{% for disk in disks %}
<tr>
<td>
<button type="submit" class="btn btn-sm btn-default"
name="details"
title="{% trans "Details" %}"
tabindex="0"
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>Bus:</strong> {{ disk.bus }} <br/> <strong>Format:</strong> {{ disk.format }}">
<i class="fa fa-info"></i>
</button>
{{ disk.dev }}
<br>
{{ disk.target }}
</td>
<td>{{ disk.used | filesizeformat}}</td>
<td>{{ disk.size | filesizeformat }}</td>
<td>{{ disk.storage }}</td>
<td>{{ disk.path }}</td>
<td>
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="path" value="{{ disk.path }}">
<input type="hidden" name="dev" value="{{ disk.dev }}">
{% ifequal status 5 %}
<button type="submit" class="btn btn-sm btn-default" name="detachvolume" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure to detach volume?" %}')">
<i class="fa fa-eject"></i>
</button>
<button type="submit" class="btn btn-sm btn-default" name="delvolume" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure to delete volume?" %}')">
<i class="fa fa-trash"></i>
</button>
{% else %}
<button class="btn btn-sm btn-default disabled" name="detachvolume" title="{% trans "Detach" %}" onclick="return confirm('{% trans "Are you sure to detach volume after shutdown?" %}')">
<i class="fa fa-eject"></i>
</button>
<button class="btn btn-sm btn-default disabled" name="delvolume" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure to delete after shutdown?" %}')">
<i class="fa fa-trash"></i>
</button>
{% endifequal %}
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="clearfix"></div>
</div>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart"> <div role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart">
<p>{% trans "Autostart your instance when host server is power on" %}</p> <p>{% trans "Autostart your instance when host server is power on" %}</p>
@ -836,36 +836,57 @@
{% 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>
<div class="col-sm-3"> <div class="form-group form-inline">
<select name="net-source-{{ forloop.counter0 }}" class="form-control" id="network_select" onchange="network_select_enable()"> <label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
{% for c_nets in compute_networks %} <input class="form-control" type="text" value="{{ network.nic }}" readonly/>
<option value="net:{{ c_nets }}" {% if c_nets == network.nic %}selected{% endif %}>Network {{ c_nets }}</option> <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 %} {% endfor %}
{% for c_iface in compute_interfaces %} {% for c_iface in compute_interfaces %}
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %}selected{% endif %}>Interface {{ c_iface }}</option> <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 %} {% endfor %}
</select> </select>
</div> </div>
</div> </div>
<div class="panel-footer">
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
</div>
</div>
{% endfor %} {% 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 %}
@ -926,7 +947,7 @@
<input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/> <input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
</div> </div>
{% ifequal disk.format 'qcow2' %} {% ifequal disk.format 'qcow2' %}
<label class="col-sm-2 control-label" style="font-weight:normal;margin-left:-35px;">Metadata</label> <label class="col-sm-2 control-label" style="font-weight:normal;margin-left:-35px;">{% trans 'Metadata' %}</label>
<div class="col-sm-1"> <div class="col-sm-1">
<input type="checkbox" name="meta-{{ disk.dev }}" value="true" style="margin-top: 10px;"> <input type="checkbox" name="meta-{{ disk.dev }}" value="true" style="margin-top: 10px;">
</div> </div>
@ -1221,6 +1242,42 @@
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script> <script src="{% static "js/ace.js" %}" type="text/javascript" charset="utf-8"></script>
<script>
function get_volumes(comp_id, pool) {
vol_url = "/computes/" + comp_id + "/storage/" + pool + "/?get_volumes";
var select = document.getElementById("vols");
while (select.options.length){
select.removeChild(select.options[0]);
}
var sto_drop = document.getElementById('select_storage');
sto_drop.value = pool;
sto_drop.innerHTML = pool + "<span class=\"caret\"></span>";
var sto_input = document.getElementById('selected_storage');
sto_input.value = pool;
sto_input.innerHTML = pool;
$.getJSON(vol_url, function(data) {
if (data.length > 0) {
select.disabled = false;
}
var opt = document.createElement('option');
opt.value = '';
opt.innerHTML = 'None';
select.appendChild(opt);
for (i = 0; i < data.length; i++){
var opt = document.createElement('option');
opt.value = data[i]['name'];
opt.innerHTML = data[i]['name'];
select.appendChild(opt);
}
});
}
</script>
<script> <script>
var editor = ace.edit("editor"); var editor = ace.edit("editor");
editor.getSession().setMode("ace/mode/xml"); editor.getSession().setMode("ace/mode/xml");
@ -1237,7 +1294,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 +1311,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 +1397,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 +1429,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) {
@ -1541,7 +1594,7 @@
} }
}); });
} }
if (~$.inArray(hash, ['#media', '#network', '#clone', '#autostart', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) { if (~$.inArray(hash, ['#media', "#disks", '#network', '#clone', '#autostart', '#xmledit', '#vncsettings', '#migrate', '#options', '#users'])) {
var btnsect = $('#navbtn>li>a'); var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () { $(btnsect).each(function () {
if ($(this).attr('href') === '#settings') { if ($(this).attr('href') === '#settings') {
@ -1574,7 +1627,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];
@ -1586,5 +1640,12 @@
$("#logs_table > tbody").html(logs); $("#logs_table > tbody").html(logs);
}); });
} }
</script>
<script type="text/javascript">
$(document).ready(function(){
$('[data-toggle="popover"]').popover({
placement : 'top'
});
});
</script> </script>
{% endblock %} {% endblock %}

View file

@ -1,7 +1,7 @@
{% 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 %}
@ -10,80 +10,89 @@
<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 %}
{% include 'create_inst_block.html' %} <a href="{% url 'create_instance' compute.id %}" type="button" class="btn btn-success btn-header pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
{% endif %} {% endif %}
{% if all_host_vms or all_user_vms %} {% if all_host_vms or all_user_vms %}
<div class="pull-right search"> <div class="pull-right search">
<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 class="row">
<div class="col-lg-12">
<ol class="breadcrumb">
<li class="active">
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
</li>
<li>
<i class="fa fa-server"></i> {% trans "Instances" %}
</li>
<li>
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
</li>
<li>
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
</li>
<li>
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
</li>
<li>
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
</li>
<li>
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
</li>
</ol>
</div> </div>
</div> </div>
<!-- /.row --> <!-- /.row -->
{% include 'errors_block.html' %} {% include 'errors_block.html' %}
<div class="row"> <div class="row">
<div class="col-lg-12">
<div class="table-responsive">
{% if request.user.is_superuser %}
{% if not all_host_vms %} {% 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 '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="col-lg-12">
<div class="alert alert-warning alert-dismissable"> <div class="alert alert-warning alert-dismissable">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> <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" %} <i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="col-lg-12">
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable> <table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>{% trans 'Name' %}<br>{% trans 'Description' %}</th>
<th>Status</th> <th>{% trans 'Host' %}<br>{% trans 'User' %}</th>
<th>VCPU</th> <th>{% trans 'Status' %}</th>
<th>Memory</th> <th>{% trans 'VCPU' %}</th>
<th data-sortable="false" style="width: 165px;">Actions</th> <th>{% trans 'Memory' %}</th>
<th data-sortable="false" style="width:205px;">{% trans 'Actions' %}</th>
</tr> </tr>
</thead> </thead>
<tbody class="searchable"> <tbody class="searchable">
{% for inst, vm in all_user_vms.items %} {% for host, inst in all_host_vms.items %}
{% for vm, info in inst.items %}
<tr> <tr>
<td><a href="{% url 'instance' vm.compute_id vm.name %}">{{ vm.name }}</a><br><small><em>{{ vm.title }}</em></small></td> <td><a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br><small><em>{{ info.title }}</em></small></td>
<td>{% ifequal vm.status 1 %} <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>
<span class="text-success">{% trans "Active" %}</span> <td>
{% endifequal %} {% ifequal info.status 1 %}<span class="text-success">{% trans "Active" %}</span>{% endifequal %}
{% ifequal vm.status 5 %} {% ifequal info.status 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endifequal %}
<span class="text-danger">{% trans "Off" %}</span> {% ifequal info.status 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endifequal %}
{% endifequal %}
{% ifequal vm.status 3 %}
<span class="text-warning">{% trans "Suspend" %}</span>
{% endifequal %}
</td> </td>
<td>{{ vm.vcpu }}</td> <td>{{ info.vcpu }}</td>
<td>{{ vm.memory }} {% trans "MB" %}</td> <td>{{ info.memory|filesizeformat }}</td>
<td><form action="" method="post" role="form">{% csrf_token %} <td><form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="name" value="{{ vm.name }}"/> <input type="hidden" name="name" value="{{ vm }}"/>
<input type="hidden" name="compute_id" value="{{ vm.compute_id }}"/> <input type="hidden" name="compute_id" value="{{ host.0 }}"/>
{% ifequal vm.status 5 %} {% ifequal info.status 5 %}
{% if inst.instance.is_template %} {% if info.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 }}');"> <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> <span class="glyphicon glyphicon-duplicate"></span>
</button> </button>
{% else %} {% else %}
@ -91,6 +100,9 @@
<span class="glyphicon glyphicon-play"></span> <span class="glyphicon glyphicon-play"></span>
</button> </button>
{% endif %} {% 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" %}"> <button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
<span class="glyphicon glyphicon-off"></span> <span class="glyphicon glyphicon-off"></span>
</button> </button>
@ -101,31 +113,37 @@
<span class="glyphicon glyphicon-eye-open"></span> <span class="glyphicon glyphicon-eye-open"></span>
</button> </button>
{% endifequal %} {% endifequal %}
{% ifequal vm.status 3 %} {% ifequal info.status 3 %}
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}"> <button class="btn btn-sm btn-default" type="submit" name="resume" title="{% trans "Resume" %}">
<span class="glyphicon glyphicon-play"></span> <span class="glyphicon glyphicon-play"></span>
</button> </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" %}"> <button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
<span class="glyphicon glyphicon-off"></span> <span class="glyphicon glyphicon-off"></span>
</button> </button>
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}"> <button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
<span class="glyphicon glyphicon-refresh"></span> <span class="glyphicon glyphicon-refresh"></span>
</button> </button>
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC/Spice Console" %}"> <button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
<span class="glyphicon glyphicon-eye-open"></span> <span class="glyphicon glyphicon-eye-open"></span>
</button> </button>
{% endifequal %} {% endifequal %}
{% ifequal vm.status 1 %} {% ifequal info.status 1 %}
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}"> <button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
<span class="glyphicon glyphicon-play"></span> <span class="glyphicon glyphicon-play"></span>
</button> </button>
<button class="btn btn-sm btn-default" type="submit" name="poweroff" title="{% trans "Power Off" %}"> <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> <span class="glyphicon glyphicon-off"></span>
</button> </button>
<button class="btn btn-sm btn-default" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')"> <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> <span class="glyphicon glyphicon-refresh"></span>
</button> </button>
<a href="#" class="btn btn-sm btn-default" onclick='open_console("{{ vm.compute_id }}-{{ vm.uuid }}")' title="{% trans "Console" %}"> <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> <span class="glyphicon glyphicon-eye-open"></span>
</a> </a>
{% endifequal %} {% endifequal %}
@ -133,12 +151,11 @@
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
{% endfor %}
</tbody> </tbody>
</table> </table>
{% endif %}
{% endif %}
</div>
</div> </div>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
@ -170,14 +187,14 @@
</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 %}

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

@ -20,181 +20,79 @@ from vrtManager.hostdetails import wvmHostDetails
from vrtManager.instance import wvmInstance, wvmInstances from vrtManager.instance import wvmInstance, wvmInstances
from vrtManager.connection import connection_manager from vrtManager.connection import connection_manager
from vrtManager.create import wvmCreate from vrtManager.create import wvmCreate
from vrtManager.storage import wvmStorage
from vrtManager.util import randomPasswd from vrtManager.util import randomPasswd
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE
from logs.views import addlogmsg 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:
status = connection_manager.host_is_up(comp.type, comp.hostname)
if status:
try: try:
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type) all_host_vms = get_host_instances(request, compute)
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: except libvirtError as lib_err:
error_messages.append(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())
@ -274,7 +172,7 @@ def instance(request, compute_id, vname):
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_devices():
if disk['size']: if disk['size']:
disk_size += int(disk['size']) >> 30 disk_size += int(disk['size']) >> 30
@ -348,6 +246,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()
@ -357,13 +256,14 @@ def instance(request, compute_id, vname):
cur_memory = conn.get_cur_memory() cur_memory = conn.get_cur_memory()
title = conn.get_title() title = conn.get_title()
description = conn.get_description() description = conn.get_description()
disks = conn.get_disk_device() disks = conn.get_disk_devices()
media = conn.get_media_device() media = conn.get_media_devices()
if len(media) != 0: if len(media) != 0:
media_iso = sorted(conn.get_iso_media()) media_iso = sorted(conn.get_iso_media())
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:
@ -387,6 +287,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()
@ -412,6 +313,10 @@ def instance(request, compute_id, vname):
if request.method == 'POST': if request.method == 'POST':
if 'poweron' in request.POST: if 'poweron' in request.POST:
if instance.is_template:
msg = _("Templates cannot be started.")
error_messages.append(msg)
else:
conn.start() conn.start()
msg = _("Power On") msg = _("Power On")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
@ -442,7 +347,7 @@ def instance(request, compute_id, vname):
if request.POST.get('delete_disk', ''): if request.POST.get('delete_disk', ''):
for snap in snapshots: for snap in snapshots:
conn.snapshot_delete(snap['name']) conn.snapshot_delete(snap['name'])
conn.delete_disk() conn.delete_all_disks()
conn.delete() conn.delete()
instance = Instance.objects.get(compute_id=compute_id, name=vname) instance = Instance.objects.get(compute_id=compute_id, name=vname)
@ -450,7 +355,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 +364,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 +410,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', '')
@ -537,27 +444,76 @@ 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() + '#resize') return HttpResponseRedirect(request.get_full_path() + '#resize')
if 'addvolume' in request.POST and (request.user.is_superuser or userinstance.is_change): if 'addnewvol' 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)
return HttpResponseRedirect(request.get_full_path() + '#resize') return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'umount_iso' in request.POST: if 'addexistingvol' in request.POST and (request.user.is_superuser or userinstance.is_change):
storage = request.POST.get('selected_storage', '')
name = request.POST.get('vols', '')
bus = request.POST.get('bus', default_bus)
cache = request.POST.get('cache', default_cache)
connCreate = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type,
storage)
format = connCreate.get_volume_type(name)
path = connCreate.get_target_path()
target = get_new_disk_dev(disks, bus)
source = path + "/" + name;
conn.attach_disk(source, target, subdriver=format, cache=cache, targetbus=bus)
msg = _('Attach Existing disk')
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'delvolume' in request.POST and (request.user.is_superuser or userinstance.is_change):
connDelete = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
dev = request.POST.get('dev', '')
path = request.POST.get('path', '')
conn.detach_disk(dev, path)
connDelete.delete_volume(path)
msg = _('Delete disk')
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'detachvolume' in request.POST and (request.user.is_superuser or userinstance.is_change):
connDelete = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
dev = request.POST.get('dev', '')
path = request.POST.get('path', '')
conn.detach_disk(dev, path)
msg = _('Detach disk')
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'umount_iso' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
image = request.POST.get('path', '') image = request.POST.get('path', '')
dev = request.POST.get('umount_iso', '') dev = request.POST.get('umount_iso', '')
conn.umount_iso(dev, image) conn.umount_iso(dev, image)
@ -565,7 +521,7 @@ 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() + '#media') return HttpResponseRedirect(request.get_full_path() + '#media')
if 'mount_iso' in request.POST: if 'mount_iso' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
image = request.POST.get('media', '') image = request.POST.get('media', '')
dev = request.POST.get('mount_iso', '') dev = request.POST.get('mount_iso', '')
conn.mount_iso(dev, image) conn.mount_iso(dev, image)
@ -573,21 +529,21 @@ 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() + '#media') return HttpResponseRedirect(request.get_full_path() + '#media')
if 'snapshot' in request.POST: if 'snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
name = request.POST.get('name', '') name = request.POST.get('name', '')
conn.create_snapshot(name) conn.create_snapshot(name)
msg = _("New snapshot") msg = _("New snapshot")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot') return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'delete_snapshot' in request.POST: if 'delete_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
snap_name = request.POST.get('name', '') snap_name = request.POST.get('name', '')
conn.snapshot_delete(snap_name) conn.snapshot_delete(snap_name)
msg = _("Delete snapshot") msg = _("Delete snapshot")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot') return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'revert_snapshot' in request.POST: if 'revert_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
snap_name = request.POST.get('name', '') snap_name = request.POST.get('name', '')
conn.snapshot_revert(snap_name) conn.snapshot_revert(snap_name)
msg = _("Successful revert snapshot: ") msg = _("Successful revert snapshot: ")
@ -689,6 +645,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,21 +657,33 @@ 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')
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') return HttpResponseRedirect(request.get_full_path() + '#network')
if 'add_owner' in request.POST: if 'add_owner' in request.POST:
@ -743,7 +712,6 @@ 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 = {}
@ -774,13 +742,20 @@ 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_instance = Instance(compute_id=compute_id, name=clone_data['name'])
new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid)
new_instance.save() new_instance.save()
try:
new_uuid = conn.clone_instance(clone_data)
new_instance.uuid = new_uuid
new_instance.save()
except Exception as e:
new_instance.delete()
raise e
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()
@ -789,9 +764,10 @@ 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 and (request.user.is_superuser or request.user.is_staff or userinstance.is_change):
instance.is_template = request.POST.get('is_template', False) instance.is_template = request.POST.get('is_template', False)
instance.save() instance.save()
@ -839,6 +815,158 @@ 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:
if instance.is_template:
msg = _("Templates cannot be started.")
messages.error(request, msg)
else:
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):
""" """
@ -1027,6 +1155,7 @@ def get_clone_disk_name(disk, prefix, clone_name=''):
image = "{}-clone".format(disk['image']) image = "{}-clone".format(disk['image'])
return image return image
def _get_clone_disks(disks, vname=''): def _get_clone_disks(disks, vname=''):
clone_disks = [] clone_disks = []
for disk in disks: for disk in disks:
@ -1042,6 +1171,7 @@ def _get_clone_disks(disks, vname=''):
clone_disks.append(new_disk) clone_disks.append(new_disk)
return clone_disks return clone_disks
def sshkeys(request, vname): def sshkeys(request, vname):
""" """
:param request: :param request:
@ -1089,7 +1219,7 @@ def delete_instance(instance, delete_disk=False):
print("Deleting snapshot {}".format(snap['name'])) print("Deleting snapshot {}".format(snap['name']))
conn.snapshot_delete(snap['name']) conn.snapshot_delete(snap['name'])
print("Deleting disks") print("Deleting disks")
conn.delete_disk() conn.delete_all_disks()
conn.delete() conn.delete()
instance.delete() instance.delete()

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.

3226
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

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

@ -1,5 +1,5 @@
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
@ -8,6 +8,7 @@ from storages.forms import AddStgPool, AddImage, CloneImage
from vrtManager.storage import wvmStorage, wvmStorages from vrtManager.storage import wvmStorage, wvmStorages
from libvirt import libvirtError from libvirt import libvirtError
from django.contrib import messages from django.contrib import messages
import json
@login_required @login_required
def storages(request, compute_id): def storages(request, compute_id):
@ -155,7 +156,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)
@ -197,14 +198,19 @@ 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)
else: else:
for msg_err in form.errors.values(): for msg_err in form.errors.values():
error_messages.append(msg_err.as_text()) error_messages.append(msg_err.as_text())
if request.method == 'GET':
if 'get_volumes' in request.GET:
conn.close()
return HttpResponse(json.dumps(sorted(volumes)))
conn.close() conn.close()
return render(request, 'storage.html', locals()) return render(request, 'storage.html', locals())

View file

@ -28,6 +28,7 @@
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]--> <![endif]-->
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
</head> </head>
<body> <body>

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

@ -354,9 +354,10 @@ class wvmConnect(object):
def get_dom_cap_xml(self): def get_dom_cap_xml(self):
""" Return domcapabilities xml""" """ Return domcapabilities xml"""
emulatorbin = self.emulator()
machine = self.machine()
arch = self.wvm.getInfo()[0] arch = self.wvm.getInfo()[0]
machine = self.get_machines(arch)
emulatorbin = self.get_emulator(arch)
virttype = self.hypervisor_type()[arch][0] virttype = self.hypervisor_type()[arch][0]
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype) return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
@ -389,6 +390,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 {
@ -411,13 +418,23 @@ class wvmConnect(object):
return result return result
return util.get_xml_path(self.get_cap_xml(), func=hypervisors) return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
def emulator(self): def get_emulator(self, arch):
"""Return emulator """ """Return emulator """
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/emulator") return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/emulator".format(arch))
def machine(self): def get_emulators(self):
def emulators(ctx):
result = {}
for arch in ctx.xpath('/capabilities/guest/arch'):
emulator = arch.xpath('emulator')
arch_name = arch.xpath('@name')[0]
result[arch_name]= emulator
return result
return util.get_xml_path(self.get_cap_xml(), func=emulators)
def get_machines(self, arch):
""" Return machine type of emulation""" """ Return machine type of emulation"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/machine") return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/machine".format(arch))
def get_busses(self): def get_busses(self):
"""Get available busses""" """Get available busses"""
@ -443,7 +460,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 +486,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

@ -3,6 +3,7 @@ from vrtManager import util
from vrtManager.connection import wvmConnect from vrtManager.connection import wvmConnect
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as default_owner
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
@ -52,7 +53,7 @@ class wvmCreate(wvmConnect):
"""Get guest capabilities""" """Get guest capabilities"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch") return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
def create_volume(self, storage, name, size, image_format=image_format, metadata=False): def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=default_owner):
size = int(size) * 1073741824 size = int(size) * 1073741824
stg = self.get_storage(storage) stg = self.get_storage(storage)
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type") storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -70,13 +71,17 @@ class wvmCreate(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>
<compat>1.1</compat>
<features>
<lazy_refcounts/>
</features>
</target> </target>
</volume>""" % (name, size, alloc, image_format) </volume>""" % (name, size, alloc, image_format, owner['uid'], owner['guid'])
stg.createXML(xml, metadata) stg.createXML(xml, metadata)
try: try:
stg.refresh(0) stg.refresh(0)
@ -110,7 +115,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(vol_path) vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume() return vol.storagePoolLookupByVolume()
def clone_from_template(self, clone, template, metadata=False): def clone_from_template(self, clone, template, metadata=False, owner=default_owner):
vol = self.get_volume_by_path(template) vol = self.get_volume_by_path(template)
stg = vol.storagePoolLookupByVolume() stg = vol.storagePoolLookupByVolume()
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type") storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -127,8 +132,8 @@ class wvmCreate(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>
@ -137,7 +142,7 @@ class wvmCreate(wvmConnect):
<lazy_refcounts/> <lazy_refcounts/>
</features> </features>
</target> </target>
</volume>""" % (clone, format) </volume>""" % (clone, format, owner['uid'], owner['guid'])
stg.createXMLFrom(xml, vol, metadata) stg.createXMLFrom(xml, vol, metadata)
clone_vol = stg.storageVolLookupByName(clone) clone_vol = stg.storageVolLookupByName(clone)
return clone_vol.path() return clone_vol.path()

View file

@ -10,6 +10,7 @@ from datetime import datetime
from vrtManager.connection import wvmConnect from vrtManager.connection import wvmConnect
from vrtManager.storage import wvmStorage from vrtManager.storage import wvmStorage
from webvirtcloud.settings import QEMU_CONSOLE_TYPES from webvirtcloud.settings import QEMU_CONSOLE_TYPES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
class wvmInstances(wvmConnect): class wvmInstances(wvmConnect):
@ -187,6 +188,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,17 +233,18 @@ 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)
def get_disk_device(self): def get_disk_devices(self):
def disks(doc): def disks(doc):
result = [] result = []
dev = None dev = None
@ -244,6 +259,7 @@ class wvmInstance(wvmConnect):
if device == 'disk': if device == 'disk':
try: try:
dev = disk.xpath('target/@dev')[0] dev = disk.xpath('target/@dev')[0]
bus = disk.xpath('target/@bus')[0]
src_fl = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0] src_fl = disk.xpath('source/@file|source/@dev|source/@name|source/@volume')[0]
try: try:
disk_format = disk.xpath('driver/@type')[0] disk_format = disk.xpath('driver/@type')[0]
@ -252,7 +268,9 @@ class wvmInstance(wvmConnect):
try: try:
vol = self.get_volume_by_path(src_fl) vol = self.get_volume_by_path(src_fl)
volume = vol.name() volume = vol.name()
disk_size = vol.info()[1] disk_size = vol.info()[1]
used_size = vol.info()[2]
stg = vol.storagePoolLookupByVolume() stg = vol.storagePoolLookupByVolume()
storage = stg.name() storage = stg.name()
except libvirtError: except libvirtError:
@ -261,13 +279,13 @@ class wvmInstance(wvmConnect):
pass pass
finally: finally:
result.append( result.append(
{'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl, {'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_fl,
'format': disk_format, 'size': disk_size}) 'format': disk_format, 'size': disk_size, 'used': used_size})
return result return result
return util.get_xml_path(self._XMLDesc(0), func=disks) return util.get_xml_path(self._XMLDesc(0), func=disks)
def get_media_device(self): def get_media_devices(self):
def disks(doc): def disks(doc):
result = [] result = []
dev = None dev = None
@ -361,6 +379,25 @@ class wvmInstance(wvmConnect):
xmldom = ElementTree.tostring(tree) xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom) self._defineXML(xmldom)
def detach_disk(self, dev, image):
tree = ElementTree.fromstring(self._XMLDesc(0))
for disk in tree.findall("./devices/disk[@device='disk']"):
source = disk.find("source")
target = disk.find("target")
if source.get("file") == image and target.get("dev") == dev:
devices = tree.find('devices')
devices.remove(disk)
if self.get_status() == 1:
xml_disk = ElementTree.tostring(disk)
yyy = self.instance.detachDevice(xml_disk)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree)
break
self._defineXML(xmldom)
def cpu_usage(self): def cpu_usage(self):
cpu_usage = {} cpu_usage = {}
if self.get_status() == 1: if self.get_status() == 1:
@ -487,8 +524,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):
@ -614,8 +650,8 @@ class wvmInstance(wvmConnect):
iso.append(img) iso.append(img)
return iso return iso
def delete_disk(self): def delete_all_disks(self):
disks = self.get_disk_device() disks = self.get_disk_devices()
for disk in disks: for disk in disks:
vol = self.get_volume_by_path(disk.get('path')) vol = self.get_volume_by_path(disk.get('path'))
vol.delete(0) vol.delete(0)
@ -700,16 +736,15 @@ 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
vol_clone_xml = """ vol_clone_xml = """
<volume> <volume>
<name>%s</name> <name>%s</name>
@ -718,7 +753,17 @@ class wvmInstance(wvmConnect):
<target> <target>
<format type='%s'/> <format type='%s'/>
</target> </target>
</volume>""" % (target_file, vol_format) <permissions>
<owner>%s</owner>
<group>%s</group>
<mode>0644</mode>
<label>virt_image_t</label>
</permissions>
<compat>1.1</compat>
<features>
<lazy_refcounts/>
</features>
</volume>""" % (target_file, vol_format, owner['uid'], owner['guid'])
stg = vol.storagePoolLookupByVolume() stg = vol.storagePoolLookupByVolume()
stg.createXMLFrom(vol_clone_xml, vol, meta_prealloc) stg.createXMLFrom(vol_clone_xml, vol, meta_prealloc)
@ -729,8 +774,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 +820,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 +828,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 +842,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)

View file

@ -81,7 +81,7 @@ class wvmInterface(wvmConnect):
def get_ipv4_type(self): def get_ipv4_type(self):
try: try:
xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE)
ipaddr = util.get_xml_path(xml, "/interface/protocol/ip/@address") ipaddr = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@address")
if ipaddr: if ipaddr:
return 'static' return 'static'
else: else:
@ -91,8 +91,8 @@ class wvmInterface(wvmConnect):
def get_ipv4(self): def get_ipv4(self):
xml = self._XMLDesc() xml = self._XMLDesc()
int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol/ip/@address") int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@address")
int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol/ip/@prefix") int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@prefix")
if not int_ipv4_ip or not int_ipv4_mask: if not int_ipv4_ip or not int_ipv4_mask:
return None return None
else: else:
@ -101,7 +101,7 @@ class wvmInterface(wvmConnect):
def get_ipv6_type(self): def get_ipv6_type(self):
try: try:
xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE) xml = self._XMLDesc(VIR_INTERFACE_XML_INACTIVE)
ipaddr = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") ipaddr = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@address")
if ipaddr: if ipaddr:
return 'static' return 'static'
else: else:
@ -111,8 +111,8 @@ class wvmInterface(wvmConnect):
def get_ipv6(self): def get_ipv6(self):
xml = self._XMLDesc() xml = self._XMLDesc()
int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address") int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@address")
int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[2]/ip/@prefix") int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@prefix")
if not int_ipv6_ip or not int_ipv6_mask: if not int_ipv6_ip or not int_ipv6_mask:
return None return None
else: else:

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

@ -2,6 +2,7 @@ import random
import lxml.etree as etree import lxml.etree as etree
import libvirt import libvirt
import string import string
import re
def is_kvm_available(xml): def is_kvm_available(xml):
@ -26,8 +27,11 @@ def randomMAC():
def randomUUID(): def randomUUID():
"""Generate a random UUID.""" """Generate a random UUID."""
u = [random.randint(0, 255) for dummy in range(0, 16)] u = [random.randint(0, 255) for ignore in range(0, 16)]
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u) u[6] = (u[6] & 0x0F) | (4 << 4)
u[8] = (u[8] & 0x3F) | (2 << 6)
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
"%02x" * 6]) % tuple(u)
def randomPasswd(length=12, alphabet=string.letters + string.digits): def randomPasswd(length=12, alphabet=string.letters + string.digits):
@ -129,3 +133,23 @@ def pretty_bytes(val):
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0)) return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
else: else:
return "%2.2f MB" % (val / (1024.0 * 1024.0)) return "%2.2f MB" % (val / (1024.0 * 1024.0))
def validate_uuid(val):
if type(val) is not str:
raise ValueError("UUID must be a string.")
form = re.match("[a-fA-F0-9]{8}[-]([a-fA-F0-9]{4}[-]){3}[a-fA-F0-9]{12}$",
val)
if form is None:
form = re.match("[a-fA-F0-9]{32}$", val)
if form is None:
raise ValueError(
"UUID must be a 32-digit hexadecimal number. It may take "
"the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx or may "
"omit hyphens altogether.")
else: # UUID had no dashes, so add them in
val = (val[0:8] + "-" + val[8:12] + "-" + val[12:16] +
"-" + val[16:20] + "-" + val[20:32])
return val

View file

@ -24,6 +24,7 @@ INSTALLED_APPS = (
'networks', 'networks',
'storages', 'storages',
'interfaces', 'interfaces',
'nwfilters',
'instances', 'instances',
'secrets', 'secrets',
'logs', 'logs',
@ -151,3 +152,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)),