1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2025-01-26 07:05:19 +00:00

Merge pull request #183 from catborise/master

create instance from template enhanced and others
This commit is contained in:
Anatoliy Guskov 2018-09-23 09:33:28 +03:00 committed by GitHub
commit f474905719
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 262 additions and 46 deletions

1
_config.yml Normal file
View file

@ -0,0 +1 @@
theme: jekyll-theme-cayman

View file

@ -34,7 +34,8 @@
<h3 class="page-header">{% trans "Basic details" %}</h3> <h3 class="page-header">{% trans "Basic details" %}</h3>
<div class="col-xs-4 col-sm-3"> <div class="col-xs-4 col-sm-3">
<p>{% trans "Hostname" %}</p> <p>{% trans "Hostname" %}</p>
<p>{% trans "Hypervisor" %}</p> <p>{% trans "Hypervisors" %}</p>
<p>{% trans "Emulator" %}</p>
<p>{% trans "Memory" %}</p> <p>{% trans "Memory" %}</p>
<p>{% trans "Architecture" %}</p> <p>{% trans "Architecture" %}</p>
<p>{% trans "Logical CPUs" %}</p> <p>{% trans "Logical CPUs" %}</p>
@ -44,7 +45,14 @@
</div> </div>
<div class="col-xs-8 col-sm-7"> <div class="col-xs-8 col-sm-7">
<p>{{ hostname }}</p> <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_memory|filesizeformat }}</p>
<p>{{ host_arch }}</p> <p>{{ host_arch }}</p>
<p>{{ logical_cpu }}</p> <p>{{ logical_cpu }}</p>

View file

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

View file

@ -2,6 +2,7 @@ import re
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from create.models import Flavor from create.models import Flavor
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
class FlavorAddForm(forms.Form): class FlavorAddForm(forms.Form):
@ -45,6 +46,9 @@ class NewVMForm(forms.Form):
meta_prealloc = forms.BooleanField(required=False) meta_prealloc = forms.BooleanField(required=False)
virtio = forms.BooleanField(required=False) virtio = forms.BooleanField(required=False)
mac = forms.CharField(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): def clean_name(self):
name = self.cleaned_data['name'] name = self.cleaned_data['name']
@ -54,3 +58,4 @@ class NewVMForm(forms.Form):
elif len(name) > 20: elif len(name) > 20:
raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters')) raise forms.ValidationError(_('The name of the virtual machine must not exceed 20 characters'))
return name return name

View file

@ -14,8 +14,8 @@
</div> </div>
</div> </div>
<!-- /.row --> <!-- /.row -->
{% include 'errors_block.html' %} {% include 'errors_block.html' %}
{% include 'pleasewaitdialog.html' %}
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
@ -108,6 +108,36 @@
</select> </select>
</div> </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>
</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"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "VirtIO" %}</label> <label class="col-sm-3 control-label">{% trans "VirtIO" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
@ -115,7 +145,7 @@
</div> </div>
</div> </div>
{% if storages %} {% 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" %} {% trans "Create" %}
</button> </button>
{% else %} {% else %}
@ -202,8 +232,39 @@
<input type="checkbox" name="virtio" value="true" checked> <input type="checkbox" name="virtio" value="true" checked>
</div> </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>
</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 %} {% 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" %} {% trans "Create" %}
</button> </button>
{% else %} {% else %}
@ -218,9 +279,10 @@
<div class="well"> <div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %} <form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight"> <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> </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" %} {% trans "Create" %}
</button> </button>
</form> </form>
@ -297,6 +359,16 @@
</select> </select>
</div> </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>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "Network" %}</label> <label class="col-sm-3 control-label">{% trans "Network" %}</label>
<div class="col-sm-6"> <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:]+"> <input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
</div> </div>
</div> </div>
<div class="form-group">
<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"> <div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label> <label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6"> <div class="col-sm-6">
@ -435,5 +537,10 @@
<script> <script>
var editor = ace.edit("editor"); var editor = ace.edit("editor");
editor.getSession().setMode("ace/mode/xml"); editor.getSession().setMode("ace/mode/xml");
var input = $('input[name="dom_xml"]');
editor.getSession().on("change",function () {
input.val(editor.getSession().getValue());
})
</script> </script>
{% endblock %} {% endblock %}

View file

@ -10,7 +10,8 @@ from instances.models import Instance
from vrtManager.create import wvmCreate from vrtManager.create import wvmCreate
from vrtManager import util from vrtManager import util
from libvirt import libvirtError from libvirt import libvirtError
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from django.contrib import messages
@login_required @login_required
def create_instance(request, compute_id): def create_instance(request, compute_id):
@ -27,7 +28,7 @@ def create_instance(request, compute_id):
storages = [] storages = []
networks = [] networks = []
meta_prealloc = False meta_prealloc = False
computes = Compute.objects.all() #computes = Compute.objects.all()
compute = get_object_or_404(Compute, pk=compute_id) compute = get_object_or_404(Compute, pk=compute_id)
flavors = Flavor.objects.filter().order_by('id') flavors = Flavor.objects.filter().order_by('id')
@ -40,7 +41,9 @@ def create_instance(request, compute_id):
storages = sorted(conn.get_storages(only_actives=True)) storages = sorted(conn.get_storages(only_actives=True))
networks = sorted(conn.get_networks()) networks = sorted(conn.get_networks())
instances = conn.get_instances() instances = conn.get_instances()
videos = conn.get_video()
cache_modes = sorted(conn.get_cache_modes().items()) cache_modes = sorted(conn.get_cache_modes().items())
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
mac_auto = util.randomMAC() mac_auto = util.randomMAC()
get_images = sorted(conn.get_storages_images()) get_images = sorted(conn.get_storages_images())
except libvirtError as lib_err: except libvirtError as lib_err:
@ -71,10 +74,10 @@ def create_instance(request, compute_id):
delete_flavor.delete() delete_flavor.delete()
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
if 'create_xml' in request.POST: if 'create_xml' in request.POST:
xml = request.POST.get('from_xml', '') xml = request.POST.get('dom_xml', '')
try: try:
name = util.get_xml_path(xml, '/domain/name') name = util.get_xml_path(xml, '/domain/name')
except util.etree.ParserError: except util.etree.Error as err:
name = None name = None
if name in instances: if name in instances:
error_msg = _("A virtual machine with this name already exists") error_msg = _("A virtual machine with this name already exists")
@ -110,6 +113,11 @@ def create_instance(request, compute_id):
error_messages.append(lib_err.message) error_messages.append(lib_err.message)
elif data['template']: elif data['template']:
templ_path = conn.get_volume_path(data['template']) templ_path = conn.get_volume_path(data['template'])
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) clone_path = conn.clone_from_template(data['name'], templ_path, metadata=meta_prealloc)
volumes[clone_path] = conn.get_volume_type(clone_path) volumes[clone_path] = conn.get_volume_type(clone_path)
else: else:
@ -131,12 +139,14 @@ def create_instance(request, compute_id):
try: try:
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'], conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
uuid, volumes, data['cache_mode'], data['networks'], data['virtio'], uuid, volumes, data['cache_mode'], data['networks'], data['virtio'],
data["listener_addr"], None, data["video"], data["console_pass"],
data['mac']) data['mac'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid) create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
create_instance.save() create_instance.save()
messages.success(request,"Instance is created.")
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']])) return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
except libvirtError as lib_err: except libvirtError as lib_err:
if data['hdd_size']: if data['hdd_size'] or volumes[clone_path]:
conn.delete_volume(volumes.keys()[0]) conn.delete_volume(volumes.keys()[0])
error_messages.append(lib_err) error_messages.append(lib_err)
conn.close() conn.close()

View file

@ -10,10 +10,12 @@ import socket
OS_VERSIONS = ['latest', ''] OS_VERSIONS = ['latest', '']
OS_UUID = "iid-dswebvirtcloud" OS_UUID = "iid-dswebvirtcloud"
def os_index(request): def os_index(request):
response = '\n'.join(OS_VERSIONS) response = '\n'.join(OS_VERSIONS)
return HttpResponse(response) return HttpResponse(response)
def os_metadata_json(request, version): def os_metadata_json(request, version):
""" """
:param request: :param request:
@ -27,9 +29,10 @@ def os_metadata_json(request, version):
response = { 'uuid': OS_UUID, 'hostname': hostname } response = { 'uuid': OS_UUID, 'hostname': hostname }
return HttpResponse(json.dumps(response)) return HttpResponse(json.dumps(response))
else: else:
err = 'Invalid version: %s' % version err = 'Invalid version: {}'.format(version)
raise Http404(err) raise Http404(err)
def os_userdata(request, version): def os_userdata(request, version):
""" """
:param request: :param request:
@ -51,9 +54,10 @@ def os_userdata(request, version):
return render(request, 'user_data', locals()) return render(request, 'user_data', locals())
else: else:
err = 'Invalid version: %s' % version err = 'Invalid version: {}'.format(version)
raise Http404(err) raise Http404(err)
def get_client_ip(request): def get_client_ip(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR') x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for: if x_forwarded_for:
@ -62,10 +66,15 @@ def get_client_ip(request):
ip = request.META.get('REMOTE_ADDR') ip = request.META.get('REMOTE_ADDR')
return ip return ip
def get_hostname_by_ip(ip): def get_hostname_by_ip(ip):
try:
addrs = socket.gethostbyaddr(ip) addrs = socket.gethostbyaddr(ip)
except Exception:
addrs = [ip,]
return addrs[0] return addrs[0]
def get_vdi_url(request, vname): def get_vdi_url(request, vname):
instance = Instance.objects.get(name=vname) instance = Instance.objects.get(name=vname)
compute = instance.compute compute = instance.compute

View file

@ -321,7 +321,12 @@
{% ifequal status 1 %} {% ifequal status 1 %}
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="vdiconsole"> <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> <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 class="clearfix"></div>
</div> </div>
{% endifequal %} {% endifequal %}
@ -511,8 +516,8 @@
</a> </a>
</li> </li>
<li role="presentation"> <li role="presentation">
<a href="#restoresnapshot" aria-controls="restoresnapshot" role="tab" data-toggle="tab"> <a href="#managesnapshot" aria-controls="managesnapshot" role="tab" data-toggle="tab">
{% trans "Restore From Snapshot" %} {% trans "Manage Snapshots" %}
</a> </a>
</li> </li>
</ul> </ul>
@ -538,10 +543,10 @@
<p>{% trans "To take a snapshot please Power Off the instance." %}</p> <p>{% trans "To take a snapshot please Power Off the instance." %}</p>
{% endifequal %} {% endifequal %}
</div> </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 %} {% ifequal status 5 %}
{% if snapshots %} {% if snapshots %}
<p>{% trans "Choose a snapshot for restore" %}</p> <p>{% trans "Choose a snapshot for restore/delete" %}</p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table"> <table class="table">
<thead> <thead>
@ -560,7 +565,7 @@
<form action="" method="post" style="height:10px" role="form">{% csrf_token %} <form action="" method="post" style="height:10px" role="form">{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}"> <input type="hidden" name="name" value="{{ snap.name }}">
{% ifequal status 5 %} {% 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> <span class="glyphicon glyphicon-save"></span>
</button> </button>
{% else %} {% else %}
@ -573,7 +578,7 @@
<td style="width:30px;"> <td style="width:30px;">
<form action="" method="post" role="form">{% csrf_token %} <form action="" method="post" role="form">{% csrf_token %}
<input type="hidden" name="name" value="{{ snap.name }}"> <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> <span class="glyphicon glyphicon-trash"></span>
</button> </button>
</form> </form>
@ -1335,6 +1340,7 @@
$(document).ready(function () { $(document).ready(function () {
// set vdi url // set vdi url
$.get("/datasource/vdi/{{ vname }}/", function(data) { $.get("/datasource/vdi/{{ vname }}/", function(data) {
$("#vdi_url_input").attr("value", data);
$("#vdi_url").attr("href", 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'); var btnsect = $('#navbtn>li>a');
$(btnsect).each(function () { $(btnsect).each(function () {
if ($(this).attr('href') === '#snapshots') { if ($(this).attr('href') === '#snapshots') {

View file

@ -14,7 +14,9 @@
<tbody class="searchable"> <tbody class="searchable">
{% for host, inst in all_host_vms.items %} {% 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;"> <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><a href="{% url 'overview' host.0 %}">{{ host.1 }}</a></td>
<td></td> <td></td>
<td> <td>
@ -32,7 +34,7 @@
</tr> </tr>
{% for vm, info in inst.items %} {% for vm, info in inst.items %}
<tr> <tr host="{{ host.1 }}">
<td style="text-align: right">{{ forloop.counter }} </td> <td style="text-align: right">{{ forloop.counter }} </td>
<td>&emsp; <a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br> <td>&emsp; <a href="{% url 'instance' host.0 vm %}">{{ vm }}</a><br>
<small><em>{{ info.title }}</em></small> <small><em>{{ info.title }}</em></small>
@ -120,3 +122,14 @@
{% endfor %} {% endfor %}
</tbody> </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 %}

View file

@ -451,7 +451,7 @@ def instance(request, compute_id, vname):
else: else:
error_messages.append(msg) error_messages.append(msg)
else: 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) error_messages.append(msg)
if 'addpublickey' in request.POST: if 'addpublickey' in request.POST:
@ -473,7 +473,7 @@ def instance(request, compute_id, vname):
else: else:
error_messages.append(msg) error_messages.append(msg)
else: 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) 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):
@ -550,14 +550,14 @@ def instance(request, compute_id, vname):
conn.create_snapshot(name) conn.create_snapshot(name)
msg = _("New snapshot") msg = _("New snapshot")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#restoresnapshot') return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'delete_snapshot' in request.POST: if 'delete_snapshot' in request.POST:
snap_name = request.POST.get('name', '') snap_name = request.POST.get('name', '')
conn.snapshot_delete(snap_name) conn.snapshot_delete(snap_name)
msg = _("Delete snapshot") msg = _("Delete snapshot")
addlogmsg(request.user.username, instance.name, msg) addlogmsg(request.user.username, instance.name, msg)
return HttpResponseRedirect(request.get_full_path() + '#restoresnapshot') return HttpResponseRedirect(request.get_full_path() + '#managesnapshot')
if 'revert_snapshot' in request.POST: if 'revert_snapshot' in request.POST:
snap_name = request.POST.get('name', '') snap_name = request.POST.get('name', '')

View file

@ -1,2 +1,2 @@
/*! sortable.js 0.5.0 */ /*! sortable.js 0.8.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); (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);

View file

@ -352,6 +352,14 @@ class wvmConnect(object):
"""Return xml capabilities""" """Return xml capabilities"""
return self.wvm.getCapabilities() 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): def is_kvm_supported(self):
"""Return KVM capabilities.""" """Return KVM capabilities."""
return util.is_kvm_available(self.get_cap_xml()) return util.is_kvm_available(self.get_cap_xml())
@ -392,9 +400,38 @@ class wvmConnect(object):
'unsafe': 'Unsafe', # since libvirt 0.9.7 '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): def get_busses(self):
"""Get available busses""" """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): def get_image_formats(self):
"""Get available image formats""" """Get available image formats"""
@ -404,6 +441,17 @@ class wvmConnect(object):
"""Get available image filename extensions""" """Get available image filename extensions"""
return [ 'img', 'qcow', 'qcow2' ] 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): def get_iface(self, name):
return self.wvm.interfaceLookupByName(name) return self.wvm.interfaceLookupByName(name)

View file

@ -2,6 +2,7 @@ import string
from vrtManager import util from vrtManager import util
from vrtManager.connection import wvmConnect from vrtManager.connection import wvmConnect
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
@ -148,7 +149,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(path) vol = self.get_volume_by_path(path)
vol.delete() 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 Create VM function
""" """
@ -228,20 +229,27 @@ class wvmCreate(wvmConnect):
xml += """<interface type='network'>""" xml += """<interface type='network'>"""
if mac: if mac:
xml += """<mac address='%s'/>""" % mac xml += """<mac address='%s'/>""" % mac
xml += """<source network='%s'/> xml += """<source network='%s'/>""" % net
<filterref filter='clean-traffic'/>""" % net if nwfilter:
xml += """<filterref filter='%s'/>""" % nwfilter
if virtio: if virtio:
xml += """<model type='virtio'/>""" xml += """<model type='virtio'/>"""
xml += """</interface>""" 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'/> xml += """ <input type='mouse' bus='ps2'/>
<input type='tablet' bus='usb'/> <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'/> <console type='pty'/>
<video> <video>
<model type='cirrus'/> <model type='%s'/>
</video> </video>
<memballoon model='virtio'/> <memballoon model='virtio'/>
</devices> </devices>
</domain>""" % (QEMU_CONSOLE_DEFAULT_TYPE, util.randomPasswd()) </domain>""" % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr, video)
self._defineXML(xml) self._defineXML(xml)

View file

@ -67,6 +67,6 @@ class wvmHostDetails(wvmConnect):
info.append(self.wvm.getURI()) #uri info.append(self.wvm.getURI()) #uri
return info return info
def hypervisor_type(self):
"""Return hypervisor type"""
return get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch/domain/@type")