mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +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>
|
||||
<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 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>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
<li class="active">
|
||||
<i class="fa fa-dashboard"></i> {% trans "Overview" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-server"></i> <a href="{% url 'instances' compute.id %}">{% trans "Instances" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-hdd-o"></i> <a href="{% url 'storages' compute.id %}">{% trans "Storages" %}</a>
|
||||
</li>
|
||||
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
@ -43,7 +49,7 @@
|
|||
<p>{% trans "Connection" %}</p>
|
||||
<p>{% trans "Details" %}</p>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<div class="col-xs-8 col-sm-9">
|
||||
<p>{{ hostname }}</p>
|
||||
<p>{% for arch, hpv in hypervisor.items %}
|
||||
<span class="glyphicon glyphicon-chevron-right"></span>
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
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 = [
|
||||
url(r'^$', views.computes, name='computes'),
|
||||
url(r'^overview/(?P<compute_id>[0-9]+)/$', views.overview, name='overview'),
|
||||
url(r'^statistics/(?P<compute_id>[0-9]+)/$',
|
||||
views.compute_graph, name='compute_graph'),
|
||||
url(r'^/', computes, name='computes'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/$', overview, name='overview'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/statistics$', compute_graph, name='compute_graph'),
|
||||
url(r'^(?P<compute_id>[0-9]+)/instances/$', instances, name='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()
|
||||
hypervisor = conn.hypervisor_type()
|
||||
mem_usage = conn.get_memory_usage()
|
||||
emulator = conn.emulator()
|
||||
emulator = conn.get_emulator(host_arch)
|
||||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
/nav>
|
||||
</nav>
|
||||
<div id='main_container' class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -100,6 +100,9 @@
|
|||
|
||||
host = document.getElementById("host").value;
|
||||
port = document.getElementById("port").value;
|
||||
if (window.location.protocol == 'https:') {
|
||||
scheme = "wss://";
|
||||
}
|
||||
password = document.getElementById("password").value;
|
||||
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
function connect()
|
||||
{
|
||||
var host, port, password, scheme = "ws://", uri;
|
||||
|
||||
console.log('>> connect');
|
||||
// By default, use the host and port of server that served this file
|
||||
//host = spice_query_var('host', window.location.hostname);
|
||||
host = '{{ ws_host| safe }}';
|
||||
|
@ -170,7 +170,7 @@
|
|||
disconnect();
|
||||
}
|
||||
|
||||
|
||||
console.log('<< connect')
|
||||
}
|
||||
|
||||
function disconnect()
|
||||
|
|
|
@ -38,6 +38,7 @@ class NewVMForm(forms.Form):
|
|||
disk = forms.IntegerField(required=False)
|
||||
memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
|
||||
networks = forms.CharField(error_messages={'required': _('No Network pool has been choice')})
|
||||
nwfilter = forms.CharField(required=False)
|
||||
storage = forms.CharField(max_length=20, required=False)
|
||||
template = forms.CharField(required=False)
|
||||
images = forms.CharField(required=False)
|
||||
|
|
|
@ -6,461 +6,518 @@
|
|||
<link href="{% static "css/bootstrap-multiselect.css" %}" rel="stylesheet">
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% include 'create_flav_block.html' %}
|
||||
<h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
{% include 'errors_block.html' %}
|
||||
{% include 'pleasewaitdialog.html' %}
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
{% include 'errors_block.html' %}
|
||||
{% include 'pleasewaitdialog.html' %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p>
|
||||
<a class="btn btn-success" data-toggle="collapse" href="#addCustom" aria-expanded="false" aria-controls="addCustom">
|
||||
<div class="row" id="max-width-page">
|
||||
<div class="col-lg-12">
|
||||
<div role="tabpanel">
|
||||
<!-- 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" %}
|
||||
</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" %}
|
||||
</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" %}
|
||||
</a>
|
||||
</p>
|
||||
<div class="collapse" id="addCustom">
|
||||
<div class="well">
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Name" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="14" required pattern="[a-zA-Z0-9\.\-_]+">
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="flavor">
|
||||
{% 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>
|
||||
<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 }}">{% 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 %}
|
||||
{% else %}
|
||||
{% include 'create_flav_block.html' %}
|
||||
<h3 class="page-header">{% trans "Create from flavor" %}</h3>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ flavor.label }}</td>
|
||||
<td>{{ flavor.vcpu }}</td>
|
||||
<td>{{ flavor.memory }} {% trans "MB" %}</td>
|
||||
<td>{{ flavor.disk }} {% trans "GB" %}</td>
|
||||
<td style="width:5px;">
|
||||
<div class="modal fade" id="addVMflavor{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addVMFlavorLabel" 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 Virtual Machine" %} ({{ flavor.label }})</h4>
|
||||
</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 }}">
|
||||
<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>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>{{ flavor.label }}</td>
|
||||
<td>{{ flavor.vcpu }}</td>
|
||||
<td>{{ flavor.memory }} {% trans "MB" %}</td>
|
||||
<td>{{ flavor.disk }} {% trans "GB" %}</td>
|
||||
<td style="width:5px;">
|
||||
<div class="modal fade" id="addVMflavor{{ forloop.counter }}" tabindex="-1" role="dialog" aria-labelledby="addVMFlavorLabel" 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 Virtual Machine" %} ({{ flavor.label }})</h4>
|
||||
</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 class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<input type="hidden" name="cache_mode" value="default">
|
||||
<select name="storage" class="form-control">
|
||||
{% if storages %}
|
||||
{% for storage in storages %}
|
||||
<option value="{{ storage }}">{{ storage }}</option>
|
||||
<div class="col-sm-6">
|
||||
<input type="hidden" name="cache_mode" value="default">
|
||||
<select name="storage" class="form-control">
|
||||
{% if storages %}
|
||||
{% for storage in storages %}
|
||||
<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 %}
|
||||
{% 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>
|
||||
{% endif %}
|
||||
</select>
|
||||
{% for nwfilter in nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</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 }}">{% trans name %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "MAC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
|
||||
</div>
|
||||
</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 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>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">{% trans "MAC" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
|
||||
<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>
|
||||
<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 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>
|
||||
<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 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-lg-1 control-label">{% trans "CPU" %}</label>
|
||||
</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 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>
|
||||
<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-lg-1 control-label">{% trans "CPU" %}</label>
|
||||
</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>
|
||||
<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 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>
|
||||
<a data-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-default">
|
||||
<span class="glyphicon glyphicon-plus"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td style="width:5px;">
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<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?" %}')">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<a data-toggle="modal" href="#addVMflavor{{ forloop.counter }}" class="btn btn-sm btn-default">
|
||||
<span class="glyphicon glyphicon-plus"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td style="width:5px;">
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<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?" %}')">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</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>
|
||||
{% 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 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 %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/bootstrap-multiselect.js" %}"></script>
|
||||
|
|
|
@ -11,6 +11,7 @@ from vrtManager.create import wvmCreate
|
|||
from vrtManager import util
|
||||
from libvirt import libvirtError
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_CACHE
|
||||
from django.contrib import messages
|
||||
|
||||
@login_required
|
||||
|
@ -28,7 +29,6 @@ def create_instance(request, compute_id):
|
|||
storages = []
|
||||
networks = []
|
||||
meta_prealloc = False
|
||||
#computes = Compute.objects.all()
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
flavors = Flavor.objects.filter().order_by('id')
|
||||
|
||||
|
@ -40,9 +40,11 @@ def create_instance(request, compute_id):
|
|||
|
||||
storages = sorted(conn.get_storages(only_actives=True))
|
||||
networks = sorted(conn.get_networks())
|
||||
nwfilters = conn.get_nwfilters()
|
||||
instances = conn.get_instances()
|
||||
videos = conn.get_video()
|
||||
cache_modes = sorted(conn.get_cache_modes().items())
|
||||
default_cache = INSTANCE_VOLUME_DEFAULT_CACHE
|
||||
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
mac_auto = util.randomMAC()
|
||||
get_images = sorted(conn.get_storages_images())
|
||||
|
@ -99,6 +101,8 @@ def create_instance(request, compute_id):
|
|||
if data['name'] in instances:
|
||||
msg = _("A virtual machine with this name already exists")
|
||||
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 data['hdd_size']:
|
||||
if not data['mac']:
|
||||
|
@ -139,11 +143,11 @@ def create_instance(request, compute_id):
|
|||
try:
|
||||
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
|
||||
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'],
|
||||
data["listener_addr"], None, data["video"], data["console_pass"],
|
||||
data["listener_addr"], data["nwfilter"], data["video"], data["console_pass"],
|
||||
data['mac'])
|
||||
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||
create_instance.save()
|
||||
messages.success(request,"Instance is created.")
|
||||
messages.success(request, _("Instance is created."))
|
||||
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
|
||||
except libvirtError as lib_err:
|
||||
if data['hdd_size'] or volumes[clone_path]:
|
||||
|
|
|
@ -33,12 +33,24 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">{% trans "NWFilter" %}</label>
|
||||
<div class="col-sm-6">
|
||||
<select class="form-control" name="add-net-nwfilter">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for nwfilter in compute_nwfilters %}
|
||||
<option value="{{ nwfilter }}">{{ nwfilter }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
|
||||
<button type="submit" class="btn btn-primary" name="add_network">{% trans "Add" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div> <!-- /.modal-content -->
|
||||
</div> <!-- /.modal-dialog -->
|
||||
</div> <!-- /.modal -->
|
||||
|
|
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" %}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab">
|
||||
<span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span>
|
||||
{% trans "Snapshots" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
|
||||
<li role="presentation">
|
||||
<a href="#snapshots" class="action-button" aria-controls="snapshots" role="tab" data-toggle="tab">
|
||||
<span id="action-block" class="glyphicon glyphicon-camera" aria-hidden="true"></span>
|
||||
{% trans "Snapshots" %}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li role="presentation">
|
||||
<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>
|
||||
|
@ -342,11 +344,6 @@
|
|||
{% trans "Resize Instance" %}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#addvolume" aria-controls="addvolume" role="tab" data-toggle="tab">
|
||||
{% trans "Add New Volume" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content">
|
||||
|
@ -428,81 +425,7 @@
|
|||
{% endif %}
|
||||
<div class="clearfix"></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>
|
||||
|
@ -607,6 +530,11 @@
|
|||
{% trans "Media" %}
|
||||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#disks" aria-controls="disks" role="tab" data-toggle="tab">
|
||||
{% trans "Disk" %}
|
||||
</a>
|
||||
</li>
|
||||
{% if request.user.is_superuser %}
|
||||
<li role="presentation">
|
||||
<a href="#autostart" aria-controls="autostart" role="tab" data-toggle="tab">
|
||||
|
@ -614,6 +542,7 @@
|
|||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% if request.user.is_superuser or userinstance.is_vnc %}
|
||||
<li role="presentation">
|
||||
<a href="#vncsettings" aria-controls="vncsettings" role="tab" data-toggle="tab">
|
||||
|
@ -682,7 +611,7 @@
|
|||
</select>
|
||||
</div>
|
||||
<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>
|
||||
{% else %}
|
||||
<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 class="col-sm-2">
|
||||
<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>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -702,6 +635,73 @@
|
|||
{% endfor %}
|
||||
<div class="clearfix"></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 %}
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="autostart">
|
||||
<p>{% trans "Autostart your instance when host server is power on" %}</p>
|
||||
|
@ -833,39 +833,60 @@
|
|||
{% if request.user.is_superuser %}
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="network">
|
||||
<p>
|
||||
{% trans "Assign network device to bridge" %}
|
||||
{% include 'add_instance_network_block.html' %}
|
||||
{% trans "Assign network device to bridge" %}
|
||||
{% include 'add_instance_network_block.html' %}
|
||||
</p>
|
||||
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
|
||||
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
||||
<p style="font-weight:bold;">{% trans "Network devices" %}</p>
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
<form method="post" role="form">{% csrf_token %}
|
||||
{% for network in networks %}
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label" style="font-weight:normal;">eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<label>eth{{ forloop.counter0 }}({{ network.target|default:"no target" }})</label>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<input type="text" class="form-control" name="net-source-{{ forloop.counter0 }}" value="{{ network.nic }}" disabled/>
|
||||
<div class="panel-body">
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "MAC" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.mac }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<input class="form-control" type="text" name="net-mac-{{ forloop.counter0 }}" value="{{ network.mac }}"/>
|
||||
</div>
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1 control-label">{% trans "NIC" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.nic }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<select class="form-control" name="net-source-{{ forloop.counter0 }}">
|
||||
{% for c_net in compute_networks %}
|
||||
<option value="net:{{ c_net }}" {% ifequal c_net network.nic %} selected {% endifequal %}>Network {{ c_net }}</option>
|
||||
{% endfor %}
|
||||
{% for c_iface in compute_interfaces %}
|
||||
<option value="iface:{{ c_iface }}" {% ifequal c_iface network.nic %} selected {% endifequal %}>Interface {{ c_iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group form-inline">
|
||||
<label class="col-sm-2 col-sm-offset-1">{% trans "Filter" %} </label>
|
||||
<input class="form-control" type="text" value="{{ network.filterref }}" readonly/>
|
||||
<label class="control-label"><em>to</em></label>
|
||||
<select class="form-control" name="net-nwfilter-{{ forloop.counter0 }}">
|
||||
<option value="">{% trans "None" %}</option>
|
||||
{% for c_filters in compute_nwfilters %}
|
||||
<option value="{{ c_filters }}" {% ifequal c_filters network.filterref %} selected {% endifequal %}>{{ c_filters }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
<select name="net-source-{{ forloop.counter0 }}" class="form-control" id="network_select" onchange="network_select_enable()">
|
||||
{% for c_nets in compute_networks %}
|
||||
<option value="net:{{ c_nets }}" {% if c_nets == network.nic %}selected{% endif %}>Network {{ c_nets }}</option>
|
||||
{% endfor %}
|
||||
{% for c_iface in compute_interfaces %}
|
||||
<option value="iface:{{ c_iface }}" {% if c_iface == network.nic %}selected{% endif %}>Interface {{ c_iface }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<div class="panel-footer">
|
||||
<button class="btn btn-sm btn-primary" name="change_network" title="{% trans "Change" %}">{% trans "Change" %}</button>
|
||||
<button class="btn btn-sm pull-right btn-danger" value="{{ network.mac }}" name="delete_network" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure?" %}')">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% ifequal status 5 %}
|
||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn btn-lg btn-success pull-right" id="btn_change_network" name="change_network" disabled>{% trans "Change" %}</button>
|
||||
{% endifequal %}
|
||||
</form>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if request.user.is_superuser or request.user.userattributes.can_clone_instances %}
|
||||
|
@ -926,7 +947,7 @@
|
|||
<input id="disk_name-{{ disk.dev }}" type="text" class="form-control" name="disk-{{ disk.dev }}" value="{{ disk.image }}"/>
|
||||
</div>
|
||||
{% 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">
|
||||
<input type="checkbox" name="meta-{{ disk.dev }}" value="true" style="margin-top: 10px;">
|
||||
</div>
|
||||
|
@ -1221,6 +1242,42 @@
|
|||
{% endblock %}
|
||||
{% block 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>
|
||||
var editor = ace.edit("editor");
|
||||
editor.getSession().setMode("ace/mode/xml");
|
||||
|
@ -1232,13 +1289,13 @@
|
|||
</script>
|
||||
<script>
|
||||
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>
|
||||
function random_mac(net) {
|
||||
$.getJSON('/instance/random_mac_address/', function(data) {
|
||||
$('input[name="'+net+'"]').val(data['mac']);
|
||||
$.getJSON('{% url 'random_mac_address' %}', function (data) {
|
||||
$('input[name="' + net + '"]').val(data['mac']);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
@ -1254,14 +1311,15 @@
|
|||
<script>
|
||||
function guess_mac_address(src_elem, net) {
|
||||
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']);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function guess_clone_name() {
|
||||
$.getJSON('/instance/guess_clone_name/', function(data) {
|
||||
$.getJSON('{% url 'guess_clone_name' %}', function(data) {
|
||||
guessed_name = data['name'].split(".")[0];
|
||||
$('#clone_name').val(guessed_name);
|
||||
update_clone_disk_name(guessed_name);
|
||||
|
@ -1339,7 +1397,8 @@
|
|||
});
|
||||
$(document).ready(function () {
|
||||
// set vdi url
|
||||
$.get("/datasource/vdi/{{ vname }}/", function(data) {
|
||||
|
||||
$.get("{% url 'vdi_url' vname %}", function(data) {
|
||||
$("#vdi_url_input").attr("value", data);
|
||||
$("#vdi_url").attr("href", data);
|
||||
});
|
||||
|
@ -1370,12 +1429,6 @@
|
|||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
function network_select_enable(){
|
||||
// set network button enabled
|
||||
$('button[name="change_network"]').removeAttr('disabled');
|
||||
}
|
||||
</script>
|
||||
<script src="{% static "js/Chart.min.js" %}"></script>
|
||||
<script>
|
||||
$('#chartgraphs').on('shown.bs.tab', function (event) {
|
||||
|
@ -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');
|
||||
$(btnsect).each(function () {
|
||||
if ($(this).attr('href') === '#settings') {
|
||||
|
@ -1574,7 +1627,8 @@
|
|||
</script>
|
||||
<script>
|
||||
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 = "";
|
||||
$.each(data, function(id) {
|
||||
row = data[id];
|
||||
|
@ -1586,5 +1640,12 @@
|
|||
$("#logs_table > tbody").html(logs);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function(){
|
||||
$('[data-toggle="popover"]').popover({
|
||||
placement : 'top'
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,145 +1,162 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block title %}{% trans "Instances" %}{% endblock %}
|
||||
{% block title %}{% trans "Instances" %} - {{ compute.name }}{% endblock %}
|
||||
{% block style %}
|
||||
<link rel="stylesheet" href="{% static "css/sortable-theme-bootstrap.css" %}" />
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Page Heading -->
|
||||
<!-- Page Heading -->
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
{% 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 %}
|
||||
{% 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>
|
||||
<h1 class="page-header">{{ compute.name }}</h1>
|
||||
</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="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 '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 class="alert alert-warning alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>
|
||||
<i class="fa fa-exclamation-triangle"></i> <strong>{% trans "Warning:" %}</strong> {% trans "Hypervisor doesn't have any instances" %}
|
||||
</div>
|
||||
</div>
|
||||
</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 %}
|
||||
{% block script %}
|
||||
<script src="{% static "js/sortable.min.js" %}"></script>
|
||||
|
@ -170,15 +187,15 @@
|
|||
</script>
|
||||
<script>
|
||||
function goto_instance_clone(compute, instance) {
|
||||
window.location = "/instance/" + compute + "/" + instance + "/#clone";
|
||||
window.location = "/instances/" + compute + "/" + instance + "/#clone";
|
||||
}
|
||||
</script>
|
||||
{% if request.user.is_superuser %}
|
||||
<script>
|
||||
function goto_compute() {
|
||||
var compute = $("#compute_select").val();
|
||||
window.location = "/compute/" + compute + "/create/";
|
||||
window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);
|
||||
}
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
|
@ -2,6 +2,7 @@ from django.conf.urls import url
|
|||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', views.allinstances, name='allinstances'),
|
||||
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'^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.connection import connection_manager
|
||||
from vrtManager.create import wvmCreate
|
||||
from vrtManager.storage import wvmStorage
|
||||
from vrtManager.util import randomPasswd
|
||||
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE
|
||||
from logs.views import addlogmsg
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request):
|
||||
"""
|
||||
:param request:
|
||||
:return:
|
||||
"""
|
||||
|
||||
return HttpResponseRedirect(reverse('instances'))
|
||||
return HttpResponseRedirect(reverse('allinstances'))
|
||||
|
||||
|
||||
@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:
|
||||
:return:
|
||||
"""
|
||||
|
||||
error_messages = []
|
||||
all_host_vms = {}
|
||||
all_user_vms = {}
|
||||
computes = Compute.objects.all().order_by("name")
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
|
||||
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:
|
||||
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})
|
||||
all_user_vms = get_user_instances(request)
|
||||
else:
|
||||
for comp in computes:
|
||||
status = connection_manager.host_is_up(comp.type, comp.hostname)
|
||||
if status:
|
||||
try:
|
||||
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)
|
||||
try:
|
||||
all_host_vms = get_host_instances(request, compute)
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
||||
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:
|
||||
conn = wvmInstances(instance.compute.hostname,
|
||||
instance.compute.login,
|
||||
instance.compute.password,
|
||||
instance.compute.type)
|
||||
if 'poweron' in request.POST:
|
||||
msg = _("Power On")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
conn.start(name)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
|
||||
if 'poweroff' in request.POST:
|
||||
msg = _("Power Off")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
conn.shutdown(name)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
|
||||
if 'powercycle' in request.POST:
|
||||
msg = _("Power Cycle")
|
||||
conn.force_shutdown(name)
|
||||
conn.start(name)
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
|
||||
if 'getvvfile' in request.POST:
|
||||
msg = _("Send console.vv file")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
response = HttpResponse(content='', content_type='application/x-virt-viewer', status=200, reason=None, charset='utf-8')
|
||||
response.writelines('[virt-viewer]\n')
|
||||
response.writelines('type=' + conn.graphics_type(name) + '\n')
|
||||
response.writelines('host=' + conn.graphics_listen(name) + '\n')
|
||||
response.writelines('port=' + conn.graphics_port(name) + '\n')
|
||||
response.writelines('title=' + conn.domain_name(name) + '\n')
|
||||
response.writelines('password=' + conn.graphics_passwd(name) + '\n')
|
||||
response.writelines('enable-usbredir=1\n')
|
||||
response.writelines('disable-effects=all\n')
|
||||
response.writelines('secure-attention=ctrl+alt+ins\n')
|
||||
response.writelines('release-cursor=ctrl+alt\n')
|
||||
response.writelines('fullscreen=1\n')
|
||||
response.writelines('delete-this-file=1\n')
|
||||
response['Content-Disposition'] = 'attachment; filename="console.vv"'
|
||||
return response
|
||||
|
||||
if request.user.is_superuser:
|
||||
|
||||
if 'suspend' in request.POST:
|
||||
msg = _("Suspend")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
conn.suspend(name)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
|
||||
if 'resume' in request.POST:
|
||||
msg = _("Resume")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
conn.resume(name)
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
|
||||
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, 'instances.html', locals())
|
||||
|
||||
|
||||
|
@ -206,7 +104,7 @@ def instance(request, compute_id, vname):
|
|||
"""
|
||||
|
||||
error_messages = []
|
||||
#messages = []
|
||||
# messages = []
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
computes = Compute.objects.all().order_by('name')
|
||||
computes_count = computes.count()
|
||||
|
@ -217,8 +115,8 @@ def instance(request, compute_id, vname):
|
|||
console_listen_addresses = settings.QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
try:
|
||||
userinstance = UserInstance.objects.get(instance__compute_id=compute_id,
|
||||
instance__name=vname,
|
||||
user__id=request.user.id)
|
||||
instance__name=vname,
|
||||
user__id=request.user.id)
|
||||
except UserInstance.DoesNotExist:
|
||||
userinstance = None
|
||||
|
||||
|
@ -231,15 +129,15 @@ def instance(request, compute_id, vname):
|
|||
return 0
|
||||
size_str = size_str.encode('ascii', 'ignore').upper().translate(None, " B")
|
||||
if 'K' == size_str[-1]:
|
||||
return long(float(size_str[:-1]))<<10
|
||||
return long(float(size_str[:-1])) << 10
|
||||
elif 'M' == size_str[-1]:
|
||||
return long(float(size_str[:-1]))<<20
|
||||
return long(float(size_str[:-1])) << 20
|
||||
elif 'G' == size_str[-1]:
|
||||
return long(float(size_str[:-1]))<<30
|
||||
return long(float(size_str[:-1])) << 30
|
||||
elif 'T' == size_str[-1]:
|
||||
return long(float(size_str[:-1]))<<40
|
||||
return long(float(size_str[:-1])) << 40
|
||||
elif 'P' == size_str[-1]:
|
||||
return long(float(size_str[:-1]))<<50
|
||||
return long(float(size_str[:-1])) << 50
|
||||
else:
|
||||
return long(float(size_str))
|
||||
|
||||
|
@ -268,16 +166,16 @@ def instance(request, compute_id, vname):
|
|||
if connection_manager.host_is_up(usr_inst.instance.compute.type,
|
||||
usr_inst.instance.compute.hostname):
|
||||
conn = wvmInstance(usr_inst.instance.compute,
|
||||
usr_inst.instance.compute.login,
|
||||
usr_inst.instance.compute.password,
|
||||
usr_inst.instance.compute.type,
|
||||
usr_inst.instance.name)
|
||||
usr_inst.instance.compute.login,
|
||||
usr_inst.instance.compute.password,
|
||||
usr_inst.instance.compute.type,
|
||||
usr_inst.instance.name)
|
||||
cpu += int(conn.get_vcpu())
|
||||
memory += int(conn.get_memory())
|
||||
for disk in conn.get_disk_device():
|
||||
for disk in conn.get_disk_devices():
|
||||
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:
|
||||
msg = "instance"
|
||||
if settings.QUOTA_DEBUG:
|
||||
|
@ -301,7 +199,7 @@ def instance(request, compute_id, vname):
|
|||
dev_base = "vd"
|
||||
else:
|
||||
dev_base = "sd"
|
||||
existing_devs = [ disk['dev'] for disk in disks ]
|
||||
existing_devs = [disk['dev'] for disk in disks]
|
||||
for l in string.lowercase:
|
||||
dev = dev_base + l
|
||||
if dev not in existing_devs:
|
||||
|
@ -348,6 +246,7 @@ def instance(request, compute_id, vname):
|
|||
vname)
|
||||
compute_networks = sorted(conn.get_networks())
|
||||
compute_interfaces = sorted(conn.get_ifaces())
|
||||
compute_nwfilters = conn.get_nwfilters()
|
||||
status = conn.get_status()
|
||||
autostart = conn.get_autostart()
|
||||
vcpu = conn.get_vcpu()
|
||||
|
@ -357,13 +256,14 @@ def instance(request, compute_id, vname):
|
|||
cur_memory = conn.get_cur_memory()
|
||||
title = conn.get_title()
|
||||
description = conn.get_description()
|
||||
disks = conn.get_disk_device()
|
||||
media = conn.get_media_device()
|
||||
disks = conn.get_disk_devices()
|
||||
media = conn.get_media_devices()
|
||||
if len(media) != 0:
|
||||
media_iso = sorted(conn.get_iso_media())
|
||||
else:
|
||||
media_iso = []
|
||||
networks = conn.get_net_device()
|
||||
|
||||
vcpu_range = conn.get_max_cpus()
|
||||
memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384]
|
||||
if memory not in memory_range:
|
||||
|
@ -377,7 +277,7 @@ def instance(request, compute_id, vname):
|
|||
console_port = conn.get_console_port()
|
||||
console_keymap = conn.get_console_keymap()
|
||||
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)
|
||||
has_managed_save_image = conn.get_managed_save_image()
|
||||
console_passwd = conn.get_console_passwd()
|
||||
|
@ -387,6 +287,7 @@ def instance(request, compute_id, vname):
|
|||
cache_modes = sorted(conn.get_cache_modes().items())
|
||||
default_cache = settings.INSTANCE_VOLUME_DEFAULT_CACHE
|
||||
default_format = settings.INSTANCE_VOLUME_DEFAULT_FORMAT
|
||||
default_owner = settings.INSTANCE_VOLUME_DEFAULT_OWNER
|
||||
formats = conn.get_image_formats()
|
||||
|
||||
busses = conn.get_busses()
|
||||
|
@ -412,10 +313,14 @@ def instance(request, compute_id, vname):
|
|||
|
||||
if request.method == 'POST':
|
||||
if 'poweron' in request.POST:
|
||||
conn.start()
|
||||
msg = _("Power On")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#poweron')
|
||||
if instance.is_template:
|
||||
msg = _("Templates cannot be started.")
|
||||
error_messages.append(msg)
|
||||
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:
|
||||
conn.force_shutdown()
|
||||
|
@ -442,7 +347,7 @@ def instance(request, compute_id, vname):
|
|||
if request.POST.get('delete_disk', ''):
|
||||
for snap in snapshots:
|
||||
conn.snapshot_delete(snap['name'])
|
||||
conn.delete_disk()
|
||||
conn.delete_all_disks()
|
||||
conn.delete()
|
||||
|
||||
instance = Instance.objects.get(compute_id=compute_id, name=vname)
|
||||
|
@ -450,7 +355,8 @@ def instance(request, compute_id, vname):
|
|||
instance.delete()
|
||||
|
||||
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()
|
||||
except UserInstance.DoesNotExist:
|
||||
pass
|
||||
|
@ -458,7 +364,7 @@ def instance(request, compute_id, vname):
|
|||
msg = _("Destroy")
|
||||
addlogmsg(request.user.username, instance_name, msg)
|
||||
|
||||
return HttpResponseRedirect(reverse('instances'))
|
||||
return HttpResponseRedirect(reverse('allinstances'))
|
||||
|
||||
if 'rootpasswd' in request.POST:
|
||||
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")
|
||||
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_cur_vcpu = request.POST.get('cur_vcpu', '')
|
||||
new_memory = request.POST.get('memory', '')
|
||||
|
@ -518,13 +425,13 @@ def instance(request, compute_id, vname):
|
|||
disks_new = []
|
||||
for disk in disks:
|
||||
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
|
||||
disks_new.append(disk)
|
||||
disk_sum = sum([disk['size']>>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)
|
||||
if not request.user.is_superuser and quota_msg:
|
||||
disks_new.append(disk)
|
||||
disk_sum = sum([disk['size'] >> 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)
|
||||
if not request.user.is_superuser and quota_msg:
|
||||
msg = _("User %s quota reached, cannot resize '%s'!" % (quota_msg, instance.name))
|
||||
error_messages.append(msg)
|
||||
else:
|
||||
|
@ -537,27 +444,76 @@ def instance(request, compute_id, vname):
|
|||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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,
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
compute.login,
|
||||
compute.password,
|
||||
compute.type)
|
||||
storage = request.POST.get('storage', '')
|
||||
name = request.POST.get('name', '')
|
||||
format = request.POST.get('format', '')
|
||||
format = request.POST.get('format', default_format)
|
||||
size = request.POST.get('size', 0)
|
||||
meta_prealloc = request.POST.get('meta_prealloc', False)
|
||||
bus = request.POST.get('bus', '')
|
||||
cache = request.POST.get('cache', '')
|
||||
bus = request.POST.get('bus', default_bus)
|
||||
cache = request.POST.get('cache', default_cache)
|
||||
target = get_new_disk_dev(disks, bus)
|
||||
|
||||
path = connCreate.create_volume(storage, name, size, format, meta_prealloc)
|
||||
|
||||
path = connCreate.create_volume(storage, name, size, format, meta_prealloc, default_owner)
|
||||
conn.attach_disk(path, target, subdriver=format, cache=cache, targetbus=bus)
|
||||
msg = _('Attach new disk')
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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', '')
|
||||
dev = request.POST.get('umount_iso', '')
|
||||
conn.umount_iso(dev, image)
|
||||
|
@ -565,7 +521,7 @@ def instance(request, compute_id, vname):
|
|||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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', '')
|
||||
dev = request.POST.get('mount_iso', '')
|
||||
conn.mount_iso(dev, image)
|
||||
|
@ -573,21 +529,21 @@ def instance(request, compute_id, vname):
|
|||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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', '')
|
||||
conn.create_snapshot(name)
|
||||
msg = _("New snapshot")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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', '')
|
||||
conn.snapshot_delete(snap_name)
|
||||
msg = _("Delete snapshot")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
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', '')
|
||||
conn.snapshot_revert(snap_name)
|
||||
msg = _("Successful revert snapshot: ")
|
||||
|
@ -667,7 +623,7 @@ def instance(request, compute_id, vname):
|
|||
msg = _("Set VNC type")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#vncsettings')
|
||||
|
||||
|
||||
if 'set_console_listen_address' in request.POST:
|
||||
console_type = request.POST.get('console_listen_address', '')
|
||||
conn.set_console_listen_addr(console_type)
|
||||
|
@ -689,6 +645,7 @@ def instance(request, compute_id, vname):
|
|||
return HttpResponseRedirect(reverse('instance', args=[new_compute.id, vname]))
|
||||
|
||||
if 'change_network' in request.POST:
|
||||
msg = _("Change network")
|
||||
network_data = {}
|
||||
|
||||
for post in request.POST:
|
||||
|
@ -700,31 +657,43 @@ def instance(request, compute_id, vname):
|
|||
network_data[post] = request.POST.get(post, '')
|
||||
|
||||
conn.change_network(network_data)
|
||||
msg = _("Edit network")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
msg = _("Network Devices are changed. Please reboot instance to activate.")
|
||||
messages.success(request, msg)
|
||||
if conn.get_status() != 5: messages.success(request, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#network')
|
||||
|
||||
if 'add_network' in request.POST:
|
||||
msg = _("Add network")
|
||||
mac = request.POST.get('add-net-mac')
|
||||
nwfilter = request.POST.get('add-net-nwfilter')
|
||||
(source, source_type) = get_network_tuple(request.POST.get('add-net-network'))
|
||||
|
||||
conn.add_network(mac, source, source_type)
|
||||
msg = _("Edit network")
|
||||
conn.add_network(mac, source, source_type, nwfilter=nwfilter)
|
||||
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
msg = _("Network Devices are changed. Please reboot instance to activate.")
|
||||
messages.success(request, msg)
|
||||
msg = _("Network Device is added. Please reboot instance to activate.")
|
||||
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')
|
||||
|
||||
if 'add_owner' in request.POST:
|
||||
user_id = int(request.POST.get('user_id', ''))
|
||||
|
||||
|
||||
if settings.ALLOW_INSTANCE_MULTIPLE_OWNER:
|
||||
check_inst = UserInstance.objects.filter(instance=instance, user_id=user_id)
|
||||
else:
|
||||
check_inst = UserInstance.objects.filter(instance=instance)
|
||||
|
||||
|
||||
if check_inst:
|
||||
msg = _("Owner already added")
|
||||
error_messages.append(msg)
|
||||
|
@ -743,19 +712,18 @@ def instance(request, compute_id, vname):
|
|||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#users')
|
||||
|
||||
|
||||
if request.user.is_superuser or request.user.userattributes.can_clone_instances:
|
||||
if 'clone' in request.POST:
|
||||
clone_data = {}
|
||||
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)
|
||||
check_instance = Instance.objects.filter(name=clone_data['name'])
|
||||
|
||||
|
||||
for post in request.POST:
|
||||
clone_data[post] = request.POST.get(post, '').strip()
|
||||
|
||||
|
||||
if clone_instance_auto_name and not clone_data['name']:
|
||||
auto_vname = clone_free_names[0]
|
||||
clone_data['name'] = auto_vname
|
||||
|
@ -764,7 +732,7 @@ def instance(request, compute_id, vname):
|
|||
disk_dev = "disk-{}".format(disk['dev'])
|
||||
disk_name = get_clone_disk_name(disk, vname, auto_vname)
|
||||
clone_data[disk_dev] = disk_name
|
||||
|
||||
|
||||
if not request.user.is_superuser and quota_msg:
|
||||
msg = _("User %s quota reached, cannot create '%s'!" % (quota_msg, clone_data['name']))
|
||||
error_messages.append(msg)
|
||||
|
@ -774,13 +742,20 @@ def instance(request, compute_id, vname):
|
|||
elif not re.match(r'^[a-zA-Z0-9-]+$', clone_data['name']):
|
||||
msg = _("Instance name '%s' contains invalid characters!" % clone_data['name'])
|
||||
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'])
|
||||
error_messages.append(msg)
|
||||
else:
|
||||
new_uuid = conn.clone_instance(clone_data)
|
||||
new_instance = Instance(compute_id=compute_id, name=clone_data['name'], uuid=new_uuid)
|
||||
new_instance = Instance(compute_id=compute_id, name=clone_data['name'])
|
||||
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.save()
|
||||
|
||||
|
@ -789,18 +764,19 @@ def instance(request, compute_id, vname):
|
|||
if settings.CLONE_INSTANCE_AUTO_MIGRATE:
|
||||
new_compute = Compute.objects.order_by('?').first()
|
||||
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.save()
|
||||
|
||||
|
||||
options = {}
|
||||
for post in request.POST:
|
||||
if post in ['title', 'description']:
|
||||
options[post] = request.POST.get(post, '')
|
||||
conn.set_options(options)
|
||||
|
||||
|
||||
msg = _("Edit options")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#options')
|
||||
|
@ -839,6 +815,158 @@ def inst_status(request, compute_id, vname):
|
|||
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
|
||||
def inst_graph(request, compute_id, vname):
|
||||
"""
|
||||
|
@ -964,7 +1092,7 @@ def _get_dhcp_mac_address(vname):
|
|||
|
||||
@login_required
|
||||
def guess_mac_address(request, vname):
|
||||
data = { 'vname': vname }
|
||||
data = {'vname': vname}
|
||||
mac = _get_dhcp_mac_address(vname)
|
||||
if not mac:
|
||||
mac = _get_random_mac_address()
|
||||
|
@ -1008,7 +1136,7 @@ def guess_clone_name(request):
|
|||
@login_required
|
||||
def check_instance(request, vname):
|
||||
check_instance = Instance.objects.filter(name=vname)
|
||||
data = { 'vname': vname, 'exists': False }
|
||||
data = {'vname': vname, 'exists': False}
|
||||
if check_instance:
|
||||
data['exists'] = True
|
||||
return HttpResponse(json.dumps(data))
|
||||
|
@ -1027,6 +1155,7 @@ def get_clone_disk_name(disk, prefix, clone_name=''):
|
|||
image = "{}-clone".format(disk['image'])
|
||||
return image
|
||||
|
||||
|
||||
def _get_clone_disks(disks, vname=''):
|
||||
clone_disks = []
|
||||
for disk in disks:
|
||||
|
@ -1042,6 +1171,7 @@ def _get_clone_disks(disks, vname=''):
|
|||
clone_disks.append(new_disk)
|
||||
return clone_disks
|
||||
|
||||
|
||||
def sshkeys(request, vname):
|
||||
"""
|
||||
:param request:
|
||||
|
@ -1089,7 +1219,7 @@ def delete_instance(instance, delete_disk=False):
|
|||
print("Deleting snapshot {}".format(snap['name']))
|
||||
conn.snapshot_delete(snap['name'])
|
||||
print("Deleting disks")
|
||||
conn.delete_disk()
|
||||
conn.delete_all_disks()
|
||||
|
||||
conn.delete()
|
||||
instance.delete()
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<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>
|
||||
|
@ -19,6 +22,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
<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>
|
||||
|
@ -21,6 +24,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> {% trans "Interfaces" %}
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<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>
|
||||
|
@ -19,6 +22,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
<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>
|
||||
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
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">
|
||||
<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>
|
||||
|
@ -24,6 +27,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> {% trans "Secrets" %}
|
||||
</li>
|
||||
|
|
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.
3230
static/fonts/fontawesome-webfont.svg
Normal file → Executable file
3230
static/fonts/fontawesome-webfont.svg
Normal file → Executable file
File diff suppressed because it is too large
Load diff
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 434 KiB |
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">
|
||||
<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>
|
||||
|
@ -25,6 +28,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
<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> {% trans "Storages" %}
|
||||
</li>
|
||||
|
@ -20,6 +23,9 @@
|
|||
<li>
|
||||
<i class="fa fa-wifi"></i> <a href="{% url 'interfaces' compute.id %}">{% trans "Interfaces" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-filter"></i> <a href="{% url 'nwfilters' compute.id %}">{% trans "NWFilters" %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-key"></i> <a href="{% url 'secrets' compute.id %}">{% trans "Secrets" %}</a>
|
||||
</li>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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.core.urlresolvers import reverse
|
||||
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 libvirt import libvirtError
|
||||
from django.contrib import messages
|
||||
import json
|
||||
|
||||
@login_required
|
||||
def storages(request, compute_id):
|
||||
|
@ -155,7 +156,7 @@ def storage(request, compute_id, pool):
|
|||
meta_prealloc = True
|
||||
try:
|
||||
conn.create_volume(data['name'], data['size'], data['format'], meta_prealloc)
|
||||
messages.success("Image file {} is created successfully".format(data['name']+".img"))
|
||||
messages.success(request, _("Image file {} is created successfully".format(data['name']+".img")))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
@ -167,7 +168,7 @@ def storage(request, compute_id, pool):
|
|||
try:
|
||||
vol = conn.get_volume(volname)
|
||||
vol.delete(0)
|
||||
messages.success(request,_('Volume: {} is deleted.'.format(volname)))
|
||||
messages.success(request, _('Volume: {} is deleted.'.format(volname)))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err.message)
|
||||
|
@ -197,14 +198,19 @@ def storage(request, compute_id, pool):
|
|||
format = None
|
||||
try:
|
||||
conn.clone_volume(data['image'], data['name'], format, meta_prealloc)
|
||||
messages.success(request, _("{} image cloned as {} successfully".format(data['image'],
|
||||
data['name'] + ".img")))
|
||||
messages.success(request, _("{} image cloned as {} successfully".format(data['image'], data['name'] + ".img")))
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
else:
|
||||
for msg_err in form.errors.values():
|
||||
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()
|
||||
|
||||
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/respond.js/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
<link rel="shortcut icon" href="{% static 'favicon.ico' %}">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<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>
|
||||
{% if request.user.is_superuser %}
|
||||
<li {% class_active request "^/compute" %}{% class_active request "^/create" %}>
|
||||
|
|
|
@ -354,9 +354,10 @@ class wvmConnect(object):
|
|||
|
||||
def get_dom_cap_xml(self):
|
||||
""" Return domcapabilities xml"""
|
||||
emulatorbin = self.emulator()
|
||||
machine = self.machine()
|
||||
|
||||
arch = self.wvm.getInfo()[0]
|
||||
machine = self.get_machines(arch)
|
||||
emulatorbin = self.get_emulator(arch)
|
||||
virttype = self.hypervisor_type()[arch][0]
|
||||
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
|
||||
|
||||
|
@ -389,6 +390,12 @@ class wvmConnect(object):
|
|||
interface.append(inface)
|
||||
return interface
|
||||
|
||||
def get_nwfilters(self):
|
||||
nwfilters = []
|
||||
for nwfilter in self.wvm.listNWFilters():
|
||||
nwfilters.append(nwfilter)
|
||||
return nwfilters
|
||||
|
||||
def get_cache_modes(self):
|
||||
"""Get cache available modes"""
|
||||
return {
|
||||
|
@ -411,13 +418,23 @@ class wvmConnect(object):
|
|||
return result
|
||||
return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
|
||||
|
||||
def emulator(self):
|
||||
def get_emulator(self, arch):
|
||||
"""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 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):
|
||||
"""Get available busses"""
|
||||
|
@ -443,7 +460,6 @@ class wvmConnect(object):
|
|||
|
||||
def get_video(self):
|
||||
""" Get available graphics video types """
|
||||
|
||||
def get_video_list(ctx):
|
||||
result = []
|
||||
for video_enum in ctx.xpath('/domainCapabilities/devices/video/enum'):
|
||||
|
@ -470,6 +486,9 @@ class wvmConnect(object):
|
|||
def get_network(self, net):
|
||||
return self.wvm.networkLookupByName(net)
|
||||
|
||||
def get_nwfilter(self, name):
|
||||
return self.wvm.nwfilterLookupByName(name)
|
||||
|
||||
def get_instance(self, name):
|
||||
return self.wvm.lookupByName(name)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from vrtManager import util
|
|||
from vrtManager.connection import wvmConnect
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
|
||||
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
|
||||
|
||||
|
||||
|
@ -52,7 +53,7 @@ class wvmCreate(wvmConnect):
|
|||
"""Get guest capabilities"""
|
||||
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
|
||||
stg = self.get_storage(storage)
|
||||
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
||||
|
@ -70,13 +71,17 @@ class wvmCreate(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
<compat>1.1</compat>
|
||||
<features>
|
||||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (name, size, alloc, image_format)
|
||||
</volume>""" % (name, size, alloc, image_format, owner['uid'], owner['guid'])
|
||||
stg.createXML(xml, metadata)
|
||||
try:
|
||||
stg.refresh(0)
|
||||
|
@ -110,7 +115,7 @@ class wvmCreate(wvmConnect):
|
|||
vol = self.get_volume_by_path(vol_path)
|
||||
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)
|
||||
stg = vol.storagePoolLookupByVolume()
|
||||
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
|
||||
|
@ -127,8 +132,8 @@ class wvmCreate(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
|
@ -137,7 +142,7 @@ class wvmCreate(wvmConnect):
|
|||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (clone, format)
|
||||
</volume>""" % (clone, format, owner['uid'], owner['guid'])
|
||||
stg.createXMLFrom(xml, vol, metadata)
|
||||
clone_vol = stg.storageVolLookupByName(clone)
|
||||
return clone_vol.path()
|
||||
|
|
|
@ -10,6 +10,7 @@ from datetime import datetime
|
|||
from vrtManager.connection import wvmConnect
|
||||
from vrtManager.storage import wvmStorage
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_TYPES
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
|
||||
|
||||
|
||||
class wvmInstances(wvmConnect):
|
||||
|
@ -187,6 +188,19 @@ class wvmInstance(wvmConnect):
|
|||
title = util.get_xml_path(self._XMLDesc(0), "/domain/title")
|
||||
return title if title else ''
|
||||
|
||||
def get_filterrefs(self):
|
||||
|
||||
def filterrefs(ctx):
|
||||
result = []
|
||||
for net in ctx.xpath('/domain/devices/interface'):
|
||||
filterref = net.xpath('filterref/@filter')
|
||||
if filterref:
|
||||
result.append(filterref[0])
|
||||
return result
|
||||
|
||||
return util.get_xml_path(self._XMLDesc(0), func=filterrefs)
|
||||
|
||||
|
||||
def get_description(self):
|
||||
description = util.get_xml_path(self._XMLDesc(0), "/domain/description")
|
||||
return description if description else ''
|
||||
|
@ -219,17 +233,18 @@ class wvmInstance(wvmConnect):
|
|||
mac_host = net.xpath('mac/@address')[0]
|
||||
network_host = net.xpath('source/@network|source/@bridge|source/@dev')[0]
|
||||
target_host = '' if not net.xpath('target/@dev') else net.xpath('target/@dev')[0]
|
||||
filterref_host = '' if not net.xpath('filterref/@filter') else net.xpath('filterref/@filter')[0]
|
||||
try:
|
||||
net = self.get_network(network_host)
|
||||
ip = get_mac_ipaddr(net, mac_host)
|
||||
except libvirtError as e:
|
||||
ip = None
|
||||
result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip})
|
||||
result.append({'mac': mac_host, 'nic': network_host, 'target': target_host,'ip': ip, 'filterref': filterref_host})
|
||||
return result
|
||||
|
||||
return util.get_xml_path(self._XMLDesc(0), func=networks)
|
||||
|
||||
def get_disk_device(self):
|
||||
def get_disk_devices(self):
|
||||
def disks(doc):
|
||||
result = []
|
||||
dev = None
|
||||
|
@ -244,6 +259,7 @@ class wvmInstance(wvmConnect):
|
|||
if device == 'disk':
|
||||
try:
|
||||
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]
|
||||
try:
|
||||
disk_format = disk.xpath('driver/@type')[0]
|
||||
|
@ -252,7 +268,9 @@ class wvmInstance(wvmConnect):
|
|||
try:
|
||||
vol = self.get_volume_by_path(src_fl)
|
||||
volume = vol.name()
|
||||
|
||||
disk_size = vol.info()[1]
|
||||
used_size = vol.info()[2]
|
||||
stg = vol.storagePoolLookupByVolume()
|
||||
storage = stg.name()
|
||||
except libvirtError:
|
||||
|
@ -261,13 +279,13 @@ class wvmInstance(wvmConnect):
|
|||
pass
|
||||
finally:
|
||||
result.append(
|
||||
{'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl,
|
||||
'format': disk_format, 'size': disk_size})
|
||||
{'dev': dev, 'bus': bus, 'image': volume, 'storage': storage, 'path': src_fl,
|
||||
'format': disk_format, 'size': disk_size, 'used': used_size})
|
||||
return result
|
||||
|
||||
return util.get_xml_path(self._XMLDesc(0), func=disks)
|
||||
|
||||
def get_media_device(self):
|
||||
def get_media_devices(self):
|
||||
def disks(doc):
|
||||
result = []
|
||||
dev = None
|
||||
|
@ -361,6 +379,25 @@ class wvmInstance(wvmConnect):
|
|||
xmldom = ElementTree.tostring(tree)
|
||||
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):
|
||||
cpu_usage = {}
|
||||
if self.get_status() == 1:
|
||||
|
@ -487,8 +524,7 @@ class wvmInstance(wvmConnect):
|
|||
return self._defineXML(newxml)
|
||||
|
||||
def get_console_socket(self):
|
||||
socket = util.get_xml_path(self._XMLDesc(0),
|
||||
"/domain/devices/graphics/@socket")
|
||||
socket = util.get_xml_path(self._XMLDesc(0), "/domain/devices/graphics/@socket")
|
||||
return socket
|
||||
|
||||
def get_console_type(self):
|
||||
|
@ -614,8 +650,8 @@ class wvmInstance(wvmConnect):
|
|||
iso.append(img)
|
||||
return iso
|
||||
|
||||
def delete_disk(self):
|
||||
disks = self.get_disk_device()
|
||||
def delete_all_disks(self):
|
||||
disks = self.get_disk_devices()
|
||||
for disk in disks:
|
||||
vol = self.get_volume_by_path(disk.get('path'))
|
||||
vol.delete(0)
|
||||
|
@ -700,16 +736,15 @@ class wvmInstance(wvmConnect):
|
|||
source_file = elm.get('file')
|
||||
if source_file:
|
||||
clone_dev_path.append(source_file)
|
||||
clone_path = os.path.join(os.path.dirname(source_file),
|
||||
target_file)
|
||||
clone_path = os.path.join(os.path.dirname(source_file), target_file)
|
||||
elm.set('file', clone_path)
|
||||
|
||||
vol = self.get_volume_by_path(source_file)
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0),
|
||||
"/volume/target/format/@type")
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
||||
|
||||
if vol_format == 'qcow2' and meta_prealloc:
|
||||
meta_prealloc = True
|
||||
|
||||
vol_clone_xml = """
|
||||
<volume>
|
||||
<name>%s</name>
|
||||
|
@ -718,7 +753,17 @@ class wvmInstance(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
</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.createXMLFrom(vol_clone_xml, vol, meta_prealloc)
|
||||
|
||||
|
@ -729,8 +774,7 @@ class wvmInstance(wvmConnect):
|
|||
elm.set('name', clone_name)
|
||||
|
||||
vol = self.get_volume_by_path(source_name)
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0),
|
||||
"/volume/target/format/@type")
|
||||
vol_format = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
|
||||
|
||||
vol_clone_xml = """
|
||||
<volume type='network'>
|
||||
|
@ -776,7 +820,7 @@ class wvmInstance(wvmConnect):
|
|||
bridge_name = net.bridgeName()
|
||||
return bridge_name
|
||||
|
||||
def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio'):
|
||||
def add_network(self, mac_address, source, source_type='net', interface_type='bridge', model='virtio', nwfilter=None):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
bridge_name = self.get_bridge_name(source, source_type)
|
||||
xml_interface = """
|
||||
|
@ -784,8 +828,13 @@ class wvmInstance(wvmConnect):
|
|||
<mac address='%s'/>
|
||||
<source bridge='%s'/>
|
||||
<model type='%s'/>
|
||||
</interface>
|
||||
""" % (interface_type, mac_address, bridge_name, model)
|
||||
""" % (interface_type, mac_address, bridge_name, model)
|
||||
if nwfilter:
|
||||
xml_interface += """
|
||||
<filterref filter='%s'/>
|
||||
""" % nwfilter
|
||||
xml_interface += """</interface>"""
|
||||
|
||||
if self.get_status() == 5:
|
||||
devices = tree.find('devices')
|
||||
elm_interface = ElementTree.fromstring(xml_interface)
|
||||
|
@ -793,20 +842,42 @@ class wvmInstance(wvmConnect):
|
|||
xmldom = ElementTree.tostring(tree)
|
||||
self._defineXML(xmldom)
|
||||
|
||||
def delete_network(self, mac_address):
|
||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||
devices = tree.find('devices')
|
||||
for interface in tree.findall('devices/interface'):
|
||||
source = interface.find('mac')
|
||||
if source.get('address', '') == mac_address:
|
||||
source = None
|
||||
devices.remove(interface)
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
self._defineXML(new_xml)
|
||||
|
||||
def change_network(self, network_data):
|
||||
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
|
||||
tree = ElementTree.fromstring(xml)
|
||||
|
||||
for num, interface in enumerate(tree.findall('devices/interface')):
|
||||
net_source = network_data['net-source-' + str(num)]
|
||||
net_source_type = network_data['net-source-' + str(num) + '-type']
|
||||
net_mac = network_data['net-mac-' + str(num)]
|
||||
net_filter = network_data['net-nwfilter-' + str(num)]
|
||||
bridge_name = self.get_bridge_name(net_source, net_source_type)
|
||||
if interface.get('type') == 'bridge':
|
||||
source = interface.find('mac')
|
||||
source.set('address', net_mac)
|
||||
source = interface.find('source')
|
||||
source.set('bridge', bridge_name)
|
||||
source = interface.find('filterref')
|
||||
|
||||
if net_filter:
|
||||
if source is not None: source.set('filter', net_filter)
|
||||
else:
|
||||
element = ElementTree.Element("filterref")
|
||||
element.attrib['filter'] = net_filter
|
||||
interface.append(element)
|
||||
else:
|
||||
if source is not None: interface.remove(source)
|
||||
|
||||
new_xml = ElementTree.tostring(tree)
|
||||
self._defineXML(new_xml)
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ class wvmInterface(wvmConnect):
|
|||
def get_ipv4_type(self):
|
||||
try:
|
||||
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:
|
||||
return 'static'
|
||||
else:
|
||||
|
@ -91,8 +91,8 @@ class wvmInterface(wvmConnect):
|
|||
|
||||
def get_ipv4(self):
|
||||
xml = self._XMLDesc()
|
||||
int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol/ip/@address")
|
||||
int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol/ip/@prefix")
|
||||
int_ipv4_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@address")
|
||||
int_ipv4_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv4']/ip/@prefix")
|
||||
if not int_ipv4_ip or not int_ipv4_mask:
|
||||
return None
|
||||
else:
|
||||
|
@ -101,7 +101,7 @@ class wvmInterface(wvmConnect):
|
|||
def get_ipv6_type(self):
|
||||
try:
|
||||
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:
|
||||
return 'static'
|
||||
else:
|
||||
|
@ -111,8 +111,8 @@ class wvmInterface(wvmConnect):
|
|||
|
||||
def get_ipv6(self):
|
||||
xml = self._XMLDesc()
|
||||
int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[2]/ip/@address")
|
||||
int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[2]/ip/@prefix")
|
||||
int_ipv6_ip = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@address")
|
||||
int_ipv6_mask = util.get_xml_path(xml, "/interface/protocol[@family='ipv6']/ip/@prefix")
|
||||
if not int_ipv6_ip or not int_ipv6_mask:
|
||||
return None
|
||||
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.connection import wvmConnect
|
||||
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as owner
|
||||
|
||||
|
||||
class wvmStorages(wvmConnect):
|
||||
|
@ -205,7 +206,7 @@ class wvmStorage(wvmConnect):
|
|||
)
|
||||
return vol_list
|
||||
|
||||
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False):
|
||||
def create_volume(self, name, size, vol_fmt='qcow2', metadata=False, owner=owner):
|
||||
size = int(size) * 1073741824
|
||||
storage_type = self.get_type()
|
||||
alloc = size
|
||||
|
@ -222,8 +223,8 @@ class wvmStorage(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
|
@ -232,10 +233,10 @@ class wvmStorage(wvmConnect):
|
|||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (name, size, alloc, vol_fmt)
|
||||
</volume>""" % (name, size, alloc, vol_fmt, owner['uid'], owner['guid'])
|
||||
self._createXML(xml, metadata)
|
||||
|
||||
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False):
|
||||
def clone_volume(self, name, target_file, vol_fmt=None, metadata=False, owner=owner):
|
||||
storage_type = self.get_type()
|
||||
if storage_type == 'dir':
|
||||
target_file += '.img'
|
||||
|
@ -250,8 +251,8 @@ class wvmStorage(wvmConnect):
|
|||
<target>
|
||||
<format type='%s'/>
|
||||
<permissions>
|
||||
<owner>107</owner>
|
||||
<group>107</group>
|
||||
<owner>%s</owner>
|
||||
<group>%s</group>
|
||||
<mode>0644</mode>
|
||||
<label>virt_image_t</label>
|
||||
</permissions>
|
||||
|
@ -260,5 +261,5 @@ class wvmStorage(wvmConnect):
|
|||
<lazy_refcounts/>
|
||||
</features>
|
||||
</target>
|
||||
</volume>""" % (target_file, vol_fmt)
|
||||
</volume>""" % (target_file, vol_fmt, owner['uid'],owner['guid'])
|
||||
self._createXMLFrom(xml, vol, metadata)
|
||||
|
|
|
@ -2,6 +2,7 @@ import random
|
|||
import lxml.etree as etree
|
||||
import libvirt
|
||||
import string
|
||||
import re
|
||||
|
||||
|
||||
def is_kvm_available(xml):
|
||||
|
@ -26,8 +27,11 @@ def randomMAC():
|
|||
def randomUUID():
|
||||
"""Generate a random UUID."""
|
||||
|
||||
u = [random.randint(0, 255) for dummy in range(0, 16)]
|
||||
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2, "%02x" * 6]) % tuple(u)
|
||||
u = [random.randint(0, 255) for ignore in range(0, 16)]
|
||||
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):
|
||||
|
@ -129,3 +133,23 @@ def pretty_bytes(val):
|
|||
return "%2.2f GB" % (val / (1024.0 * 1024.0 * 1024.0))
|
||||
else:
|
||||
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',
|
||||
'storages',
|
||||
'interfaces',
|
||||
'nwfilters',
|
||||
'instances',
|
||||
'secrets',
|
||||
'logs',
|
||||
|
@ -151,3 +152,5 @@ VIEW_INSTANCES_LIST_STYLE = 'grouped'
|
|||
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
|
||||
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
|
||||
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
|
||||
# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
|
||||
INSTANCE_VOLUME_DEFAULT_OWNER = {'uid': 0, 'guid': 0}
|
||||
|
|
|
@ -1,32 +1,18 @@
|
|||
from django.conf.urls import include, url
|
||||
|
||||
from instances.views import instances, instance, 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 instances.views import index
|
||||
from console.views import console
|
||||
# from django.contrib import admin
|
||||
|
||||
urlpatterns = [
|
||||
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'^computes/', include('computes.urls')),
|
||||
url(r'^logs/', include('logs.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'),
|
||||
# (r'^admin/', include(admin.site.urls)),
|
||||
|
|
Loading…
Reference in a new issue