mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 23:25:24 +00:00
Merge pull request #183 from catborise/master
create instance from template enhanced and others
This commit is contained in:
commit
f474905719
14 changed files with 262 additions and 46 deletions
1
_config.yml
Normal file
1
_config.yml
Normal file
|
@ -0,0 +1 @@
|
|||
theme: jekyll-theme-cayman
|
|
@ -34,7 +34,8 @@
|
|||
<h3 class="page-header">{% trans "Basic details" %}</h3>
|
||||
<div class="col-xs-4 col-sm-3">
|
||||
<p>{% trans "Hostname" %}</p>
|
||||
<p>{% trans "Hypervisor" %}</p>
|
||||
<p>{% trans "Hypervisors" %}</p>
|
||||
<p>{% trans "Emulator" %}</p>
|
||||
<p>{% trans "Memory" %}</p>
|
||||
<p>{% trans "Architecture" %}</p>
|
||||
<p>{% trans "Logical CPUs" %}</p>
|
||||
|
@ -44,7 +45,14 @@
|
|||
</div>
|
||||
<div class="col-xs-8 col-sm-7">
|
||||
<p>{{ hostname }}</p>
|
||||
<p>{{ hypervisor }}</p>
|
||||
<p>{% for arch, hpv in hypervisor.items %}
|
||||
<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 %}
|
||||
{% endfor %}
|
||||
</p>
|
||||
<p>{{ emulator }}</p>
|
||||
<p>{{ host_memory|filesizeformat }}</p>
|
||||
<p>{{ host_arch }}</p>
|
||||
<p>{{ logical_cpu }}</p>
|
||||
|
|
|
@ -156,6 +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()
|
||||
conn.close()
|
||||
except libvirtError as lib_err:
|
||||
error_messages.append(lib_err)
|
||||
|
|
|
@ -2,6 +2,7 @@ import re
|
|||
from django import forms
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from create.models import Flavor
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
|
||||
|
||||
class FlavorAddForm(forms.Form):
|
||||
|
@ -45,6 +46,9 @@ class NewVMForm(forms.Form):
|
|||
meta_prealloc = forms.BooleanField(required=False)
|
||||
virtio = 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')})
|
||||
listener_addr = forms.ChoiceField(required=True, widget=forms.RadioSelect, choices=QEMU_CONSOLE_LISTEN_ADDRESSES)
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
|
@ -54,3 +58,4 @@ class NewVMForm(forms.Form):
|
|||
elif len(name) > 20:
|
||||
raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters'))
|
||||
return name
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
{% include 'errors_block.html' %}
|
||||
{% include 'pleasewaitdialog.html' %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
@ -108,6 +108,36 @@
|
|||
</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">
|
||||
|
@ -115,7 +145,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% if storages %}
|
||||
<button type="submit" class="btn btn-primary" name="create" value="1">
|
||||
<button type="submit" class="btn btn-primary" name="create" onclick="showPleaseWaitDialog()" value="1">
|
||||
{% trans "Create" %}
|
||||
</button>
|
||||
{% else %}
|
||||
|
@ -202,8 +232,39 @@
|
|||
<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">
|
||||
<button type="submit" class="btn btn-primary" name="create" value="1" onclick="showPleaseWaitDialog()">
|
||||
{% trans "Create" %}
|
||||
</button>
|
||||
{% else %}
|
||||
|
@ -218,9 +279,10 @@
|
|||
<div class="well">
|
||||
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
|
||||
<div class="col-sm-12" id="xmlheight">
|
||||
<textarea id="editor" name="from_xml"></textarea>
|
||||
<input type="hidden" name="dom_xml"/>
|
||||
<textarea id="editor"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" name="create_xml">
|
||||
<button type="submit" class="btn btn-primary" name="create_xml" onclick="showPleaseWaitDialog()">
|
||||
{% trans "Create" %}
|
||||
</button>
|
||||
</form>
|
||||
|
@ -297,6 +359,16 @@
|
|||
</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 }}">{% 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">
|
||||
|
@ -313,6 +385,36 @@
|
|||
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
|
||||
</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 "Host-Model" %}</label>
|
||||
<div class="col-sm-6">
|
||||
|
@ -435,5 +537,10 @@
|
|||
<script>
|
||||
var editor = ace.edit("editor");
|
||||
editor.getSession().setMode("ace/mode/xml");
|
||||
|
||||
var input = $('input[name="dom_xml"]');
|
||||
editor.getSession().on("change",function () {
|
||||
input.val(editor.getSession().getValue());
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -10,7 +10,8 @@ from instances.models import Instance
|
|||
from vrtManager.create import wvmCreate
|
||||
from vrtManager import util
|
||||
from libvirt import libvirtError
|
||||
|
||||
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
from django.contrib import messages
|
||||
|
||||
@login_required
|
||||
def create_instance(request, compute_id):
|
||||
|
@ -27,7 +28,7 @@ def create_instance(request, compute_id):
|
|||
storages = []
|
||||
networks = []
|
||||
meta_prealloc = False
|
||||
computes = Compute.objects.all()
|
||||
#computes = Compute.objects.all()
|
||||
compute = get_object_or_404(Compute, pk=compute_id)
|
||||
flavors = Flavor.objects.filter().order_by('id')
|
||||
|
||||
|
@ -40,7 +41,9 @@ def create_instance(request, compute_id):
|
|||
storages = sorted(conn.get_storages(only_actives=True))
|
||||
networks = sorted(conn.get_networks())
|
||||
instances = conn.get_instances()
|
||||
videos = conn.get_video()
|
||||
cache_modes = sorted(conn.get_cache_modes().items())
|
||||
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
|
||||
mac_auto = util.randomMAC()
|
||||
get_images = sorted(conn.get_storages_images())
|
||||
except libvirtError as lib_err:
|
||||
|
@ -71,10 +74,10 @@ def create_instance(request, compute_id):
|
|||
delete_flavor.delete()
|
||||
return HttpResponseRedirect(request.get_full_path())
|
||||
if 'create_xml' in request.POST:
|
||||
xml = request.POST.get('from_xml', '')
|
||||
xml = request.POST.get('dom_xml', '')
|
||||
try:
|
||||
name = util.get_xml_path(xml, '/domain/name')
|
||||
except util.etree.ParserError:
|
||||
except util.etree.Error as err:
|
||||
name = None
|
||||
if name in instances:
|
||||
error_msg = _("A virtual machine with this name already exists")
|
||||
|
@ -110,8 +113,13 @@ def create_instance(request, compute_id):
|
|||
error_messages.append(lib_err.message)
|
||||
elif data['template']:
|
||||
templ_path = conn.get_volume_path(data['template'])
|
||||
clone_path = conn.clone_from_template(data['name'], templ_path, metadata=meta_prealloc)
|
||||
volumes[clone_path] = conn.get_volume_type(clone_path)
|
||||
dest_vol = conn.get_volume_path(data["name"] + ".img")
|
||||
if dest_vol:
|
||||
error_msg = _("Image has already exist. Please check volumes or change instance name")
|
||||
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)
|
||||
else:
|
||||
if not data['images']:
|
||||
error_msg = _("First you need to create or select an image")
|
||||
|
@ -131,12 +139,14 @@ 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['mac'])
|
||||
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
|
||||
create_instance.save()
|
||||
messages.success(request,"Instance is created.")
|
||||
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
|
||||
except libvirtError as lib_err:
|
||||
if data['hdd_size']:
|
||||
if data['hdd_size'] or volumes[clone_path]:
|
||||
conn.delete_volume(volumes.keys()[0])
|
||||
error_messages.append(lib_err)
|
||||
conn.close()
|
||||
|
|
|
@ -7,13 +7,15 @@ from libvirt import libvirtError
|
|||
import json
|
||||
import socket
|
||||
|
||||
OS_VERSIONS = [ 'latest', '' ]
|
||||
OS_VERSIONS = ['latest', '']
|
||||
OS_UUID = "iid-dswebvirtcloud"
|
||||
|
||||
|
||||
def os_index(request):
|
||||
response = '\n'.join(OS_VERSIONS)
|
||||
return HttpResponse(response)
|
||||
|
||||
|
||||
def os_metadata_json(request, version):
|
||||
"""
|
||||
:param request:
|
||||
|
@ -27,9 +29,10 @@ def os_metadata_json(request, version):
|
|||
response = { 'uuid': OS_UUID, 'hostname': hostname }
|
||||
return HttpResponse(json.dumps(response))
|
||||
else:
|
||||
err = 'Invalid version: %s' % version
|
||||
err = 'Invalid version: {}'.format(version)
|
||||
raise Http404(err)
|
||||
|
||||
|
||||
def os_userdata(request, version):
|
||||
"""
|
||||
:param request:
|
||||
|
@ -51,9 +54,10 @@ def os_userdata(request, version):
|
|||
|
||||
return render(request, 'user_data', locals())
|
||||
else:
|
||||
err = 'Invalid version: %s' % version
|
||||
err = 'Invalid version: {}'.format(version)
|
||||
raise Http404(err)
|
||||
|
||||
|
||||
def get_client_ip(request):
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if x_forwarded_for:
|
||||
|
@ -62,10 +66,15 @@ def get_client_ip(request):
|
|||
ip = request.META.get('REMOTE_ADDR')
|
||||
return ip
|
||||
|
||||
|
||||
def get_hostname_by_ip(ip):
|
||||
addrs = socket.gethostbyaddr(ip)
|
||||
try:
|
||||
addrs = socket.gethostbyaddr(ip)
|
||||
except Exception:
|
||||
addrs = [ip,]
|
||||
return addrs[0]
|
||||
|
||||
|
||||
def get_vdi_url(request, vname):
|
||||
instance = Instance.objects.get(name=vname)
|
||||
compute = instance.compute
|
||||
|
|
|
@ -321,7 +321,12 @@
|
|||
{% ifequal status 1 %}
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole">
|
||||
<p>{% trans "This action opens a remote viewer with a connection to the console of the instance." %}</p>
|
||||
<a href="#" class="btn btn-lg btn-success pull-right" id="vdi_url" >{% trans "VDI" %}</a>
|
||||
<div class="input-group">
|
||||
<input type="text" class="input-lg disabled form-control" disabled id="vdi_url_input"/>
|
||||
<span class="input-group-btn">
|
||||
<a href="#" class="btn btn-lg btn-success" id="vdi_url" >{% trans "VDI" %}</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</div>
|
||||
{% endifequal %}
|
||||
|
@ -511,8 +516,8 @@
|
|||
</a>
|
||||
</li>
|
||||
<li role="presentation">
|
||||
<a href="#restoresnapshot" aria-controls="restoresnapshot" role="tab" data-toggle="tab">
|
||||
{% trans "Restore From Snapshot" %}
|
||||
<a href="#managesnapshot" aria-controls="managesnapshot" role="tab" data-toggle="tab">
|
||||
{% trans "Manage Snapshots" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -538,10 +543,10 @@
|
|||
<p>{% trans "To take a snapshot please Power Off the instance." %}</p>
|
||||
{% endifequal %}
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="restoresnapshot">
|
||||
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="managesnapshot">
|
||||
{% ifequal status 5 %}
|
||||
{% if snapshots %}
|
||||
<p>{% trans "Choose a snapshot for restore" %}</p>
|
||||
<p>{% trans "Choose a snapshot for restore/delete" %}</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
@ -560,7 +565,7 @@
|
|||
<form action="" method="post" style="height:10px" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="name" value="{{ snap.name }}">
|
||||
{% ifequal status 5 %}
|
||||
<button type="submit" class="btn btn-sm btn-default" name="revert_snapshot" onclick="return confirm('Are you sure?')">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="revert_snapshot" title="Revert to this Snapshot" onclick="return confirm('Are you sure?')">
|
||||
<span class="glyphicon glyphicon-save"></span>
|
||||
</button>
|
||||
{% else %}
|
||||
|
@ -573,7 +578,7 @@
|
|||
<td style="width:30px;">
|
||||
<form action="" method="post" role="form">{% csrf_token %}
|
||||
<input type="hidden" name="name" value="{{ snap.name }}">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="delete_snapshot" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<button type="submit" class="btn btn-sm btn-default" name="delete_snapshot" title="Delete Snapshot" onclick="return confirm('{% trans "Are you sure?" %}')">
|
||||
<span class="glyphicon glyphicon-trash"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
@ -1335,6 +1340,7 @@
|
|||
$(document).ready(function () {
|
||||
// set vdi url
|
||||
$.get("/datasource/vdi/{{ vname }}/", function(data) {
|
||||
$("#vdi_url_input").attr("value", data);
|
||||
$("#vdi_url").attr("href", data);
|
||||
});
|
||||
});
|
||||
|
@ -1550,7 +1556,7 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
if (~$.inArray(hash, ['#takesnapshot', '#restoresnapshot'])) {
|
||||
if (~$.inArray(hash, ['#takesnapshot', '#managesnapshot'])) {
|
||||
var btnsect = $('#navbtn>li>a');
|
||||
$(btnsect).each(function () {
|
||||
if ($(this).attr('href') === '#snapshots') {
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
<tbody class="searchable">
|
||||
{% for host, inst in all_host_vms.items %}
|
||||
<tr class="active" style="font-weight: bold;border-bottom: 2px solid darkgray;border-top: 2px solid darkgray;">
|
||||
<td><span class="fa fa-server"></span> </td>
|
||||
<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></td>
|
||||
<td>
|
||||
|
@ -32,7 +34,7 @@
|
|||
</tr>
|
||||
|
||||
{% for vm, info in inst.items %}
|
||||
<tr>
|
||||
<tr host="{{ host.1 }}">
|
||||
<td style="text-align: right">{{ forloop.counter }} </td>
|
||||
<td>  <a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br>
|
||||
<small><em>{{ info.title }}</em></small>
|
||||
|
@ -119,4 +121,15 @@
|
|||
{% endfor %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
{% block script %}
|
||||
<script>
|
||||
function hide_host_instances(host) {
|
||||
var rows = $('table tr');
|
||||
|
||||
host_rows = rows.filter('[host='+host+']');
|
||||
host_rows.toggle();
|
||||
$('span#collapse_host_instances_'+host).toggleClass("glyphicon-chevron-down").toggleClass("glyphicon-chevron-up");
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -451,7 +451,7 @@ def instance(request, compute_id, vname):
|
|||
else:
|
||||
error_messages.append(msg)
|
||||
else:
|
||||
msg = _("Please shutdow down your instance and then try again")
|
||||
msg = _("Please shutdown down your instance and then try again")
|
||||
error_messages.append(msg)
|
||||
|
||||
if 'addpublickey' in request.POST:
|
||||
|
@ -473,7 +473,7 @@ def instance(request, compute_id, vname):
|
|||
else:
|
||||
error_messages.append(msg)
|
||||
else:
|
||||
msg = _("Please shutdow down your instance and then try again")
|
||||
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):
|
||||
|
@ -550,14 +550,14 @@ def instance(request, compute_id, vname):
|
|||
conn.create_snapshot(name)
|
||||
msg = _("New snapshot")
|
||||
addlogmsg(request.user.username, instance.name, msg)
|
||||
return HttpResponseRedirect(request.get_full_path() + '#restoresnapshot')
|
||||
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
|
||||
|
||||
if 'delete_snapshot' in request.POST:
|
||||
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() + '#restoresnapshot')
|
||||
return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
|
||||
|
||||
if 'revert_snapshot' in request.POST:
|
||||
snap_name = request.POST.get('name', '')
|
||||
|
|
4
static/js/sortable.min.js
vendored
4
static/js/sortable.min.js
vendored
|
@ -1,2 +1,2 @@
|
|||
/*! sortable.js 0.5.0 */
|
||||
(function(){var a,b,c,d,e,f;a="table[data-sortable]",c=/^-?[£$¤]?[\d,.]+%?$/,f=/^\s+|\s+$/g,e="ontouchstart"in document.documentElement,b=e?"touchstart":"click",d={init:function(){var b,c,e,f,g;for(c=document.querySelectorAll(a),g=[],e=0,f=c.length;f>e;e++)b=c[e],g.push(d.initTable(b));return g},initTable:function(a){var b,c,e,f,g;if(1===a.tHead.rows.length&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),e=a.querySelectorAll("th"),b=f=0,g=e.length;g>f;b=++f)c=e[b],"false"!==c.getAttribute("data-sortable")&&d.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,c,e){var f;return f=d.getColumnType(a,e),c.addEventListener(b,function(){var b,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;for(j="true"===this.getAttribute("data-sorted"),k=this.getAttribute("data-sorted-direction"),b=j?"ascending"===k?"descending":"ascending":f.defaultSortDirection,m=this.parentNode.querySelectorAll("th"),n=0,q=m.length;q>n;n++)c=m[n],c.setAttribute("data-sorted","false"),c.removeAttribute("data-sorted-direction");for(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",b),l=a.tBodies[0],h=[],t=l.rows,o=0,r=t.length;r>o;o++)g=t[o],h.push([d.getNodeValue(g.cells[e]),g]);for(j?h.reverse():h.sort(f.compare),u=[],p=0,s=h.length;s>p;p++)i=h[p],u.push(l.appendChild(i[1]));return u})},getColumnType:function(a,b){var e,f,g,h,i;for(i=a.tBodies[0].rows,g=0,h=i.length;h>g;g++)if(e=i[g],f=d.getNodeValue(e.cells[b]),""!==f&&f.match(c))return d.types.numeric;return d.types.alpha},getNodeValue:function(a){return a?null!==a.getAttribute("data-value")?a.getAttribute("data-value"):"undefined"!=typeof a.innerText?a.innerText.replace(f,""):a.textContent.replace(f,""):""},types:{numeric:{defaultSortDirection:"descending",compare:function(a,b){var c,d;return c=parseFloat(a[0].replace(/[^0-9.-]/g,"")),d=parseFloat(b[0].replace(/[^0-9.-]/g,"")),isNaN(c)&&(c=0),isNaN(d)&&(d=0),d-c}},alpha:{defaultSortDirection:"ascending",compare:function(a,b){var c,d;return c=a[0].toLowerCase(),d=b[0].toLowerCase(),c===d?0:d>c?-1:1}}}},setTimeout(d.init,0),window.Sortable=d}).call(this);
|
||||
/*! sortable.js 0.8.0 */
|
||||
(function(){var a,b,c,d,e,f,g;a="table[data-sortable]",d=/^-?[£$¤]?[\d,.]+%?$/,g=/^\s+|\s+$/g,c=["click"],f="ontouchstart"in document.documentElement,f&&c.push("touchstart"),b=function(a,b,c){return null!=a.addEventListener?a.addEventListener(b,c,!1):a.attachEvent("on"+b,c)},e={init:function(b){var c,d,f,g,h;for(null==b&&(b={}),null==b.selector&&(b.selector=a),d=document.querySelectorAll(b.selector),h=[],f=0,g=d.length;g>f;f++)c=d[f],h.push(e.initTable(c));return h},initTable:function(a){var b,c,d,f,g,h;if(1===(null!=(h=a.tHead)?h.rows.length:void 0)&&"true"!==a.getAttribute("data-sortable-initialized")){for(a.setAttribute("data-sortable-initialized","true"),d=a.querySelectorAll("th"),b=f=0,g=d.length;g>f;b=++f)c=d[b],"false"!==c.getAttribute("data-sortable")&&e.setupClickableTH(a,c,b);return a}},setupClickableTH:function(a,d,f){var g,h,i,j,k,l;for(i=e.getColumnType(a,f),h=function(b){var c,g,h,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D;if(b.handled===!0)return!1;for(b.handled=!0,m="true"===this.getAttribute("data-sorted"),n=this.getAttribute("data-sorted-direction"),h=m?"ascending"===n?"descending":"ascending":i.defaultSortDirection,p=this.parentNode.querySelectorAll("th"),s=0,w=p.length;w>s;s++)d=p[s],d.setAttribute("data-sorted","false"),d.removeAttribute("data-sorted-direction");if(this.setAttribute("data-sorted","true"),this.setAttribute("data-sorted-direction",h),o=a.tBodies[0],l=[],m){for(D=o.rows,v=0,z=D.length;z>v;v++)g=D[v],l.push(g);for(l.reverse(),B=0,A=l.length;A>B;B++)k=l[B],o.appendChild(k)}else{for(r=null!=i.compare?i.compare:function(a,b){return b-a},c=function(a,b){return a[0]===b[0]?a[2]-b[2]:i.reverse?r(b[0],a[0]):r(a[0],b[0])},C=o.rows,j=t=0,x=C.length;x>t;j=++t)k=C[j],q=e.getNodeValue(k.cells[f]),null!=i.comparator&&(q=i.comparator(q)),l.push([q,k,j]);for(l.sort(c),u=0,y=l.length;y>u;u++)k=l[u],o.appendChild(k[1])}return"function"==typeof window.CustomEvent&&"function"==typeof a.dispatchEvent?a.dispatchEvent(new CustomEvent("Sortable.sorted",{bubbles:!0})):void 0},l=[],j=0,k=c.length;k>j;j++)g=c[j],l.push(b(d,g,h));return l},getColumnType:function(a,b){var c,d,f,g,h,i,j,k,l,m,n;if(d=null!=(l=a.querySelectorAll("th")[b])?l.getAttribute("data-sortable-type"):void 0,null!=d)return e.typesObject[d];for(m=a.tBodies[0].rows,h=0,j=m.length;j>h;h++)for(c=m[h],f=e.getNodeValue(c.cells[b]),n=e.types,i=0,k=n.length;k>i;i++)if(g=n[i],g.match(f))return g;return e.typesObject.alpha},getNodeValue:function(a){var b;return a?(b=a.getAttribute("data-value"),null!==b?b:"undefined"!=typeof a.innerText?a.innerText.replace(g,""):a.textContent.replace(g,"")):""},setupTypes:function(a){var b,c,d,f;for(e.types=a,e.typesObject={},f=[],c=0,d=a.length;d>c;c++)b=a[c],f.push(e.typesObject[b.name]=b);return f}},e.setupTypes([{name:"numeric",defaultSortDirection:"descending",match:function(a){return a.match(d)},comparator:function(a){return parseFloat(a.replace(/[^0-9.-]/g,""),10)||0}},{name:"date",defaultSortDirection:"ascending",reverse:!0,match:function(a){return!isNaN(Date.parse(a))},comparator:function(a){return Date.parse(a)||0}},{name:"alpha",defaultSortDirection:"ascending",match:function(){return!0},compare:function(a,b){return a.localeCompare(b)}}]),setTimeout(e.init,0),"function"==typeof define&&define.amd?define(function(){return e}):"undefined"!=typeof exports?module.exports=e:window.Sortable=e}).call(this);
|
|
@ -352,6 +352,14 @@ class wvmConnect(object):
|
|||
"""Return xml capabilities"""
|
||||
return self.wvm.getCapabilities()
|
||||
|
||||
def get_dom_cap_xml(self):
|
||||
""" Return domcapabilities xml"""
|
||||
emulatorbin = self.emulator()
|
||||
machine = self.machine()
|
||||
arch = self.wvm.getInfo()[0]
|
||||
virttype = self.hypervisor_type()[arch][0]
|
||||
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
|
||||
|
||||
def is_kvm_supported(self):
|
||||
"""Return KVM capabilities."""
|
||||
return util.is_kvm_available(self.get_cap_xml())
|
||||
|
@ -391,10 +399,39 @@ class wvmConnect(object):
|
|||
'directsync': 'Direct sync', # since libvirt 0.9.5
|
||||
'unsafe': 'Unsafe', # since libvirt 0.9.7
|
||||
}
|
||||
|
||||
|
||||
def hypervisor_type(self):
|
||||
"""Return hypervisor type"""
|
||||
def hypervisors(ctx):
|
||||
result = {}
|
||||
for arch in ctx.xpath('/capabilities/guest/arch'):
|
||||
domain_types = arch.xpath('domain/@type')
|
||||
arch_name = arch.xpath('@name')[0]
|
||||
result[arch_name]= domain_types
|
||||
return result
|
||||
return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
|
||||
|
||||
def emulator(self):
|
||||
"""Return emulator """
|
||||
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/emulator")
|
||||
|
||||
def machine(self):
|
||||
""" Return machine type of emulation"""
|
||||
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/machine")
|
||||
|
||||
def get_busses(self):
|
||||
"""Get available busses"""
|
||||
return [ 'ide', 'scsi', 'usb', 'virtio' ]
|
||||
|
||||
def get_bus_list(ctx):
|
||||
result = []
|
||||
for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'):
|
||||
if disk_enum.xpath("@name")[0] == "bus":
|
||||
for values in disk_enum: result.append(values.text)
|
||||
return result
|
||||
|
||||
# return [ 'ide', 'scsi', 'usb', 'virtio' ]
|
||||
return util.get_xml_path(self.get_dom_cap_xml(), func=get_bus_list)
|
||||
|
||||
|
||||
def get_image_formats(self):
|
||||
"""Get available image formats"""
|
||||
|
@ -404,6 +441,17 @@ class wvmConnect(object):
|
|||
"""Get available image filename extensions"""
|
||||
return [ 'img', 'qcow', 'qcow2' ]
|
||||
|
||||
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'):
|
||||
if video_enum.xpath("@name")[0] == "modelType":
|
||||
for values in video_enum: result.append(values.text)
|
||||
return result
|
||||
return util.get_xml_path(self.get_dom_cap_xml(),func=get_video_list)
|
||||
|
||||
def get_iface(self, name):
|
||||
return self.wvm.interfaceLookupByName(name)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ import string
|
|||
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_FORMAT
|
||||
|
||||
|
||||
|
@ -148,7 +149,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, 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):
|
||||
"""
|
||||
Create VM function
|
||||
"""
|
||||
|
@ -228,20 +229,27 @@ class wvmCreate(wvmConnect):
|
|||
xml += """<interface type='network'>"""
|
||||
if mac:
|
||||
xml += """<mac address='%s'/>""" % mac
|
||||
xml += """<source network='%s'/>
|
||||
<filterref filter='clean-traffic'/>""" % net
|
||||
xml += """<source network='%s'/>""" % net
|
||||
if nwfilter:
|
||||
xml += """<filterref filter='%s'/>""" % nwfilter
|
||||
if virtio:
|
||||
xml += """<model type='virtio'/>"""
|
||||
xml += """</interface>"""
|
||||
|
||||
if console_pass == "random":
|
||||
console_pass = "passwd='" + util.randomPasswd() + "'"
|
||||
else:
|
||||
if not console_pass == "":
|
||||
console_pass = "passwd='" + console_pass + "'"
|
||||
|
||||
xml += """ <input type='mouse' bus='ps2'/>
|
||||
<input type='tablet' bus='usb'/>
|
||||
<graphics type='%s' port='-1' autoport='yes' passwd='%s' listen='127.0.0.1'/>
|
||||
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
|
||||
<console type='pty'/>
|
||||
<video>
|
||||
<model type='cirrus'/>
|
||||
<model type='%s'/>
|
||||
</video>
|
||||
<memballoon model='virtio'/>
|
||||
</devices>
|
||||
</domain>""" % (QEMU_CONSOLE_DEFAULT_TYPE, util.randomPasswd())
|
||||
</domain>""" % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr, video)
|
||||
self._defineXML(xml)
|
||||
|
|
|
@ -67,6 +67,6 @@ class wvmHostDetails(wvmConnect):
|
|||
info.append(self.wvm.getURI()) #uri
|
||||
return info
|
||||
|
||||
def hypervisor_type(self):
|
||||
"""Return hypervisor type"""
|
||||
return get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/domain/@type")
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue