mirror of
https://github.com/retspen/webvirtcloud
synced 2024-11-01 12:04:15 +00:00
Merge pull request #197 from catborise/master
NWFilters Addition & Instances Tab & Other Fixes
This commit is contained in:
commit
cd940c99e5
55 changed files with 5249 additions and 1545 deletions
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -6,461 +6,518 @@
|
||||||
<link href="{% static "css/bootstrap-multiselect.css" %}" rel="stylesheet">
|
<link href="{% static "css/bootstrap-multiselect.css" %}" rel="stylesheet">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Page Heading -->
|
<!-- Page Heading -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
{% 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>
|
<!-- /.row -->
|
||||||
<!-- /.row -->
|
{% 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">
|
</ul>
|
||||||
<div class="well">
|
<!-- Tab panes -->
|
||||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
<div class="tab-content">
|
||||||
<div class="form-group">
|
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="flavor">
|
||||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
{% if not flavors %}
|
||||||
<div class="col-sm-6">
|
<div class="col-lg-12">
|
||||||
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
|
<div class="alert alert-warning alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any Flavors" %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
{% else %}
|
||||||
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
|
{% include 'create_flav_block.html' %}
|
||||||
<div class="col-sm-6">
|
<h3 class="page-header">{% trans "Create from flavor" %}</h3>
|
||||||
<input type="text" class="form-control" name="vcpu" value="1" maxlength="1" required pattern="[0-9]">
|
<div class="table-responsive">
|
||||||
</div>
|
<table class="table table-bordered table-hover">
|
||||||
</div>
|
<thead>
|
||||||
<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 }}">{% 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 %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="alert alert-warning alert-dismissable">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
|
||||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any Flavors" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<h3 class="page-header">{% trans "Create from flavor" %}</h3>
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-bordered table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>{% trans "Name" %}</th>
|
|
||||||
<th>{% trans "VCPU's" %}</th>
|
|
||||||
<th>{% trans "RAM" %}</th>
|
|
||||||
<th>{% trans "HDD" %}</th>
|
|
||||||
<th colspan="2">{% trans "Action" %}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for flavor in flavors %}
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ forloop.counter }}</td>
|
<th>#</th>
|
||||||
<td>{{ flavor.label }}</td>
|
<th>{% trans "Name" %}</th>
|
||||||
<td>{{ flavor.vcpu }}</td>
|
<th>{% trans "VCPU's" %}</th>
|
||||||
<td>{{ flavor.memory }} {% trans "MB" %}</td>
|
<th>{% trans "RAM" %}</th>
|
||||||
<td>{{ flavor.disk }} {% trans "GB" %}</td>
|
<th>{% trans "HDD" %}</th>
|
||||||
<td style="width:5px;">
|
<th colspan="2">{% trans "Action" %}</th>
|
||||||
<div class="modal fade" id="addVMflavor{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addVMFlavorLabel" aria-hidden="true">
|
</tr>
|
||||||
<div class="modal-dialog">
|
</thead>
|
||||||
<div class="modal-content">
|
<tbody>
|
||||||
<div class="modal-header">
|
{% for flavor in flavors %}
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
<tr>
|
||||||
<h4 class="modal-title">{% trans "Create Virtual Machine" %} ({{ flavor.label }})</h4>
|
<td>{{ forloop.counter }}</td>
|
||||||
</div>
|
<td>{{ flavor.label }}</td>
|
||||||
<div class="modal-body">
|
<td>{{ flavor.vcpu }}</td>
|
||||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
<td>{{ flavor.memory }} {% trans "MB" %}</td>
|
||||||
<div class="form-group">
|
<td>{{ flavor.disk }} {% trans "GB" %}</td>
|
||||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
<td style="width:5px;">
|
||||||
<div class="col-sm-6">
|
<div class="modal fade" id="addVMflavor{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addVMFlavorLabel" aria-hidden="true">
|
||||||
<input type="text" class="form-control" name="name"
|
<div class="modal-dialog">
|
||||||
placeholder="{% trans "Name" %}" maxlength="14" required
|
<div class="modal-content">
|
||||||
pattern="[a-zA-Z0-9\.\-_]+">
|
<div class="modal-header">
|
||||||
<input type="hidden" name="vcpu" value="{{ flavor.vcpu }}">
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<input type="hidden" name="memory" value="{{ flavor.memory }}">
|
<h4 class="modal-title">{% trans "Create Virtual Machine" %} ({{ flavor.label }})</h4>
|
||||||
<input type="hidden" name="hdd_size" value="{{ flavor.disk }}">
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<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\.\-_]+">
|
||||||
|
<input type="hidden" name="vcpu" value="{{ flavor.vcpu }}">
|
||||||
|
<input type="hidden" name="memory" value="{{ flavor.memory }}">
|
||||||
|
<input type="hidden" name="hdd_size" value="{{ flavor.disk }}">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
|
||||||
|
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<input type="hidden" name="cache_mode" value="default">
|
<input type="hidden" name="cache_mode" value="default">
|
||||||
<select name="storage" class="form-control">
|
<select name="storage" class="form-control">
|
||||||
{% if storages %}
|
{% if storages %}
|
||||||
{% for storage in storages %}
|
{% for storage in storages %}
|
||||||
<option value="{{ storage }}">{{ storage }}</option>
|
<option value="{{ storage }}">{{ storage }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<option value="">{% trans "None" %}</option>
|
||||||
|
{% endif %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</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 %}
|
{% endfor %}
|
||||||
{% else %}
|
</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>
|
<option value="">{% trans "None" %}</option>
|
||||||
{% endif %}
|
{% for nwfilter in nwfilters %}
|
||||||
</select>
|
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</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 "HDD cache mode" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
|
||||||
<select id="cache_mode" name="cache_mode" class="form-control">
|
</div>
|
||||||
{% for mode, name in cache_modes %}
|
|
||||||
<option value="{{ mode }}">{% trans name %}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<select name="video" class="form-control">
|
||||||
<select name="networks" class="form-control">
|
{% if not videos %}
|
||||||
{% for network in networks %}
|
<option value="vga">vga</option>
|
||||||
<option value="{{ network }}">{{ network }}</option>
|
<option value="cirrus">cirrus</option>
|
||||||
{% endfor %}
|
{% endif %}
|
||||||
</select>
|
{% for video in videos %}
|
||||||
|
<option value="{{ video }}">{{ video }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "MAC" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
|
||||||
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<select name="listener_addr" class="form-control">
|
||||||
<select name="video" class="form-control">
|
{% for addr, label in listener_addr %}
|
||||||
{% if not videos %}
|
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
|
||||||
<option value="vga">vga</option>
|
{% endfor %}
|
||||||
<option value="cirrus">cirrus</option>
|
</select>
|
||||||
{% endif %}
|
</div>
|
||||||
{% for video in videos %}
|
|
||||||
<option value="{{ video }}">{{ video }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<input type="checkbox" name="host_model" value="true" checked>
|
||||||
<input type="password" class="form-control" name="console_pass" placeholder="{% trans "Console Password" %}" maxlength="14">
|
</div>
|
||||||
|
<label class="col-lg-1 control-label">{% trans "CPU" %}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group">
|
||||||
<div class="form-group">
|
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
|
||||||
<label class="col-sm-3 control-label">{% trans "Console Access" %}</label>
|
<div class="col-sm-6">
|
||||||
<div class="col-sm-6">
|
<input type="checkbox" name="virtio" value="true" checked>
|
||||||
<select name="listener_addr" class="form-control">
|
</div>
|
||||||
{% for addr, label in listener_addr %}
|
|
||||||
<option value="{{ addr }}" {% if addr == "0.0.0.0" %} selected {% endif %}>{{ label }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="modal-footer">
|
||||||
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
|
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||||
<div class="col-sm-6">
|
{% if storages %}
|
||||||
<input type="checkbox" name="host_model" value="true" checked>
|
<button type="submit" class="btn btn-primary" name="create">{% trans "Create" %}</button>
|
||||||
</div>
|
{% else %}
|
||||||
<label class="col-lg-1 control-label">{% trans "CPU" %}</label>
|
<button class="btn btn-primary disabled">{% trans "Create" %}</button>
|
||||||
</div>
|
{% endif %}
|
||||||
<div class="form-group">
|
</div>
|
||||||
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
|
</form>
|
||||||
<div class="col-sm-6">
|
|
||||||
<input type="checkbox" name="virtio" value="true" checked>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
|
||||||
{% if storages %}
|
|
||||||
<button type="submit" class="btn btn-primary" name="create">{% trans "Create" %}</button>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn btn-primary disabled">{% trans "Create" %}</button>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<a data-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-default">
|
||||||
<a data-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-default">
|
<span class="glyphicon glyphicon-plus"></span>
|
||||||
<span class="glyphicon glyphicon-plus"></span>
|
</a>
|
||||||
</a>
|
</td>
|
||||||
</td>
|
<td style="width:5px;">
|
||||||
<td style="width:5px;">
|
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
<input type="hidden" name="flavor" value="{{ flavor.id }}">
|
||||||
<input type="hidden" name="flavor" value="{{ flavor.id }}">
|
<button type="submit" class="btn btn-sm btn-default" name="delete_flavor" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||||
<button type="submit" class="btn btn-sm btn-default" name="delete_flavor" onclick="return confirm('{% trans "Are you sure?" %}')">
|
<span class="glyphicon glyphicon-trash"></span>
|
||||||
<span class="glyphicon glyphicon-trash"></span>
|
</button>
|
||||||
</button>
|
</form>
|
||||||
</form>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
{% endfor %}
|
||||||
{% endfor %}
|
</tbody>
|
||||||
</tbody>
|
</table>
|
||||||
</table>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="clearfix"></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>
|
</div>
|
||||||
{% endif %}
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
|
@ -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 -->
|
||||||
|
|
159
instances/templates/add_instance_volume.html
Normal file
159
instances/templates/add_instance_volume.html
Normal 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">×</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 %}
|
184
instances/templates/allinstances.html
Normal file
184
instances/templates/allinstances.html
Normal 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">×</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">×</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 %}
|
|
@ -72,12 +72,14 @@
|
||||||
{% trans "Resize" %}
|
{% trans "Resize" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li role="presentation">
|
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
|
||||||
<a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab">
|
<li role="presentation">
|
||||||
<span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span>
|
<a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab">
|
||||||
{% trans "Snapshots" %}
|
<span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span>
|
||||||
</a>
|
{% trans "Snapshots" %}
|
||||||
</li>
|
</a>
|
||||||
|
</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 }}">
|
||||||
<button type="submit" class="btn btn-sm btn-success pull-left" value="{{ cd.dev }}" name="umount_iso" style="margin-top: 2px;">{% trans "Umount" %}</button>
|
{% 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>
|
||||||
|
{% 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>
|
||||||
|
@ -833,39 +833,60 @@
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
|
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
|
||||||
<p>
|
<p>
|
||||||
{% trans "Assign network device to bridge" %}
|
{% trans "Assign network device to bridge" %}
|
||||||
{% include 'add_instance_network_block.html' %}
|
{% include 'add_instance_network_block.html' %}
|
||||||
</p>
|
</p>
|
||||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
||||||
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
<div class="col-xs-12 col-sm-12">
|
||||||
|
<form method="post" role="form">{% csrf_token %}
|
||||||
{% for network in networks %}
|
{% for network in networks %}
|
||||||
<div class="form-group">
|
<div class="panel panel-default">
|
||||||
<label class="col-sm-3 control-label" style="font-weight:normal;">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
<div class="panel-heading">
|
||||||
<div class="col-sm-3">
|
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
||||||
<input type="text" class="form-control" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3">
|
<div class="panel-body">
|
||||||
<input type="text" class="form-control" name="net-source-{{ forloop.counter0 }}" value="{{ network.nic }}" disabled/>
|
<div class="form-group form-inline">
|
||||||
|
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
|
||||||
|
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
|
||||||
|
<label class="control-label"><em>to</em></label>
|
||||||
|
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-inline">
|
||||||
|
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
|
||||||
|
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
||||||
|
<label class="control-label"><em>to</em></label>
|
||||||
|
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
|
||||||
|
{% for c_net in compute_networks %}
|
||||||
|
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
{% for c_iface in compute_interfaces %}
|
||||||
|
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group form-inline">
|
||||||
|
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
|
||||||
|
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
|
||||||
|
<label class="control-label"><em>to</em></label>
|
||||||
|
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
|
||||||
|
<option value="">{% trans "None" %}</option>
|
||||||
|
{% for c_filters in compute_nwfilters %}
|
||||||
|
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3">
|
|
||||||
<select name="net-source-{{ forloop.counter0 }}" class="form-control" id="network_select" onchange="network_select_enable()">
|
<div class="panel-footer">
|
||||||
{% for c_nets in compute_networks %}
|
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
|
||||||
<option value="net:{{ c_nets }}" {% if c_nets == network.nic %}selected{% endif %}>Network {{ c_nets }}</option>
|
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
|
||||||
{% endfor %}
|
|
||||||
{% for c_iface in compute_interfaces %}
|
|
||||||
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %}selected{% endif %}>Interface {{ c_iface }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% ifequal status 5 %}
|
|
||||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
|
||||||
{% else %}
|
|
||||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
|
||||||
{% endifequal %}
|
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
|
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
|
||||||
|
@ -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");
|
||||||
|
@ -1232,13 +1289,13 @@
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
function open_console(view_style) {
|
function open_console(view_style) {
|
||||||
window.open('{% url 'console' %}?token={{ compute_id }}-{{ uuid }}&view=' +view_style +'', '', 'width=850,height=600')
|
window.open('{% url 'console' %}?token={{ compute_id }}-{{ uuid }}&view=' + view_style +'', '', 'width=850,height=600')
|
||||||
}
|
}
|
||||||
</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']);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -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 %}
|
||||||
|
|
|
@ -1,145 +1,162 @@
|
||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load staticfiles %}
|
{% load staticfiles %}
|
||||||
{% block title %}{% trans "Instances" %}{% endblock %}
|
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
|
||||||
{% block style %}
|
{% block style %}
|
||||||
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
|
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<!-- Page Heading -->
|
<!-- Page Heading -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
{% 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>
|
</div>
|
||||||
<!-- /.row -->
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
|
||||||
{% include 'errors_block.html' %}
|
<ol class="breadcrumb">
|
||||||
|
<li class="active">
|
||||||
|
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-server"></i> {% trans "Instances" %}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
|
||||||
<div class="row">
|
{% include 'errors_block.html' %}
|
||||||
|
<div class="row">
|
||||||
|
{% if not all_host_vms %}
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div class="table-responsive">
|
<div class="alert alert-warning alert-dismissable">
|
||||||
{% if request.user.is_superuser %}
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
{% if not all_host_vms %}
|
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="alert alert-warning alert-dismissable">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
|
||||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "You don't have any Instance" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
{% ifequal view_style "nongrouped" %}
|
|
||||||
{% include 'instances_nongrouped.html' %}
|
|
||||||
{% endifequal %}
|
|
||||||
{% ifequal view_style "grouped" %}
|
|
||||||
{% include 'instances_grouped.html' %}
|
|
||||||
{% endifequal %}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
|
||||||
{% if not all_user_vms %}
|
|
||||||
<div class="col-lg-12">
|
|
||||||
<div class="alert alert-warning alert-dismissable">
|
|
||||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
|
||||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "You don't have any Instance" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Name</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>VCPU</th>
|
|
||||||
<th>Memory</th>
|
|
||||||
<th data-sortable="false" style="width: 165px;">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="searchable">
|
|
||||||
{% for inst, vm in all_user_vms.items %}
|
|
||||||
<tr>
|
|
||||||
<td><a href="{% url 'instance' vm.compute_id vm.name %}">{{ vm.name }}</a><br><small><em>{{ vm.title }}</em></small></td>
|
|
||||||
<td>{% ifequal vm.status 1 %}
|
|
||||||
<span class="text-success">{% trans "Active" %}</span>
|
|
||||||
{% endifequal %}
|
|
||||||
{% ifequal vm.status 5 %}
|
|
||||||
<span class="text-danger">{% trans "Off" %}</span>
|
|
||||||
{% endifequal %}
|
|
||||||
{% ifequal vm.status 3 %}
|
|
||||||
<span class="text-warning">{% trans "Suspend" %}</span>
|
|
||||||
{% endifequal %}
|
|
||||||
</td>
|
|
||||||
<td>{{ vm.vcpu }}</td>
|
|
||||||
<td>{{ vm.memory }} {% trans "MB" %}</td>
|
|
||||||
<td><form action="" method="post" role="form">{% csrf_token %}
|
|
||||||
<input type="hidden" name="name" value="{{ vm.name }}"/>
|
|
||||||
<input type="hidden" name="compute_id" value="{{ vm.compute_id }}"/>
|
|
||||||
{% ifequal vm.status 5 %}
|
|
||||||
{% if inst.instance.is_template %}
|
|
||||||
<button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ vm.compute_id }}, '{{ vm.name }}');">
|
|
||||||
<span class="glyphicon glyphicon-duplicate"></span>
|
|
||||||
</button>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn btn-sm btn-default" type="submit" name="poweron" title="{% trans "Power On" %}">
|
|
||||||
<span class="glyphicon glyphicon-play"></span>
|
|
||||||
</button>
|
|
||||||
{% endif %}
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
|
||||||
<span class="glyphicon glyphicon-off"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
|
||||||
<span class="glyphicon glyphicon-refresh"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
|
|
||||||
<span class="glyphicon glyphicon-eye-open"></span>
|
|
||||||
</button>
|
|
||||||
{% endifequal %}
|
|
||||||
{% ifequal vm.status 3 %}
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
|
|
||||||
<span class="glyphicon glyphicon-play"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
|
||||||
<span class="glyphicon glyphicon-off"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
|
||||||
<span class="glyphicon glyphicon-refresh"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC/Spice Console" %}">
|
|
||||||
<span class="glyphicon glyphicon-eye-open"></span>
|
|
||||||
</button>
|
|
||||||
{% endifequal %}
|
|
||||||
{% ifequal vm.status 1 %}
|
|
||||||
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
|
|
||||||
<span class="glyphicon glyphicon-play"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default" type="submit" name="poweroff" title="{% trans "Power Off" %}">
|
|
||||||
<span class="glyphicon glyphicon-off"></span>
|
|
||||||
</button>
|
|
||||||
<button class="btn btn-sm btn-default" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')">
|
|
||||||
<span class="glyphicon glyphicon-refresh"></span>
|
|
||||||
</button>
|
|
||||||
<a href="#" class="btn btn-sm btn-default" onclick='open_console("{{ vm.compute_id }}-{{ vm.uuid }}")' title="{% trans "Console" %}">
|
|
||||||
<span class="glyphicon glyphicon-eye-open"></span>
|
|
||||||
</a>
|
|
||||||
{% endifequal %}
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{% else %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<table class="table table-hover table-striped sortable-theme-bootstrap" data-sortable>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans 'Name' %}<br>{% trans 'Description' %}</th>
|
||||||
|
<th>{% trans 'Host' %}<br>{% trans 'User' %}</th>
|
||||||
|
<th>{% trans 'Status' %}</th>
|
||||||
|
<th>{% trans 'VCPU' %}</th>
|
||||||
|
<th>{% trans 'Memory' %}</th>
|
||||||
|
<th data-sortable="false" style="width:205px;">{% trans 'Actions' %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="searchable">
|
||||||
|
{% for host, inst in all_host_vms.items %}
|
||||||
|
{% for vm, info in inst.items %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br><small><em>{{ info.title }}</em></small></td>
|
||||||
|
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a><br><small><em>{% if info.userinstances.count > 0 %}{{ info.userinstances.first_user.user.username }}{% if info.userinstances.count > 1 %} (+{{ info.userinstances.count|add:"-1" }}){% endif %}{% endif %}</em></small></td>
|
||||||
|
<td>
|
||||||
|
{% ifequal info.status 1 %}<span class="text-success">{% trans "Active" %}</span>{% endifequal %}
|
||||||
|
{% ifequal info.status 5 %}<span class="text-danger">{% trans "Off" %}</span>{% endifequal %}
|
||||||
|
{% ifequal info.status 3 %}<span class="text-warning">{% trans "Suspend" %}</span>{% endifequal %}
|
||||||
|
</td>
|
||||||
|
<td>{{ info.vcpu }}</td>
|
||||||
|
<td>{{ info.memory|filesizeformat }}</td>
|
||||||
|
<td><form action="" method="post" role="form">{% csrf_token %}
|
||||||
|
<input type="hidden" name="name" value="{{ vm }}"/>
|
||||||
|
<input type="hidden" name="compute_id" value="{{ host.0 }}"/>
|
||||||
|
{% ifequal info.status 5 %}
|
||||||
|
{% if info.is_template %}
|
||||||
|
<button class="btn btn-sm btn-default" type="button" name="clone" title="{% trans "Clone" %}" onclick="goto_instance_clone({{ host.0 }}, '{{ vm }}');">
|
||||||
|
<span class="glyphicon glyphicon-duplicate"></span>
|
||||||
|
</button>
|
||||||
|
{% else %}
|
||||||
|
<button class="btn btn-sm btn-default" type="submit" name="poweron" title="{% trans "Power On" %}">
|
||||||
|
<span class="glyphicon glyphicon-play"></span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
|
||||||
|
<span class="glyphicon glyphicon-pause"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
||||||
|
<span class="glyphicon glyphicon-off"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
||||||
|
<span class="glyphicon glyphicon-refresh"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
|
||||||
|
<span class="glyphicon glyphicon-eye-open"></span>
|
||||||
|
</button>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal info.status 3 %}
|
||||||
|
<button class="btn btn-sm btn-default" type="submit" name="resume" title="{% trans "Resume" %}">
|
||||||
|
<span class="glyphicon glyphicon-play"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Suspend" %}">
|
||||||
|
<span class="glyphicon glyphicon-pause"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Off" %}">
|
||||||
|
<span class="glyphicon glyphicon-off"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Power Cycle" %}">
|
||||||
|
<span class="glyphicon glyphicon-refresh"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "VNC Console" %}">
|
||||||
|
<span class="glyphicon glyphicon-eye-open"></span>
|
||||||
|
</button>
|
||||||
|
{% endifequal %}
|
||||||
|
{% ifequal info.status 1 %}
|
||||||
|
<button class="btn btn-sm btn-default disabled" title="{% trans "Power On" %}">
|
||||||
|
<span class="glyphicon glyphicon-play"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default" type="submit" name="suspend" title="{% trans "Suspend" %}">
|
||||||
|
<span class="glyphicon glyphicon-pause"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default" type="submit" name="poweroff" title="{% trans "Power Off" %}" onclick="return confirm('Are you sure?')">
|
||||||
|
<span class="glyphicon glyphicon-off"></span>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-default" type="submit" name="powercycle" title="{% trans "Power Cycle" %}" onclick="return confirm('Are you sure?')">
|
||||||
|
<span class="glyphicon glyphicon-refresh"></span>
|
||||||
|
</button>
|
||||||
|
<a href="#" class="btn btn-sm btn-default" onclick='open_console("{{ host.0 }}-{{ info.uuid }}")' title="{% trans "Console" %}">
|
||||||
|
<span class="glyphicon glyphicon-eye-open"></span>
|
||||||
|
</a>
|
||||||
|
{% endifequal %}
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block script %}
|
{% block script %}
|
||||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||||
|
@ -170,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 %}
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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:
|
try:
|
||||||
status = connection_manager.host_is_up(comp.type, comp.hostname)
|
all_host_vms = get_host_instances(request, compute)
|
||||||
if status:
|
except libvirtError as lib_err:
|
||||||
try:
|
error_messages.append(lib_err)
|
||||||
conn = wvmHostDetails(comp, comp.login, comp.password, comp.type)
|
|
||||||
comp_node_info = conn.get_node_info()
|
|
||||||
comp_mem = conn.get_memory_usage()
|
|
||||||
comp_instances = conn.get_host_instances(True)
|
|
||||||
|
|
||||||
if comp_instances:
|
|
||||||
comp_info= {
|
|
||||||
"id": comp.id,
|
|
||||||
"name": comp.name,
|
|
||||||
"status": status,
|
|
||||||
"cpu": comp_node_info[3],
|
|
||||||
"mem_size": comp_node_info[2],
|
|
||||||
"mem_perc": comp_mem['percent']
|
|
||||||
}
|
|
||||||
all_host_vms[comp_info["id"], comp_info["name"], comp_info["status"], comp_info["cpu"],
|
|
||||||
comp_info["mem_size"], comp_info["mem_perc"]] = comp_instances
|
|
||||||
for vm, info in comp_instances.items():
|
|
||||||
refresh_instance_database(comp_info, vm, info)
|
|
||||||
|
|
||||||
conn.close()
|
|
||||||
except libvirtError as lib_err:
|
|
||||||
error_messages.append(lib_err)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
name = request.POST.get('name', '')
|
|
||||||
compute_id = request.POST.get('compute_id', '')
|
|
||||||
instance = Instance.objects.get(compute_id=compute_id, name=name)
|
|
||||||
try:
|
try:
|
||||||
conn = wvmInstances(instance.compute.hostname,
|
instances_actions(request)
|
||||||
instance.compute.login,
|
|
||||||
instance.compute.password,
|
|
||||||
instance.compute.type)
|
|
||||||
if 'poweron' in request.POST:
|
|
||||||
msg = _("Power On")
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
conn.start(name)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
if 'poweroff' in request.POST:
|
|
||||||
msg = _("Power Off")
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
conn.shutdown(name)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
if 'powercycle' in request.POST:
|
|
||||||
msg = _("Power Cycle")
|
|
||||||
conn.force_shutdown(name)
|
|
||||||
conn.start(name)
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
if 'getvvfile' in request.POST:
|
|
||||||
msg = _("Send console.vv file")
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None, charset='utf-8')
|
|
||||||
response.writelines('[virt-viewer]\n')
|
|
||||||
response.writelines('type=' + conn.graphics_type(name) + '\n')
|
|
||||||
response.writelines('host=' + conn.graphics_listen(name) + '\n')
|
|
||||||
response.writelines('port=' + conn.graphics_port(name) + '\n')
|
|
||||||
response.writelines('title=' + conn.domain_name(name) + '\n')
|
|
||||||
response.writelines('password=' + conn.graphics_passwd(name) + '\n')
|
|
||||||
response.writelines('enable-usbredir=1\n')
|
|
||||||
response.writelines('disable-effects=all\n')
|
|
||||||
response.writelines('secure-attention=ctrl+alt+ins\n')
|
|
||||||
response.writelines('release-cursor=ctrl+alt\n')
|
|
||||||
response.writelines('fullscreen=1\n')
|
|
||||||
response.writelines('delete-this-file=1\n')
|
|
||||||
response['Content-Disposition'] = 'attachment; filename="console.vv"'
|
|
||||||
return response
|
|
||||||
|
|
||||||
if request.user.is_superuser:
|
|
||||||
|
|
||||||
if 'suspend' in request.POST:
|
|
||||||
msg = _("Suspend")
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
conn.suspend(name)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
if 'resume' in request.POST:
|
|
||||||
msg = _("Resume")
|
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
|
||||||
conn.resume(name)
|
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
|
||||||
|
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
error_messages.append(lib_err)
|
error_messages.append(lib_err)
|
||||||
addlogmsg(request.user.username, instance.name, lib_err.message)
|
addlogmsg(request.user.username, instance.name, lib_err.message)
|
||||||
|
|
||||||
view_style = settings.VIEW_INSTANCES_LIST_STYLE
|
|
||||||
|
|
||||||
return render(request, 'instances.html', locals())
|
return render(request, 'instances.html', locals())
|
||||||
|
|
||||||
|
|
||||||
|
@ -206,7 +104,7 @@ def instance(request, compute_id, vname):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
error_messages = []
|
error_messages = []
|
||||||
#messages = []
|
# messages = []
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
computes = Compute.objects.all().order_by('name')
|
computes = Compute.objects.all().order_by('name')
|
||||||
computes_count = computes.count()
|
computes_count = computes.count()
|
||||||
|
@ -217,8 +115,8 @@ def instance(request, compute_id, vname):
|
||||||
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||||
try:
|
try:
|
||||||
userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
|
userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
|
||||||
instance__name=vname,
|
instance__name=vname,
|
||||||
user__id=request.user.id)
|
user__id=request.user.id)
|
||||||
except UserInstance.DoesNotExist:
|
except UserInstance.DoesNotExist:
|
||||||
userinstance = None
|
userinstance = None
|
||||||
|
|
||||||
|
@ -231,15 +129,15 @@ def instance(request, compute_id, vname):
|
||||||
return 0
|
return 0
|
||||||
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
|
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
|
||||||
if 'K' == size_str[-1]:
|
if 'K' == size_str[-1]:
|
||||||
return long(float(size_str[:-1]))<<10
|
return long(float(size_str[:-1])) << 10
|
||||||
elif 'M' == size_str[-1]:
|
elif 'M' == size_str[-1]:
|
||||||
return long(float(size_str[:-1]))<<20
|
return long(float(size_str[:-1])) << 20
|
||||||
elif 'G' == size_str[-1]:
|
elif 'G' == size_str[-1]:
|
||||||
return long(float(size_str[:-1]))<<30
|
return long(float(size_str[:-1])) << 30
|
||||||
elif 'T' == size_str[-1]:
|
elif 'T' == size_str[-1]:
|
||||||
return long(float(size_str[:-1]))<<40
|
return long(float(size_str[:-1])) << 40
|
||||||
elif 'P' == size_str[-1]:
|
elif 'P' == size_str[-1]:
|
||||||
return long(float(size_str[:-1]))<<50
|
return long(float(size_str[:-1])) << 50
|
||||||
else:
|
else:
|
||||||
return long(float(size_str))
|
return long(float(size_str))
|
||||||
|
|
||||||
|
@ -268,15 +166,15 @@ def instance(request, compute_id, vname):
|
||||||
if connection_manager.host_is_up(usr_inst.instance.compute.type,
|
if connection_manager.host_is_up(usr_inst.instance.compute.type,
|
||||||
usr_inst.instance.compute.hostname):
|
usr_inst.instance.compute.hostname):
|
||||||
conn = wvmInstance(usr_inst.instance.compute,
|
conn = wvmInstance(usr_inst.instance.compute,
|
||||||
usr_inst.instance.compute.login,
|
usr_inst.instance.compute.login,
|
||||||
usr_inst.instance.compute.password,
|
usr_inst.instance.compute.password,
|
||||||
usr_inst.instance.compute.type,
|
usr_inst.instance.compute.type,
|
||||||
usr_inst.instance.name)
|
usr_inst.instance.name)
|
||||||
cpu += int(conn.get_vcpu())
|
cpu += int(conn.get_vcpu())
|
||||||
memory += int(conn.get_memory())
|
memory += int(conn.get_memory())
|
||||||
for disk in conn.get_disk_device():
|
for disk in conn.get_disk_devices():
|
||||||
if disk['size']:
|
if disk['size']:
|
||||||
disk_size += int(disk['size'])>>30
|
disk_size += int(disk['size']) >> 30
|
||||||
|
|
||||||
if ua.max_instances > 0 and instance > ua.max_instances:
|
if ua.max_instances > 0 and instance > ua.max_instances:
|
||||||
msg = "instance"
|
msg = "instance"
|
||||||
|
@ -301,7 +199,7 @@ def instance(request, compute_id, vname):
|
||||||
dev_base = "vd"
|
dev_base = "vd"
|
||||||
else:
|
else:
|
||||||
dev_base = "sd"
|
dev_base = "sd"
|
||||||
existing_devs = [ disk['dev'] for disk in disks ]
|
existing_devs = [disk['dev'] for disk in disks]
|
||||||
for l in string.lowercase:
|
for l in string.lowercase:
|
||||||
dev = dev_base + l
|
dev = dev_base + l
|
||||||
if dev not in existing_devs:
|
if dev not in existing_devs:
|
||||||
|
@ -348,6 +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:
|
||||||
|
@ -377,7 +277,7 @@ def instance(request, compute_id, vname):
|
||||||
console_port = conn.get_console_port()
|
console_port = conn.get_console_port()
|
||||||
console_keymap = conn.get_console_keymap()
|
console_keymap = conn.get_console_keymap()
|
||||||
console_listen_address = conn.get_console_listen_addr()
|
console_listen_address = conn.get_console_listen_addr()
|
||||||
snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k:k['date'])
|
snapshots = sorted(conn.get_snapshot(), reverse=True, key=lambda k: k['date'])
|
||||||
inst_xml = conn._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
inst_xml = conn._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||||
has_managed_save_image = conn.get_managed_save_image()
|
has_managed_save_image = conn.get_managed_save_image()
|
||||||
console_passwd = conn.get_console_passwd()
|
console_passwd = conn.get_console_passwd()
|
||||||
|
@ -387,6 +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,10 +313,14 @@ def instance(request, compute_id, vname):
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if 'poweron' in request.POST:
|
if 'poweron' in request.POST:
|
||||||
conn.start()
|
if instance.is_template:
|
||||||
msg = _("Power On")
|
msg = _("Templates cannot be started.")
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
error_messages.append(msg)
|
||||||
return HttpResponseRedirect(request.get_full_path() + '#poweron')
|
else:
|
||||||
|
conn.start()
|
||||||
|
msg = _("Power On")
|
||||||
|
addlogmsg(request.user.username, instance.name, msg)
|
||||||
|
return HttpResponseRedirect(request.get_full_path() + '#poweron')
|
||||||
|
|
||||||
if 'powercycle' in request.POST:
|
if 'powercycle' in request.POST:
|
||||||
conn.force_shutdown()
|
conn.force_shutdown()
|
||||||
|
@ -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', '')
|
||||||
|
@ -518,12 +425,12 @@ def instance(request, compute_id, vname):
|
||||||
disks_new = []
|
disks_new = []
|
||||||
for disk in disks:
|
for disk in disks:
|
||||||
input_disk_size = filesizefstr(request.POST.get('disk_size_' + disk['dev'], ''))
|
input_disk_size = filesizefstr(request.POST.get('disk_size_' + disk['dev'], ''))
|
||||||
if input_disk_size > disk['size']+(64<<20):
|
if input_disk_size > disk['size'] + (64 << 20):
|
||||||
disk['size_new'] = input_disk_size
|
disk['size_new'] = input_disk_size
|
||||||
disks_new.append(disk)
|
disks_new.append(disk)
|
||||||
disk_sum = sum([disk['size']>>30 for disk in disks_new])
|
disk_sum = sum([disk['size'] >> 30 for disk in disks_new])
|
||||||
disk_new_sum = sum([disk['size_new']>>30 for disk in disks_new])
|
disk_new_sum = sum([disk['size_new'] >> 30 for disk in disks_new])
|
||||||
quota_msg = check_user_quota(0, int(new_vcpu)-vcpu, int(new_memory)-memory, disk_new_sum-disk_sum)
|
quota_msg = check_user_quota(0, int(new_vcpu) - vcpu, int(new_memory) - memory, disk_new_sum - disk_sum)
|
||||||
if not request.user.is_superuser and quota_msg:
|
if not request.user.is_superuser and quota_msg:
|
||||||
msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name))
|
msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name))
|
||||||
error_messages.append(msg)
|
error_messages.append(msg)
|
||||||
|
@ -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,13 +712,12 @@ def instance(request, compute_id, vname):
|
||||||
addlogmsg(request.user.username, instance.name, msg)
|
addlogmsg(request.user.username, instance.name, msg)
|
||||||
return HttpResponseRedirect(request.get_full_path() + '#users')
|
return HttpResponseRedirect(request.get_full_path() + '#users')
|
||||||
|
|
||||||
|
|
||||||
if request.user.is_superuser or request.user.userattributes.can_clone_instances:
|
if request.user.is_superuser or request.user.userattributes.can_clone_instances:
|
||||||
if 'clone' in request.POST:
|
if 'clone' in request.POST:
|
||||||
clone_data = {}
|
clone_data = {}
|
||||||
clone_data['name'] = request.POST.get('name', '')
|
clone_data['name'] = request.POST.get('name', '')
|
||||||
|
|
||||||
disk_sum = sum([disk['size']>>30 for disk in disks])
|
disk_sum = sum([disk['size'] >> 30 for disk in disks])
|
||||||
quota_msg = check_user_quota(1, vcpu, memory, disk_sum)
|
quota_msg = check_user_quota(1, vcpu, memory, disk_sum)
|
||||||
check_instance = Instance.objects.filter(name=clone_data['name'])
|
check_instance = Instance.objects.filter(name=clone_data['name'])
|
||||||
|
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -964,7 +1092,7 @@ def _get_dhcp_mac_address(vname):
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def guess_mac_address(request, vname):
|
def guess_mac_address(request, vname):
|
||||||
data = { 'vname': vname }
|
data = {'vname': vname}
|
||||||
mac = _get_dhcp_mac_address(vname)
|
mac = _get_dhcp_mac_address(vname)
|
||||||
if not mac:
|
if not mac:
|
||||||
mac = _get_random_mac_address()
|
mac = _get_random_mac_address()
|
||||||
|
@ -1008,7 +1136,7 @@ def guess_clone_name(request):
|
||||||
@login_required
|
@login_required
|
||||||
def check_instance(request, vname):
|
def check_instance(request, vname):
|
||||||
check_instance = Instance.objects.filter(name=vname)
|
check_instance = Instance.objects.filter(name=vname)
|
||||||
data = { 'vname': vname, 'exists': False }
|
data = {'vname': vname, 'exists': False}
|
||||||
if check_instance:
|
if check_instance:
|
||||||
data['exists'] = True
|
data['exists'] = True
|
||||||
return HttpResponse(json.dumps(data))
|
return HttpResponse(json.dumps(data))
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
0
nwfilters/__init__.py
Normal file
6
nwfilters/admin.py
Normal file
6
nwfilters/admin.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
8
nwfilters/apps.py
Normal file
8
nwfilters/apps.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class NwfiltersConfig(AppConfig):
|
||||||
|
name = 'nwfilters'
|
0
nwfilters/migrations/__init__.py
Normal file
0
nwfilters/migrations/__init__.py
Normal file
6
nwfilters/models.py
Normal file
6
nwfilters/models.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
32
nwfilters/templates/create_nwfilter_block.html
Normal file
32
nwfilters/templates/create_nwfilter_block.html
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
{% load i18n %}
|
||||||
|
{% if request.user.is_superuser %}
|
||||||
|
<a href="#AddNWFilter" type="button" class="btn btn-success pull-right" data-toggle="modal">
|
||||||
|
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Modal Secret -->
|
||||||
|
<div class="modal fade" id="AddNWFilter" tabindex="-1" role="dialog" aria-labelledby="AddNWFilter" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">{% trans "Create New NWFilter" %}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-xs-12" id="xmlheight">
|
||||||
|
<input type="hidden" name="nwfilter_xml"/>
|
||||||
|
<textarea id="editor" name="from_xml"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary" name="create_nwfilter">{% trans "Create" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div> <!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
{% endif %}
|
223
nwfilters/templates/nwfilter.html
Normal file
223
nwfilters/templates/nwfilter.html
Normal 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">×</button>
|
||||||
|
<h4 class="modal-title">{% trans "Add New NWFilter Rule" %}</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-xs-12" id="xmlheight">
|
||||||
|
<input type="hidden" name="nwfilterrule_xml"/>
|
||||||
|
<textarea id="rule_editor"></textarea>
|
||||||
|
</div>
|
||||||
|
<p><small><em>If there is a rule which has same attributes it replaces that rule...</em></small></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary" name="add_nwfilter_rule">{% trans "Add" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div> <!-- /.modal-content -->
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 45px;">{% trans "Rule" %}</th>
|
||||||
|
<th>{% trans "ActionType" %}</th>
|
||||||
|
<th>{% trans "Direction" %}</th>
|
||||||
|
<th>{% trans "Priority" %}</th>
|
||||||
|
<th>{% trans "Statematch" %}</th>
|
||||||
|
<th>{% trans "Directives" %}</th>
|
||||||
|
<th colspan="3">{% trans "Action" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for rule in rules %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td>{{ rule.action }}</td>
|
||||||
|
<td>{{ rule.direction }}</td>
|
||||||
|
<td>{{ rule.priority }}</td>
|
||||||
|
<td>{{ rule.statematch }}</td>
|
||||||
|
<td>{{ rule.directives }}</td>
|
||||||
|
<td style="width:30px;">
|
||||||
|
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||||
|
<input type="hidden" name="action" value="{{ rule.action }}">
|
||||||
|
<input type="hidden" name="direction" value="{{ rule.direction }}">
|
||||||
|
<input type="hidden" name="priority" value="{{ rule.priority }}">
|
||||||
|
<button type="submit" class="btn btn-sm btn-default" name="del_nwfilter_rule" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
(function ($) {
|
||||||
|
$('#filter').keyup(function () {
|
||||||
|
var rex = new RegExp($(this).val(), 'i');
|
||||||
|
$('.searchable tr').hide();
|
||||||
|
$('.searchable tr').filter(function () {
|
||||||
|
return rex.test($(this).text());
|
||||||
|
}).show();
|
||||||
|
})
|
||||||
|
}(jQuery));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src="{% static "js/ace.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
var editor = ace.edit("edit_editor");
|
||||||
|
editor.getSession().setMode("ace/mode/xml");
|
||||||
|
|
||||||
|
var edit_input = $('input[name="edit_xml"]');
|
||||||
|
editor.getSession().on("change",function () {
|
||||||
|
edit_input.val(editor.getSession().getValue());
|
||||||
|
})
|
||||||
|
|
||||||
|
var rule_editor = ace.edit("rule_editor");
|
||||||
|
rule_editor.getSession().setMode("ace/mode/xml");
|
||||||
|
|
||||||
|
var rule_input = $('input[name="nwfilterrule_xml"]');
|
||||||
|
rule_editor.getSession().on("change",function () {
|
||||||
|
rule_input.val(rule_editor.getSession().getValue());
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
171
nwfilters/templates/nwfilters.html
Normal file
171
nwfilters/templates/nwfilters.html
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% block title %}{% trans "NWFilters" %} - {{ compute.name }}{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
<!-- Page Heading -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
{% include 'create_nwfilter_block.html' %}
|
||||||
|
<h1 class="page-header">{{ compute.name }}</h1>
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="active">
|
||||||
|
<i class="fa fa-dashboard"></i> <a href="{% url 'overview' compute.id %}">{% trans "Overview" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-sitemap"></i> <a href="{% url 'networks' compute.id %}">{% trans "Networks" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-filter"></i> {% trans "NWFilters" %}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /.row -->
|
||||||
|
|
||||||
|
{% include 'errors_block.html' %}
|
||||||
|
{% include 'messages_block.html' %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="pull-right">
|
||||||
|
<input id="filter" class="form-control" type="text" placeholder="Search">
|
||||||
|
</div>
|
||||||
|
<h3 class="page-header">{% trans "NWFilters" %}</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% if nwfilters %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped sortable-theme-bootstrap" data-sortable>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 45px;">#</th>
|
||||||
|
<th>{% trans "UUID" %}</th>
|
||||||
|
<th>{% trans "Name" %}</th>
|
||||||
|
<th data-sortable="false" colspan="3">{% trans "Action" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="searchable">
|
||||||
|
{% for nwfilter in nwfilters %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ forloop.counter }}</td>
|
||||||
|
<td><a href="{% url "nwfilter" compute.id nwfilter.name %} ">{{ nwfilter.uuid }}</a></td>
|
||||||
|
<td>{{ nwfilter.name }}</td>
|
||||||
|
<td style="width:30px;">
|
||||||
|
<div class="modal fade" id="Show{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="showNWFilter" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">{% trans "Details of NWFilter" %}: <span class="text-danger">{{ nwfilter.name }}</span></h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<pre>
|
||||||
|
<code>
|
||||||
|
{{ nwfilter.xml }}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
</div>
|
||||||
|
</div> <!-- /.modal-content -->
|
||||||
|
</div> <!-- /.modal-dialog -->
|
||||||
|
</div> <!-- /.modal -->
|
||||||
|
<a data-toggle="modal" href="#Show{{ forloop.counter }}" class="btn btn-sm btn-default" title="{% trans "Show" %}"><i class="fa fa-eye"></i></a>
|
||||||
|
</td>
|
||||||
|
<td style="width:30px;">
|
||||||
|
<div class="modal fade" id="Clone{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addNwFilterLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">{% trans "Clone nwfilter" %} <span class="text-danger">{{ nwfilter.name }}</span></h4>
|
||||||
|
</div>
|
||||||
|
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input type="text" class="form-control" name="cln_name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
|
||||||
|
<input type="hidden" name="nwfiltername" value="{{ nwfilter.name }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary" name="cln_nwfilter">{% trans "Clone" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div> <!-- /.modal-content -->
|
||||||
|
</div> <!-- /.modal-dialog -->
|
||||||
|
</div> <!-- /.modal -->
|
||||||
|
<a data-toggle="modal" href="#Clone{{ forloop.counter }}" class="btn btn-sm btn-default" title="{% trans "Clone" %}"><i class="fa fa-files-o"></i></a>
|
||||||
|
</td>
|
||||||
|
<td style="width:30px;">
|
||||||
|
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||||
|
<input type="hidden" name="nwfiltername" value="{{ nwfilter.name }}">
|
||||||
|
<button type="submit" class="btn btn-sm btn-default" name="del_nwfilter" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||||
|
<i class="fa fa-trash"></i>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="alert alert-warning alert-dismissable">
|
||||||
|
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||||
|
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any NWFilters" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
(function ($) {
|
||||||
|
$('#filter').keyup(function () {
|
||||||
|
var rex = new RegExp($(this).val(), 'i');
|
||||||
|
$('.searchable tr').hide();
|
||||||
|
$('.searchable tr').filter(function () {
|
||||||
|
return rex.test($(this).text());
|
||||||
|
}).show();
|
||||||
|
})
|
||||||
|
}(jQuery));
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="{% static "js/ace.js" %}"></script>
|
||||||
|
<script>
|
||||||
|
var editor = ace.edit("editor");
|
||||||
|
editor.getSession().setMode("ace/mode/xml");
|
||||||
|
|
||||||
|
var input = $('input[name="nwfilter_xml"]');
|
||||||
|
editor.getSession().on("change",function () {
|
||||||
|
input.val(editor.getSession().getValue());
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
6
nwfilters/tests.py
Normal file
6
nwfilters/tests.py
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
211
nwfilters/views.py
Normal file
211
nwfilters/views.py
Normal file
|
@ -0,0 +1,211 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from computes.models import Compute
|
||||||
|
from vrtManager import util
|
||||||
|
from vrtManager.nwfilters import wvmNWFilters, wvmNWFilter
|
||||||
|
from vrtManager.instance import wvmInstances, wvmInstance
|
||||||
|
from libvirt import libvirtError
|
||||||
|
from logs.views import addlogmsg
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def nwfilters(request, compute_id):
|
||||||
|
"""
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not request.user.is_superuser:
|
||||||
|
return HttpResponseRedirect(reverse('index'))
|
||||||
|
|
||||||
|
error_messages = []
|
||||||
|
nwfilters_all = []
|
||||||
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = wvmNWFilters(compute.hostname,
|
||||||
|
compute.login,
|
||||||
|
compute.password,
|
||||||
|
compute.type)
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if 'create_nwfilter' in request.POST:
|
||||||
|
xml = request.POST.get('nwfilter_xml', '')
|
||||||
|
if xml:
|
||||||
|
try:
|
||||||
|
util.etree.fromstring(xml)
|
||||||
|
name = util.get_xml_path(xml, '/filter/@name')
|
||||||
|
uuid = util.get_xml_path(xml, '/filter/uuid')
|
||||||
|
except util.etree.ParseError:
|
||||||
|
name = None
|
||||||
|
|
||||||
|
for nwf in nwfilters:
|
||||||
|
if name == nwf.name():
|
||||||
|
error_msg = _("A network filter with this name already exists")
|
||||||
|
raise Exception(error_msg)
|
||||||
|
if uuid == nwf.UUIDString():
|
||||||
|
error_msg = _("A network filter with this uuid already exists")
|
||||||
|
raise Exception(error_msg)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
msg = _("Creating NWFilter: %s" % name)
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
addlogmsg(request.user.username, compute.hostname, msg)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
error_messages.append(lib_err.message)
|
||||||
|
addlogmsg(request.user.username, compute.hostname, lib_err.message)
|
||||||
|
|
||||||
|
if 'del_nwfilter' in request.POST:
|
||||||
|
name = request.POST.get('nwfiltername','')
|
||||||
|
msg = _("Deleting NWFilter: %s" % name)
|
||||||
|
in_use = False
|
||||||
|
nwfilter = conn.get_nwfilter(name)
|
||||||
|
|
||||||
|
is_conn = wvmInstances(compute.hostname, compute.login, compute.password, compute.type)
|
||||||
|
instances = is_conn.get_instances()
|
||||||
|
for inst in instances:
|
||||||
|
# if in_use: break
|
||||||
|
i_conn = wvmInstance(compute.hostname, compute.login, compute.password, compute.type, inst)
|
||||||
|
dom_filterrefs = i_conn.get_filterrefs()
|
||||||
|
|
||||||
|
if name in dom_filterrefs:
|
||||||
|
in_use = True
|
||||||
|
msg = _("NWFilter is in use by %s. Cannot be deleted." % inst)
|
||||||
|
error_messages.append(msg)
|
||||||
|
addlogmsg(request.user.username, compute.hostname, msg)
|
||||||
|
i_conn.close()
|
||||||
|
break
|
||||||
|
|
||||||
|
is_conn.close()
|
||||||
|
if nwfilter and not in_use:
|
||||||
|
nwfilter.undefine()
|
||||||
|
addlogmsg(request.user.username, compute.hostname, msg)
|
||||||
|
|
||||||
|
if 'cln_nwfilter' in request.POST:
|
||||||
|
|
||||||
|
name = request.POST.get('nwfiltername','')
|
||||||
|
cln_name = request.POST.get('cln_name', name + '-clone')
|
||||||
|
|
||||||
|
conn.clone_nwfilter(name,cln_name)
|
||||||
|
msg = _("Cloning NWFilter %s as %s" % (name, cln_name))
|
||||||
|
addlogmsg(request.user.username, compute.hostname, msg)
|
||||||
|
|
||||||
|
for nwf in conn.get_nwfilters():
|
||||||
|
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
error_messages.append(lib_err)
|
||||||
|
addlogmsg(request.user.username, compute.hostname, lib_err)
|
||||||
|
except Exception as err:
|
||||||
|
error_messages.append(err)
|
||||||
|
addlogmsg(request.user.username, compute.hostname, err)
|
||||||
|
|
||||||
|
return render(request, 'nwfilters.html', {'error_messages': error_messages,
|
||||||
|
'nwfilters': nwfilters_all,
|
||||||
|
'compute': compute})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def nwfilter(request, compute_id, nwfltr):
|
||||||
|
|
||||||
|
error_messages = []
|
||||||
|
nwfilters_all = []
|
||||||
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
nwfilter = wvmNWFilter(compute.hostname,
|
||||||
|
compute.login,
|
||||||
|
compute.password,
|
||||||
|
compute.type,
|
||||||
|
nwfltr)
|
||||||
|
conn = wvmNWFilters(compute.hostname,
|
||||||
|
compute.login,
|
||||||
|
compute.password,
|
||||||
|
compute.type)
|
||||||
|
|
||||||
|
for nwf in conn.get_nwfilters():
|
||||||
|
nwfilters_all.append(conn.get_nwfilter_info(nwf))
|
||||||
|
|
||||||
|
uuid = nwfilter.get_uuid()
|
||||||
|
name = nwfilter.get_name()
|
||||||
|
xml = nwfilter.get_xml()
|
||||||
|
rules = nwfilter.get_rules()
|
||||||
|
refs = nwfilter.get_filter_refs()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
|
||||||
|
if 'edit_nwfilter' in request.POST:
|
||||||
|
new_xml = request.POST.get('edit_xml', '')
|
||||||
|
|
||||||
|
if new_xml:
|
||||||
|
nwfilter.delete()
|
||||||
|
try:
|
||||||
|
conn.create_nwfilter(new_xml)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
raise libvirtError(lib_err)
|
||||||
|
|
||||||
|
if 'del_nwfilter_rule' in request.POST:
|
||||||
|
action = request.POST.get('action', '')
|
||||||
|
direction = request.POST.get('direction', '')
|
||||||
|
priority = request.POST.get('priority', '')
|
||||||
|
|
||||||
|
new_xml = nwfilter.delete_rule(action, direction, priority)
|
||||||
|
nwfilter.delete()
|
||||||
|
try:
|
||||||
|
conn.create_nwfilter(new_xml)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
raise libvirtError(lib_err)
|
||||||
|
|
||||||
|
if 'del_nwfilter_ref' in request.POST:
|
||||||
|
ref_name = request.POST.get('ref')
|
||||||
|
new_xml = nwfilter.delete_ref(ref_name)
|
||||||
|
nwfilter.delete()
|
||||||
|
try:
|
||||||
|
conn.create_nwfilter(new_xml)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
raise libvirtError(lib_err)
|
||||||
|
|
||||||
|
if 'add_nwfilter_rule' in request.POST:
|
||||||
|
rule_xml = request.POST.get('nwfilterrule_xml', '')
|
||||||
|
if not rule_xml:
|
||||||
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
new_xml = nwfilter.add_rule(rule_xml)
|
||||||
|
nwfilter.delete()
|
||||||
|
try:
|
||||||
|
conn.create_nwfilter(new_xml)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
raise libvirtError(lib_err)
|
||||||
|
|
||||||
|
if 'add_nwfilter_ref' in request.POST:
|
||||||
|
ref_name = request.POST.get('nwfilters_select', '')
|
||||||
|
if not ref_name:
|
||||||
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
new_xml = nwfilter.add_ref(ref_name)
|
||||||
|
nwfilter.delete()
|
||||||
|
try:
|
||||||
|
conn.create_nwfilter(new_xml)
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
conn.create_nwfilter(xml)
|
||||||
|
raise libvirtError(lib_err)
|
||||||
|
|
||||||
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
conn.close()
|
||||||
|
nwfilter.close()
|
||||||
|
except libvirtError as lib_err:
|
||||||
|
error_messages.append(lib_err)
|
||||||
|
except Exception as error_msg:
|
||||||
|
error_messages.append(error_msg)
|
||||||
|
|
||||||
|
return render(request, 'nwfilter.html', locals())
|
|
@ -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
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
BIN
static/fonts/FontAwesome.otf
Normal file → Executable file
Binary file not shown.
BIN
static/fonts/fontawesome-webfont.eot
Normal file → Executable file
BIN
static/fonts/fontawesome-webfont.eot
Normal file → Executable file
Binary file not shown.
3228
static/fonts/fontawesome-webfont.svg
Normal file → Executable file
3228
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
BIN
static/fonts/fontawesome-webfont.ttf
Normal file → Executable file
Binary file not shown.
BIN
static/fonts/fontawesome-webfont.woff
Normal file → Executable file
BIN
static/fonts/fontawesome-webfont.woff
Normal file → Executable file
Binary file not shown.
BIN
static/fonts/fontawesome-webfont.woff2
Normal file → Executable file
BIN
static/fonts/fontawesome-webfont.woff2
Normal file → Executable file
Binary file not shown.
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
@ -167,7 +168,7 @@ def storage(request, compute_id, pool):
|
||||||
try:
|
try:
|
||||||
vol = conn.get_volume(volname)
|
vol = conn.get_volume(volname)
|
||||||
vol.delete(0)
|
vol.delete(0)
|
||||||
messages.success(request,_('Volume: {} is deleted.'.format(volname)))
|
messages.success(request, _('Volume: {} is deleted.'.format(volname)))
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
except libvirtError as lib_err:
|
except libvirtError as lib_err:
|
||||||
error_messages.append(lib_err.message)
|
error_messages.append(lib_err.message)
|
||||||
|
@ -197,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())
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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" %}>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
125
vrtManager/nwfilters.py
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
from vrtManager.connection import wvmConnect
|
||||||
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
|
|
||||||
|
class wvmNWFilters(wvmConnect):
|
||||||
|
def get_nwfilter_info(self, name):
|
||||||
|
nwfilter = self.get_nwfilter(name)
|
||||||
|
xml = nwfilter.XMLDesc(0)
|
||||||
|
uuid = nwfilter.UUIDString()
|
||||||
|
return {'name': name, 'uuid': uuid, 'xml': xml}
|
||||||
|
|
||||||
|
def create_nwfilter(self, xml):
|
||||||
|
self.wvm.nwfilterDefineXML(xml)
|
||||||
|
|
||||||
|
def clone_nwfilter(self,name, cln_name):
|
||||||
|
nwfilter = self.get_nwfilter(name)
|
||||||
|
if nwfilter:
|
||||||
|
tree = ElementTree.fromstring(nwfilter.XMLDesc(0))
|
||||||
|
tree.set('name', cln_name)
|
||||||
|
uuid = tree.find('uuid')
|
||||||
|
tree.remove(uuid)
|
||||||
|
self.create_nwfilter(ElementTree.tostring(tree))
|
||||||
|
|
||||||
|
|
||||||
|
class wvmNWFilter(wvmConnect):
|
||||||
|
def __init__(self, host, login, passwd, conn, nwfiltername):
|
||||||
|
wvmConnect.__init__(self, host, login, passwd, conn)
|
||||||
|
self.nwfilter = self.get_nwfilter(nwfiltername)
|
||||||
|
|
||||||
|
def _XMLDesc(self, flags):
|
||||||
|
return self.nwfilter.XMLDesc(flags)
|
||||||
|
|
||||||
|
def get_uuid(self):
|
||||||
|
return self.nwfilter.UUIDString()
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.nwfilter.name()
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.nwfilter.undefine()
|
||||||
|
|
||||||
|
def get_xml(self):
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
uuid = tree.find('uuid')
|
||||||
|
tree.remove(uuid)
|
||||||
|
return ElementTree.tostring(tree)
|
||||||
|
|
||||||
|
def get_filter_refs(self):
|
||||||
|
refs = []
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
for ref in tree.findall("./filterref"):
|
||||||
|
refs.append(ref.get('filter'))
|
||||||
|
return refs
|
||||||
|
|
||||||
|
def get_rules(self):
|
||||||
|
rules = []
|
||||||
|
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
for r in tree.findall("./rule"):
|
||||||
|
rule_action = r.get('action')
|
||||||
|
rule_direction = r.get('direction')
|
||||||
|
rule_priority = r.get('priority')
|
||||||
|
rule_statematch = r.get('statematch')
|
||||||
|
|
||||||
|
rule_directives = r.find("./")
|
||||||
|
if rule_directives is not None:
|
||||||
|
rule_directives = ElementTree.tostring(rule_directives)
|
||||||
|
|
||||||
|
rule_info = {
|
||||||
|
"action": rule_action,
|
||||||
|
"direction": rule_direction,
|
||||||
|
"priority": rule_priority,
|
||||||
|
"statematch": rule_statematch,
|
||||||
|
"directives": rule_directives
|
||||||
|
}
|
||||||
|
|
||||||
|
rules.append(rule_info)
|
||||||
|
|
||||||
|
return rules
|
||||||
|
|
||||||
|
def delete_ref(self, name):
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
for ref in tree.findall("./filterref"):
|
||||||
|
if name == ref.get('filter'):
|
||||||
|
tree.remove(ref)
|
||||||
|
break
|
||||||
|
return ElementTree.tostring(tree)
|
||||||
|
|
||||||
|
def delete_rule(self, action, direction, priority):
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
|
||||||
|
rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (action, direction, priority))
|
||||||
|
if rule_tree:
|
||||||
|
tree.remove(rule_tree[0])
|
||||||
|
|
||||||
|
return ElementTree.tostring(tree)
|
||||||
|
|
||||||
|
def add_ref(self, name):
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
element = ElementTree.Element("filterref")
|
||||||
|
element.attrib['filter'] = name
|
||||||
|
tree.append(element)
|
||||||
|
return ElementTree.tostring(tree)
|
||||||
|
|
||||||
|
def add_rule(self, xml):
|
||||||
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
rule = ElementTree.fromstring(xml)
|
||||||
|
|
||||||
|
rule_action = rule.get('action')
|
||||||
|
rule_direction = rule.get('direction')
|
||||||
|
rule_priority = rule.get('priority')
|
||||||
|
rule_directives = rule.find("./")
|
||||||
|
rule_tree = tree.findall("./rule[@action='%s'][@direction='%s'][@priority='%s']" % (rule_action, rule_direction, rule_priority))
|
||||||
|
|
||||||
|
if rule_tree:
|
||||||
|
rule_tree[0].append(rule_directives)
|
||||||
|
else:
|
||||||
|
element = ElementTree.Element("rule")
|
||||||
|
element.attrib['action'] = rule_action
|
||||||
|
element.attrib['direction'] = rule_direction
|
||||||
|
element.attrib['priority'] = rule_priority
|
||||||
|
element.append(rule_directives)
|
||||||
|
tree.append(element)
|
||||||
|
|
||||||
|
return ElementTree.tostring(tree)
|
|
@ -1,5 +1,6 @@
|
||||||
from vrtManager import util
|
from vrtManager 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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
Loading…
Reference in a new issue