1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-11-01 03:54:15 +00:00

Merge pull request #208 from catborise/master

New Features: Add/Remove CD-Rom Device, Enchance VM Creation
This commit is contained in:
Anatoliy Guskov 2018-12-18 07:29:59 +02:00 committed by GitHub
commit b7639d2ba6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1955 additions and 538 deletions

View file

@ -42,6 +42,7 @@
<p>{% trans "Hostname" %}</p>
<p>{% trans "Hypervisors" %}</p>
<p>{% trans "Emulator" %}</p>
<p>{% trans "Version" %}</p>
<p>{% trans "Memory" %}</p>
<p>{% trans "Architecture" %}</p>
<p>{% trans "Logical CPUs" %}</p>
@ -55,10 +56,17 @@
<span class="glyphicon glyphicon-chevron-right"></span>
<span class="label label-default">{{ arch }}</span>
{% for h in hpv %}
<span class="label label-primary">{{ h }}</span>{% endfor %}
<span class="label label-primary">{{ h }}</span>
{% endfor %}
{% endfor %}
</p>
<p>{{ emulator }}</p>
<p>
<span class="label label-default">{% trans 'Qemu' %} </span>
<span class="label label-primary">{{ version }}</span> &nbsp;
<span class="label label-default">{% trans 'Libvirt' %} </span>
<span class="label label-primary">{{ lib_version }}</span> &nbsp;
</p>
<p>{{ host_memory|filesizeformat }}</p>
<p>{{ host_arch }}</p>
<p>{{ logical_cpu }}</p>

View file

@ -1,10 +1,10 @@
from django.conf.urls import url
from storages.views import storages, storage
from storages.views import storages, storage, get_volumes
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 computes.views import overview, compute_graph, computes, get_compute_disk_buses
from instances.views import instances
from nwfilters.views import nwfilter, nwfilters
@ -14,6 +14,7 @@ urlpatterns = [
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\-\.\/]+)/volumes$', get_volumes, name='volumes'),
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'),
@ -23,4 +24,5 @@ urlpatterns = [
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'),
url(r'^(?P<compute_id>[0-9]+)/disk/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
]

View file

@ -9,7 +9,7 @@ from instances.models import Instance
from accounts.models import UserInstance
from computes.forms import ComputeAddTcpForm, ComputeAddSshForm, ComputeEditHostForm, ComputeAddTlsForm, ComputeAddSocketForm
from vrtManager.hostdetails import wvmHostDetails
from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager
from vrtManager.connection import CONN_SSH, CONN_TCP, CONN_TLS, CONN_SOCKET, connection_manager, wvmConnect
from libvirt import libvirtError
@ -157,6 +157,8 @@ def overview(request, compute_id):
hypervisor = conn.hypervisor_type()
mem_usage = conn.get_memory_usage()
emulator = conn.get_emulator(host_arch)
version = conn.get_version()
lib_version = conn.get_lib_version()
conn.close()
except libvirtError as lib_err:
error_messages.append(lib_err)
@ -225,3 +227,32 @@ def compute_graph(request, compute_id):
response.cookies['mem'] = datasets['mem']
response.write(data)
return response
@login_required
def get_compute_disk_buses(request, compute_id, disk):
data = {}
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
disk_device_types = conn.get_disk_device_types()
disk_bus_types = conn.get_disk_bus_types()
if disk in disk_device_types:
if disk == 'disk':
data['bus'] = sorted(disk_device_types)
elif disk == 'cdrom':
data['bus'] = ['ide', 'sata', 'scsi',]
elif disk == 'floppy':
data['bus'] = ['fdc',]
elif disk == 'lun':
data['bus'] = ['scsi',]
except libvirtError:
pass
return HttpResponse(json.dumps(data))

View file

@ -37,7 +37,7 @@ class NewVMForm(forms.Form):
host_model = forms.BooleanField(required=False)
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')})
networks = forms.CharField(error_messages={'required': _('No Network pool has been choosen')})
nwfilter = forms.CharField(required=False)
storage = forms.CharField(max_length=20, required=False)
template = forms.CharField(required=False)
@ -46,6 +46,7 @@ class NewVMForm(forms.Form):
hdd_size = forms.IntegerField(required=False)
meta_prealloc = forms.BooleanField(required=False)
virtio = forms.BooleanField(required=False)
qemu_ga = forms.BooleanField(required=False)
mac = forms.CharField(required=False)
console_pass = forms.CharField(required=False,empty_value="", widget=forms.PasswordInput())
video = forms.CharField(error_messages={'required': _('Please select a graphic display')})

View file

@ -16,6 +16,22 @@
{% include 'errors_block.html' %}
{% include 'pleasewaitdialog.html' %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<div class="row" id="max-width-page">
<div class="col-lg-12">
<div role="tabpanel">
@ -187,6 +203,12 @@
</div>
<label class="col-lg-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Guest Agent" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="qemu_ga" value="true" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6">
@ -232,58 +254,70 @@
<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 class="col-sm-7">
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="20" 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 class="col-sm-7">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="2" 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">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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">
<div id="disk_list_div" class="form-group" hidden>
<label id="added_disks" class="col-sm-3 control-label">{% trans "Added Disks" %}</label>
<div class="col-sm-7">
<ul id="img-list" class="form-row">
<!-- populated from javascript -->
</ul>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<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>
<div class="col-sm-3">
<select id="storage-control" name="storage-control" class="form-control" onchange="get_cust_vols({{ compute_id }}, value);">
{% if storages %}
<option value disabled selected>{% trans "Select pool..." %}</option>
{% for storage in storages %}
<option value="{{ storage }}" >{{ storage }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
<div class="col-sm-4">
<select id="image-control" class="form-control" name="image-control" multiple="multiple">
<!-- populated from javascript -->
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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 %}>
@ -294,7 +328,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<ul id="net-list">
<!-- populated from javascript -->
</ul>
@ -308,7 +342,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<select name="nwfilter" class="form-control">
<option value="">{% trans "None" %}</option>
{% for nwfilter in nwfilters %}
@ -319,7 +353,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
@ -333,13 +367,13 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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>
@ -348,13 +382,19 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<label class="col-sm-3 control-label">{% trans "Guest Agent" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="qemu_ga" value="true" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-7">
<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">
<button type="submit" class="btn btn-primary" name="create" formnovalidate onclick="showPleaseWaitDialog()" value="1">
{% trans "Create" %}
</button>
{% else %}
@ -372,54 +412,62 @@
<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 class="col-sm-7">
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="20" 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 class="col-sm-7">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="2" 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">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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>
<input id="images" name="images" type="hidden" value=""/>
<div class="col-sm-3">
<select id="temp-storage-control" name="temp-storage-control" class="form-control" onchange="get_template_vols({{ compute_id }}, value);">
{% if storages %}
<option value disabled selected>{% trans "Select pool" %}...</option>
{% for storage in storages %}
<option value="{{ storage }}" >{{ storage }}</option>
{% endfor %}
{% else %}
<option value="">{% trans "None" %}</option>
{% endif %}
</select>
</div>
<div class="col-sm-4">
<select id="template" class="form-control" name="template" disabled>
<!-- populated from javascript -->
</select>
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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 %}>
@ -430,7 +478,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<select name="networks" class="form-control">
{% for network in networks %}
<option value="{{ network }}">{{ network }}</option>
@ -440,7 +488,7 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "NWFilter" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<select name="nwfilter" class="form-control">
<option value="">{% trans "None" %}</option>
{% for nwfilter in nwfilters %}
@ -449,15 +497,9 @@
</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">
<div class="col-sm-7">
<select name="video" class="form-control">
{% if not videos %}
<option value="vga">vga</option>
@ -471,13 +513,13 @@
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Console Password" %}</label>
<div class="col-sm-6">
<div class="col-sm-7">
<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">
<div class="col-sm-7">
<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>
@ -485,9 +527,21 @@
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Guest Agent" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="qemu_ga" value="true" checked>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-7">
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" value="1" onclick="showPleaseWaitDialog()">
<button type="submit" class="btn btn-primary" name="create" value="1" formnovalidate onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
{% else %}
@ -539,9 +593,17 @@
}
$(document).ready(function () {
$('#image-control').multiselect({
disableIfEmpty: true,
enableCaseInsensitiveFiltering: true,
maxHeight: 400,
inheritClass: true,
buttonWidth:function (options, select) {
return '100%';
},
buttonText: function (options, select) {
return 'Add image <b class="caret"></b>';
return 'Add image...';
},
buttonTitle: function (options, select) {
return '';
@ -549,28 +611,51 @@
onChange: function (element, checked) {
var input_value = toggleValue($('#images').val(), element.val(), checked);
$('#images').val(input_value);
var selected_list_html = '';
var counter = 0;
if (input_value != '') {
$('#disk_list_div').show();
$.each(input_value.split(','), function (index, value) {
var li = '<li>hdd' + counter +
var li = '<li>hdd' + counter + ' - ' +
'<select name="device' + counter + '" class="image-format" onchange="get_disk_bus_choices({{ compute_id }},' + counter + ', value);">' +
'{% for dev in disk_devices %}' +
'<option value=' + '"{{ dev }}">' + '{% trans dev %}</option>' +
'{% endfor %}' +
'</select>' +
'<select id="bus' + counter + '" name="bus' + counter + '" class="image-format">' +
'{% for bus in disk_buses %}' +
'<option value=' + '"{{ bus }}"' +
'{% if bus == default_bus %}selected{% endif %}>' +
'{% trans bus %}</option>' +
'{% endfor %}' +
'</select>' +
' -> ' + value + ' ' +
'<a class="btn-link" onclick="javascript:$(\'#image-control\').multiselect(\'deselect\', \'' + value + '\')">x</a></li>';
'<a class="btn-link pull-right" onclick="javascript:$(\'#image-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a>' +
'</li>';
selected_list_html += li;
counter++;
});
}else {
$('#disk_list_div').hide();
}
$('#img-list').html(selected_list_html);
}
});
$('#network-control').multiselect({
inheritClass: true,
buttonText: function (options, select) {
return 'Add network <b class="caret"></b>';
return 'Add network';
},
buttonTitle: function (options, select) {
return '';
},
buttonWidth:function (options, select) {
return '100%';
},
onChange: function (element, checked) {
var input_value = toggleValue($('#networks').val(), element.val(), checked);
$('#networks').val(input_value);
@ -580,7 +665,7 @@
$.each(input_value.split(','), function (index, value) {
var li = '<li>eth' + counter +
' -> ' + value + ' ' +
'<a class="btn-link" onclick="javascript:$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\')">x</a></li>';
'<a class="btn-link pull-right" onclick="javascript:$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
selected_list_html += li;
counter++;
});
@ -589,7 +674,43 @@
}
});
});
function get_cust_vols(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) {
$("#image-control").find('option').remove();
$.each(data['vols'], function(i, item) {
$("#image-control").append('<option value=' + item +'>' + item + '</option>');
});
$('#image-control').multiselect('rebuild');
});
}
function get_template_vols(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) {
$("#template").find('option').remove();
$.each(data['vols'], function(i, item) {
$("#template").append('<option value=' + item +'>' + item + '</option>');
});
});
$("#template").removeAttr("disabled");
}
function get_disk_bus_choices(compute_id, dev_idx, disk_type){
get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses";
$.getJSON(get_diskBus_url, function (data) {
$("#bus" + dev_idx).find('option').remove();
$.each(data['bus'], function(i, item) {
$("#bus" + dev_idx).append('<option value=' + item +'>' + item + '</option>');
});
});
}
</script>
<script src="{% static "js/ace.js" %}"></script>
<script>
var editor = ace.edit("editor");

View file

@ -12,7 +12,10 @@ 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 webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_BUS
from django.contrib import messages
from logs.views import addlogmsg
@login_required
def create_instance(request, compute_id):
@ -47,7 +50,9 @@ def create_instance(request, compute_id):
default_cache = INSTANCE_VOLUME_DEFAULT_CACHE
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
mac_auto = util.randomMAC()
get_images = sorted(conn.get_storages_images())
disk_devices = conn.get_disk_device_types()
disk_buses = conn.get_disk_bus_types()
default_bus = INSTANCE_VOLUME_DEFAULT_BUS
except libvirtError as lib_err:
error_messages.append(lib_err)
@ -91,7 +96,9 @@ def create_instance(request, compute_id):
except libvirtError as lib_err:
error_messages.append(lib_err.message)
if 'create' in request.POST:
volumes = {}
volume_list = []
clone_path = ""
form = NewVMForm(request.POST)
if form.is_valid():
data = form.cleaned_data
@ -112,7 +119,13 @@ def create_instance(request, compute_id):
try:
path = conn.create_volume(data['storage'], data['name'], data['hdd_size'],
metadata=meta_prealloc)
volumes[path] = conn.get_volume_type(path)
volume = dict()
volume['path'] = path
volume['type'] = conn.get_volume_type(path)
volume['device'] = 'disk'
volume['bus'] = 'virtio'
volume_list.append(volume)
#volumes[path] = conn.get_volume_type(path)
except libvirtError as lib_err:
error_messages.append(lib_err.message)
elif data['template']:
@ -123,16 +136,31 @@ def create_instance(request, compute_id):
error_messages.append(error_msg)
else:
clone_path = conn.clone_from_template(data['name'], templ_path, metadata=meta_prealloc)
volumes[clone_path] = conn.get_volume_type(clone_path)
volume = dict()
volume['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path)
volume['device'] = 'disk'
volume['bus'] = 'virtio'
volume_list.append(volume)
#volumes[clone_path] = conn.get_volume_type(clone_path)
else:
if not data['images']:
error_msg = _("First you need to create or select an image")
error_messages.append(error_msg)
else:
for vol in data['images'].split(','):
for idx, vol in enumerate(data['images'].split(',')):
try:
path = conn.get_volume_path(vol)
volumes[path] = conn.get_volume_type(path)
volume = dict()
volume['path'] = path
volume['type'] = conn.get_volume_type(path)
volume['device'] = request.POST.get('device' + str(idx), '')
volume['bus'] = request.POST.get('bus' + str(idx), '')
volume_list.append(volume)
#volumes[path] = conn.get_volume_type(path)
except libvirtError as lib_err:
error_messages.append(lib_err.message)
if data['cache_mode'] not in conn.get_cache_modes():
@ -142,17 +170,19 @@ def create_instance(request, compute_id):
uuid = util.randomUUID()
try:
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'],
uuid, volume_list, data['cache_mode'], data['networks'], data['virtio'],
data["listener_addr"], data["nwfilter"], data["video"], data["console_pass"],
data['mac'])
data['mac'], data['qemu_ga'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
create_instance.save()
messages.success(request, _("Instance is created."))
msg = _("Instance is created.")
messages.success(request, msg)
addlogmsg(request.user.username, create_instance.name, msg)
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
except libvirtError as lib_err:
if data['hdd_size'] or volumes[clone_path]:
conn.delete_volume(volumes.keys()[0])
if data['hdd_size'] or volume_list.count() > 0:
for vol in volume_list:
conn.delete_volume(vol['path'])
error_messages.append(lib_err)
conn.close()
return render(request, 'create_instance.html', locals())

View file

@ -25,7 +25,7 @@
<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>
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
<div class="col-sm-4">
<select name="storage" class="form-control image-format">
{% for storage in storages %}
@ -35,13 +35,13 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Name" %}</label>
<label class="col-sm-3 control-label">{% 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>
<label class="col-sm-3 control-label">{% trans "Format" %}</label>
<div class="col-sm-4">
<select name="format" class="form-control image-format">
{% for format in formats %}
@ -51,14 +51,14 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Size" %}</label>
<label class="col-sm-3 control-label">{% trans "Size" %}</label>
<div class="col-sm-4">
<input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+">
<input type="text" class="form-control" name="size" value="10" maxlength="5" 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>
<label class="col-sm-3 control-label">{% trans "Bus" %}</label>
<div class="col-sm-4">
<select name="bus" class="form-control image-format">
{% for bus in busses %}
@ -68,7 +68,7 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
<label class="col-sm-3 control-label">{% trans "Cache" %}</label>
<div class="col-sm-4">
<select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %}
@ -78,14 +78,14 @@
</div>
</div>
<div class="form-group meta-prealloc">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Metadata" %}</label>
<label class="col-sm-3 control-label">{% 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>
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}
@ -97,7 +97,7 @@
<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>
<label class="col-sm-3 control-label">{% 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...' %}
@ -112,7 +112,7 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Volume" %}</label>
<label class="col-sm-3 control-label" >{% trans "Volume" %}</label>
<div class="col-sm-4">
<select id="vols" name="vols" class="form-control" disabled>
<option value="" selected>{% trans 'None' %}</option>
@ -121,7 +121,7 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label>
<label class="col-sm-3 control-label">{% trans "Bus" %}</label>
<div class="col-sm-4">
<select name="bus" class="form-control image-format">
{% for bus in busses %}
@ -131,7 +131,7 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label>
<label class="col-sm-3 control-label">{% trans "Cache" %}</label>
<div class="col-sm-4">
<select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %}
@ -143,7 +143,7 @@
</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>
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}

View file

@ -2,13 +2,13 @@
<table class="table table-hover table-striped sortable-theme-bootstrap">
<thead>
<tr>
<th>#</th>
<th><a href="#" id="hide_all_instances" onclick="hide_all_host_instances()">#</a> </th>
<th>{% trans "Name" %}<br>{% trans "Description" %}</th></th>
<th>{% trans "User"%}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "VCPU" %}</th>
<th>{% trans "Memory" %}</th>
<th data-sortable="false" style="width:205px;">{% trans "Actions & Mem Usage" %}</th>
<th style="width:205px;">{% trans "Actions & Mem Usage" %}</th>
</tr>
</thead>
<tbody class="searchable">
@ -17,7 +17,10 @@
<td>
<span id="collapse_host_instances_{{ host.1 }}" class="glyphicon glyphicon-chevron-up" onclick="hide_host_instances('{{ host.1 }}');"></span>
</td>
<td><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a></td>
<td>
<a href="{% url 'overview' host.0 %}">{{ host.1 }}</a>
<span id="inst_count_badge_{{ host.1 }}" class="badge hidden">{{ inst.items|length }}</span>
</td>
<td></td>
<td>
{% ifequal host.2 1 %}<span class="label label-success">{% trans "Active" %}</span>{% endifequal %}
@ -124,12 +127,19 @@
</table>
{% block script %}
<script>
function hide_all_host_instances() {
var rows = $('table tr');
all_host_rows = rows.filter('[host]');
all_host_rows.toggle();
$('span[id^=collapse_host_instances_]').toggleClass("glyphicon-chevron-down").toggleClass("glyphicon-chevron-up");
$('span[id^=inst_count_badge_]').toggleClass("hidden");
}
function hide_host_instances(host) {
var rows = $('table tr');
host_rows = rows.filter("[host='"+host+"']");
host_rows.toggle();
$("span[id='collapse_host_instances_"+host+"']").toggleClass("glyphicon-chevron-down").toggleClass("glyphicon-chevron-up");
$("span[id='inst_count_badge_"+host+"']").toggleClass("hidden");
}
</script>
{% endblock %}

View file

@ -7,7 +7,7 @@
<th>Status</th>
<th>VCPU</th>
<th>Memory</th>
<th data-sortable="false" style="width:205px;">Actions</th>
<th data-sortable="false" style="width:205px;">{% trans "Actions" %}</th>
</tr>
</thead>
<tbody class="searchable">

View file

@ -72,7 +72,7 @@
{% trans "Resize" %}
</a>
</li>
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
{% if allow_admin_or_not_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>
@ -416,7 +416,7 @@
{% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="resize">{% trans "Resize" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Resize" %}</button>
<button class="btn btn-lg btn-success pull-right disabled" name="resize">{% trans "Resize" %}</button>
{% endifequal %}
</form>
{% else %}
@ -594,10 +594,30 @@
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="media">
{% if request.user.is_superuser and status == 5 %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<div class="col-sm-12">
<button type="submit" name="add_cdrom" type="button" class="btn btn-success pull-right">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</button>
</div>
</div>
</form>
{% endif %}
{% for cd in media %}
<form class="form-horizontal" action="" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-2 control-label">{% trans "CDROM" %} {{ forloop.counter }}</label>
<a class="col-sm-2 control-label"
name="details"
title="{% trans "Details" %}"
tabindex="0"
data-trigger="focus"
data-toggle="popover"
data-html="true"
data-content="<strong>Bus:</strong> {{ cd.bus }} <br/> <strong>Dev:</strong> {{ cd.dev }}">
{% trans "CDROM" %} {{ forloop.counter }}
</a>
{% if not cd.image %}
<div class="col-sm-6">
<select name="media" class="form-control">
@ -611,19 +631,22 @@
</select>
</div>
<div class="col-sm-2">
{% if media_iso and request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
{% if media_iso and allow_admin_or_not_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>
{% if status == 5 %}
<button type="submit" class="btn btn-sm btn-danger pull-left" name="detach_cdrom" value="{{ cd.dev }}" style="margin-top: 2px;"><i class="glyphicon glyphicon-remove-circle"></i></button>
{% endif %}
{% else %}
<button class="btn btn-sm btn-success pull-left disabled" name="mount_iso" style="margin-top: 2px;">{% trans "Mount" %}</button>
<button class="btn btn-sm btn-success pull-left disabled" style="margin-top: 2px;">{% trans "Mount" %}</button>
{% endif %}
</div>
{% else %}
<div class="col-sm-5">
<p>{{ cd.image }}</p>
<div class="col-sm-6">
<input class="form-control" value="{{ cd.image }}" disabled/>
</div>
<div class="col-sm-2">
<input type="hidden" name="path" value="{{ cd.path }}">
{% if request.user.is_superuser or request.user.is_staff or not userinstance.is_template %}
{% if allow_admin_or_not_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>
@ -677,18 +700,20 @@
<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 }}">
<input type="hidden" name="storage" value="{{ disk.storage }}">
<input type="hidden" name="name" value="{{ disk.image }}">
{% 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?" %}')">
<button type="submit" class="btn btn-sm btn-default" name="detach_vol" value="{{ disk.dev }}" 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?" %}')">
<button type="submit" class="btn btn-sm btn-default" name="delete_vol" 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?" %}')">
<button class="btn btn-sm btn-default disabled" name="detach_vol" value="{{ disk.dev }}" 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?" %}')">
<button class="btn btn-sm btn-default disabled" name="delete_vol" title="{% trans "Delete" %}" onclick="return confirm('{% trans "Are you sure to delete after shutdown?" %}')">
<i class="fa fa-trash"></i>
</button>
{% endifequal %}
@ -1243,13 +1268,20 @@
{% 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");
function get_volumes(compute_id, pool) {
while (select.options.length){
select.removeChild(select.options[0]);
}
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) {
$("#vols").find('option').remove();
$("#vols").removeAttr("disabled");
$.each(data['vols'], function(i, item) {
$("#vols").append('<option value=' + item +'>' + item + '</option>');
});
});
var sto_drop = document.getElementById('select_storage');
sto_drop.value = pool;
@ -1259,22 +1291,6 @@
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>
@ -1420,6 +1436,13 @@
});
{% endif %}
</script>
<script>
$(document).ready(function(){
$('[data-toggle="popover"]').popover({
placement : 'top'
});
});
</script>
<script>
$(function () {
$('.js-custom__checkbox').change(function () {
@ -1640,12 +1663,5 @@
$("#logs_table > tbody").html(logs);
});
}
</script>
<script type="text/javascript">
$(document).ready(function(){
$('[data-toggle="popover"]').popover({
placement : 'top'
});
});
</script>
{% endblock %}

View file

@ -197,12 +197,18 @@ def instance(request, compute_id, vname):
def get_new_disk_dev(disks, bus):
if bus == "virtio":
dev_base = "vd"
elif bus == "ide":
dev_base = "hd"
elif bus == "fdc":
dev_base = "fd"
else:
dev_base = "sd"
existing_devs = [disk['dev'] for disk in disks]
existing_disk_devs = [disk['dev'] for disk in disks]
# cd-rom bus could be virtio/sata, because of that we should check it also
existing_media_devs = [disk['dev'] for disk in media]
for l in string.lowercase:
dev = dev_base + l
if dev not in existing_devs:
if dev not in existing_disk_devs and dev not in existing_media_devs:
return dev
raise Exception(_('None available device name'))
@ -265,7 +271,7 @@ def instance(request, compute_id, vname):
networks = conn.get_net_device()
vcpu_range = conn.get_max_cpus()
memory_range = [256, 512, 768, 1024, 2048, 4096, 6144, 8192, 16384]
memory_range = [256, 512, 768, 1024, 2048, 3072, 4096, 6144, 8192, 16384]
if memory not in memory_range:
insort(memory_range, memory)
if cur_memory not in memory_range:
@ -290,7 +296,7 @@ def instance(request, compute_id, vname):
default_owner = settings.INSTANCE_VOLUME_DEFAULT_OWNER
formats = conn.get_image_formats()
busses = conn.get_busses()
busses = conn.get_disk_bus_types()
default_bus = settings.INSTANCE_VOLUME_DEFAULT_BUS
show_access_root_password = settings.SHOW_ACCESS_ROOT_PASSWORD
show_access_ssh_keys = settings.SHOW_ACCESS_SSH_KEYS
@ -310,6 +316,7 @@ def instance(request, compute_id, vname):
addlogmsg(request.user.username, instance.name, msg)
userinstances = UserInstance.objects.filter(instance=instance).order_by('user__username')
allow_admin_or_not_template = request.user.is_superuser or request.user.is_staff or not instance.is_template
if request.method == 'POST':
if 'poweron' in request.POST:
@ -444,7 +451,7 @@ def instance(request, compute_id, vname):
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#resize')
if 'addnewvol' in request.POST and (request.user.is_superuser or userinstance.is_change):
if 'add_new_vol' in request.POST and allow_admin_or_not_template:
connCreate = wvmCreate(compute.hostname,
compute.login,
compute.password,
@ -460,11 +467,11 @@ def instance(request, compute_id, vname):
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')
msg = _('Attach new disk: ' + target)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#disks')
if 'addexistingvol' in request.POST and (request.user.is_superuser or userinstance.is_change):
if 'add_existing_vol' in request.POST and allow_admin_or_not_template:
storage = request.POST.get('selected_storage', '')
name = request.POST.get('vols', '')
bus = request.POST.get('bus', default_bus)
@ -482,68 +489,83 @@ def instance(request, compute_id, vname):
source = path + "/" + name;
conn.attach_disk(source, target, subdriver=format, cache=cache, targetbus=bus)
msg = _('Attach Existing disk')
msg = _('Attach Existing disk: ' + target)
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,
if 'delete_vol' in request.POST and allow_admin_or_not_template:
storage = request.POST.get('storage', '')
connDelete = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type)
compute.type,
storage)
dev = request.POST.get('dev', '')
path = request.POST.get('path', '')
name = request.POST.get('name', '')
conn.detach_disk(dev, path)
connDelete.delete_volume(path)
conn.detach_disk(dev)
connDelete.del_volume(name)
msg = _('Delete disk')
msg = _('Delete disk: ' + dev)
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', '')
if 'detach_vol' in request.POST and allow_admin_or_not_template:
dev = request.POST.get('detach_vol', '')
path = request.POST.get('path', '')
conn.detach_disk(dev, path)
msg = _('Detach disk')
conn.detach_disk(dev)
msg = _('Detach disk: ' + dev)
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):
if 'add_cdrom' in request.POST and allow_admin_or_not_template:
bus = request.POST.get('bus', 'ide')
target = get_new_disk_dev(media, bus)
conn.attach_disk("", target, device='cdrom', cache='none', targetbus=bus)
msg = _('Add CD-Rom: ' + target)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media')
if 'detach_cdrom' in request.POST and allow_admin_or_not_template:
dev = request.POST.get('detach_cdrom', '')
path = request.POST.get('path', '')
conn.detach_disk(dev)
msg = _('Detach CD-Rom: ' + dev)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media')
if 'umount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('path', '')
dev = request.POST.get('umount_iso', '')
conn.umount_iso(dev, image)
msg = _("Mount media")
msg = _("Mount media: " + dev)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media')
if 'mount_iso' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
if 'mount_iso' in request.POST and allow_admin_or_not_template:
image = request.POST.get('media', '')
dev = request.POST.get('mount_iso', '')
conn.mount_iso(dev, image)
msg = _("Umount media")
msg = _("Umount media: " + dev)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#media')
if 'snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
if 'snapshot' in request.POST and allow_admin_or_not_template:
name = request.POST.get('name', '')
conn.create_snapshot(name)
msg = _("New snapshot")
msg = _("New snapshot :" + name)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'delete_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
if 'delete_snapshot' in request.POST and allow_admin_or_not_template:
snap_name = request.POST.get('name', '')
conn.snapshot_delete(snap_name)
msg = _("Delete snapshot")
msg = _("Delete snapshot :" + snap_name)
addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'revert_snapshot' in request.POST and (request.user.is_superuser or request.user.is_staff or not userinstance.is_template):
if 'revert_snapshot' in request.POST and allow_admin_or_not_template:
snap_name = request.POST.get('name', '')
conn.snapshot_revert(snap_name)
msg = _("Successful revert snapshot: ")

View file

@ -1,38 +1 @@
.multiselect-container {
position: absolute;
list-style-type: none;
margin: 0;
padding: 0;
}
.multiselect-container .input-group {
margin: 5px;
}
.multiselect-container > li {
padding: 0;
}
.multiselect-container > li > a.multiselect-all label {
font-weight: bold;
}
.multiselect-container > li > label.multiselect-group {
margin: 0;
padding: 3px 20px 3px 20px;
height: 100%;
}
.multiselect-container > li > a > label {
margin: 0;
height: 100%;
cursor: pointer;
font-weight: normal;
}
.multiselect-container > li > a > label.radio,
.multiselect-container > li > a > label.checkbox {
margin: 0;
}
.multiselect-container > li > a > label > input[type="checkbox"] {
margin-bottom: 5px;
}
.btn-group > .btn-group:nth-child(2) > .multiselect.btn {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
span.multiselect-native-select{position:relative}span.multiselect-native-select select{border:0!important;clip:rect(0 0 0 0)!important;height:1px!important;margin:-1px -1px -1px -3px!important;overflow:hidden!important;padding:0!important;position:absolute!important;width:1px!important;left:50%;top:30px}.multiselect-container{position:absolute;list-style-type:none;margin:0;padding:0}.multiselect-container .input-group{margin:5px}.multiselect-container .multiselect-reset .input-group{width:93%}.multiselect-container>li{padding:0}.multiselect-container>li>a.multiselect-all label{font-weight:700}.multiselect-container>li.multiselect-group label{margin:0;padding:3px 20px;height:100%;font-weight:700}.multiselect-container>li.multiselect-group-clickable label{cursor:pointer}.multiselect-container>li>a{padding:0}.multiselect-container>li>a>label{margin:0;height:100%;cursor:pointer;font-weight:400;padding:3px 20px 3px 40px}.multiselect-container>li>a>label.checkbox,.multiselect-container>li>a>label.radio{margin:0}.multiselect-container>li>a>label>input[type=checkbox]{margin-bottom:5px}.btn-group>.btn-group:nth-child(2)>.multiselect.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.form-inline .multiselect-container label.checkbox,.form-inline .multiselect-container label.radio{padding:3px 20px 3px 40px}.form-inline .multiselect-container li a label.checkbox input[type=checkbox],.form-inline .multiselect-container li a label.radio input[type=radio]{margin-left:-20px;margin-right:0}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -205,12 +205,23 @@ def storage(request, compute_id, pool):
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())
@login_required
def get_volumes(request, compute_id, pool):
data = {}
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
except libvirtError as liberr:
pass
data['vols'] = sorted(conn.get_volumes())
return HttpResponse(json.dumps(data))

View file

@ -361,6 +361,24 @@ class wvmConnect(object):
virttype = self.hypervisor_type()[arch][0]
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
def get_version(self):
ver = self.wvm.getVersion()
major = ver / 1000000
ver = ver % 1000000
minor = ver / 1000
ver = ver % 1000
release = ver
return "%s.%s.%s" % (major,minor,release)
def get_lib_version(self):
ver = self.wvm.getLibVersion()
major = ver / 1000000
ver %= 1000000
minor = ver / 1000
ver %= 1000
release = ver
return "%s.%s.%s" % (major,minor,release)
def is_kvm_supported(self):
"""Return KVM capabilities."""
return util.is_kvm_available(self.get_cap_xml())
@ -436,8 +454,8 @@ class wvmConnect(object):
""" Return machine type of emulation"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/machine".format(arch))
def get_busses(self):
"""Get available busses"""
def get_disk_bus_types(self):
"""Get available disk bus types list"""
def get_bus_list(ctx):
result = []
@ -449,6 +467,18 @@ class wvmConnect(object):
# return [ 'ide', 'scsi', 'usb', 'virtio' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_bus_list)
def get_disk_device_types(self):
"""Get available disk device type list"""
def get_device_list(ctx):
result = []
for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'):
if disk_enum.xpath("@name")[0] == "diskDevice":
for values in disk_enum: result.append(values.text)
return result
# return [ 'disk', 'cdrom', 'floppy', 'lun' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_device_list)
def get_image_formats(self):
"""Get available image formats"""

View file

@ -93,7 +93,7 @@ class wvmCreate(wvmConnect):
def get_volume_type(self, path):
vol = self.get_volume_by_path(path)
vol_type = util.get_xml_path(vol.XMLDesc(0), "/volume/target/format/@type")
if vol_type == 'unknown':
if vol_type == 'unknown' or vol_type == 'iso':
return 'raw'
if vol_type:
return vol_type
@ -154,7 +154,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(path)
vol.delete()
def create_instance(self, name, memory, vcpu, host_model, uuid, images, cache_mode, networks, virtio, listen_addr, nwfilter=None, video="cirrus", console_pass="random", mac=None):
def create_instance(self, name, memory, vcpu, host_model, uuid, images, cache_mode, networks, virtio, listen_addr, nwfilter=None, video="cirrus", console_pass="random", mac=None, qemu_ga=False):
"""
Create VM function
"""
@ -189,11 +189,18 @@ class wvmCreate(wvmConnect):
<on_crash>restart</on_crash>
<devices>"""
disk_letters = list(string.lowercase)
for image, img_type in images.items():
stg = self.get_storage_by_vol_path(image)
vd_disk_letters = list(string.lowercase)
fd_disk_letters = list(string.lowercase)
hd_disk_letters = list(string.lowercase)
sd_disk_letters = list(string.lowercase)
add_cd = True
#for image, img_type in images.items():
for volume in images:
stg = self.get_storage_by_vol_path(volume['path'])
stg_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
if volume['device'] == 'cdrom': add_cd = False
if stg_type == 'rbd':
ceph_user, secret_uuid, ceph_hosts = get_rbd_storage_data(stg)
xml += """<disk type='network' device='disk'>
@ -201,7 +208,7 @@ class wvmCreate(wvmConnect):
<auth username='%s'>
<secret type='ceph' uuid='%s'/>
</auth>
<source protocol='rbd' name='%s'>""" % (img_type, cache_mode, ceph_user, secret_uuid, image)
<source protocol='rbd' name='%s'>""" % (volume['type'], cache_mode, ceph_user, secret_uuid, volume['path'])
if isinstance(ceph_hosts, list):
for host in ceph_hosts:
if host.get('port'):
@ -213,23 +220,26 @@ class wvmCreate(wvmConnect):
xml += """
</source>"""
else:
xml += """<disk type='file' device='disk'>
xml += """<disk type='file' device='%s'>
<driver name='qemu' type='%s' cache='%s'/>
<source file='%s'/>""" % (img_type, cache_mode, image)
<source file='%s'/>""" % (volume['device'], volume['type'], cache_mode, volume['path'])
if virtio:
xml += """<target dev='vd%s' bus='virtio'/>""" % (disk_letters.pop(0),)
if volume['bus'] == 'virtio':
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), volume['bus'])
elif volume['bus'] == 'ide':
xml += """<target dev='hd%s' bus='%s'/>""" % (hd_disk_letters.pop(0), volume['bus'])
elif volume['bus'] == 'fdc':
xml += """<target dev='fd%s' bus='%s'/>""" % (fd_disk_letters.pop(0), volume['bus'])
else:
xml += """<target dev='sd%s' bus='ide'/>""" % (disk_letters.pop(0),)
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), volume['bus'])
xml += """</disk>"""
if add_cd:
xml += """ <disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file=''/>
<target dev='hda' bus='ide'/>
<target dev='hd%s' bus='ide'/>
<readonly/>
<address type='drive' controller='0' bus='1' target='0' unit='1'/>
</disk>"""
</disk>""" % (hd_disk_letters.pop(0),)
for net in networks.split(','):
xml += """<interface type='network'>"""
if mac:
@ -250,11 +260,21 @@ class wvmCreate(wvmConnect):
xml += """ <input type='mouse' bus='ps2'/>
<input type='tablet' bus='usb'/>
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
<console type='pty'/>
<video>
<console type='pty'/> """ % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr)
if qemu_ga:
xml += """ <channel type='unix'>
<target type='virtio' name='org.qemu.guest_agent.0'/>
</channel>"""
xml += """ <video>
<model type='%s'/>
</video>
<memballoon model='virtio'/>
</devices>
</domain>""" % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr, video)
</domain>""" % video
self._defineXML(xml)

View file

@ -1,7 +1,8 @@
import time
import os.path
try:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE, VIR_MIGRATE_UNSAFE
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE, VIR_MIGRATE_UNSAFE, VIR_DOMAIN_RUNNING, \
VIR_DOMAIN_AFFECT_LIVE, VIR_DOMAIN_AFFECT_CONFIG
except:
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_MIGRATE_LIVE
from vrtManager import util
@ -252,6 +253,7 @@ class wvmInstance(wvmConnect):
storage = None
src_fl = None
disk_format = None
used_size = None
disk_size = None
for disk in doc.xpath('/domain/devices/disk'):
@ -297,6 +299,7 @@ class wvmInstance(wvmConnect):
if device == 'cdrom':
try:
dev = media.xpath('target/@dev')[0]
bus = media.xpath('target/@bus')[0]
try:
src_fl = media.xpath('source/@file')[0]
vol = self.get_volume_by_path(src_fl)
@ -309,7 +312,7 @@ class wvmInstance(wvmConnect):
except:
pass
finally:
result.append({'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl})
result.append({'dev': dev, 'image': volume, 'storage': storage, 'path': src_fl, 'bus': bus})
return result
return util.get_xml_path(self._XMLDesc(0), func=disks)
@ -363,7 +366,7 @@ class wvmInstance(wvmConnect):
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
def attach_disk(self, source, target, sourcetype='file', type='disk', driver='qemu', subdriver='raw', cache='none', targetbus='ide'):
def attach_disk(self, source, target, sourcetype='file', device='disk', driver='qemu', subdriver='raw', cache='none', targetbus='ide'):
tree = ElementTree.fromstring(self._XMLDesc(0))
xml_disk = """
<disk type='%s' device='%s'>
@ -371,7 +374,7 @@ class wvmInstance(wvmConnect):
<source file='%s'/>
<target dev='%s' bus='%s'/>
</disk>
""" % (sourcetype, type, driver, subdriver, cache, source, target, targetbus)
""" % (sourcetype, device, driver, subdriver, cache, source, target, targetbus)
if self.get_status() == 5:
devices = tree.find('devices')
elm_disk = ElementTree.fromstring(xml_disk)
@ -379,19 +382,18 @@ class wvmInstance(wvmConnect):
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
def detach_disk(self, dev, image):
def detach_disk(self, dev):
tree = ElementTree.fromstring(self._XMLDesc(0))
for disk in tree.findall("./devices/disk[@device='disk']"):
source = disk.find("source")
for disk in tree.findall("./devices/disk"):
target = disk.find("target")
if source.get("file") == image and target.get("dev") == dev:
if 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)
ret = self.instance.detachDevice(xml_disk)
xmldom = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
if self.get_status() == 5:
xmldom = ElementTree.tostring(tree)
@ -613,8 +615,14 @@ class wvmInstance(wvmConnect):
"""
Function change ram and cpu on vds.
"""
memory = int(memory) * 1024
cur_memory = int(cur_memory) * 1024
# if dom is running change only ram
if self.get_status() == VIR_DOMAIN_RUNNING:
self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_LIVE)
self.set_memory(cur_memory, VIR_DOMAIN_AFFECT_CONFIG)
return
xml = self._XMLDesc(VIR_DOMAIN_XML_SECURE)
tree = ElementTree.fromstring(xml)
@ -905,3 +913,6 @@ class wvmInstance(wvmConnect):
new_xml = ElementTree.tostring(tree)
self._defineXML(new_xml)
def set_memory(self, size, flags=0):
self.instance.setMemoryFlags(size, flags)