1
0
Fork 0
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:
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>
<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>

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()
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)

View file

@ -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

View file

@ -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 %}

View file

@ -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()

View file

@ -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

View file

@ -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') {

View file

@ -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>&emsp; <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 %}

View file

@ -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', '')

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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")