1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 15:15:22 +00:00

Enrich Instance Create operation: -add capability to create arm, ppc, i686, aarch64 instances, -add option to choose firmware, -add options to choose chipset. -add capability to choose volume driver options. -add new default settings

This commit is contained in:
catborise 2019-12-13 16:47:51 +03:00
parent 28b001e7cb
commit dd16a5b2d5
14 changed files with 828 additions and 211 deletions

View file

@ -72,6 +72,10 @@ Setup libvirt and KVM on server
```bash
wget -O - https://clck.ru/9V9fH | sudo sh
```
Done!!
Go to http://serverip and you should see the login screen.
### Install WebVirtCloud panel (CentOS)

View file

@ -73,7 +73,7 @@
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton{{ forloop.counter0 }}" role="menu">
{% for arc in hypervisor.keys|slice:"4:" %}
<li><a href="#">{{ arc }}</a></li>
<li><a tabindex="-1" href="#">{{ arc }}</a></li>
{% endfor %}
</ul>
{% endif %}

View file

@ -2,9 +2,9 @@ from django.conf.urls import url
from storages.views import storages, storage, get_volumes
from networks.views import networks, network
from secrets.views import secrets
from create.views import create_instance
from create.views import create_instance, create_instance_select_type
from interfaces.views import interfaces, interface
from computes.views import overview, compute_graph, computes, get_compute_disk_buses
from computes.views import overview, compute_graph, computes, get_compute_disk_buses, get_compute_machine_types, get_dom_capabilities
from instances.views import instances
from nwfilters.views import nwfilter, nwfilters
@ -23,6 +23,9 @@ urlpatterns = [
url(r'^(?P<compute_id>[0-9]+)/nwfilters/$', nwfilters, name='nwfilters'),
url(r'^(?P<compute_id>[0-9]+)/nwfilter/(?P<nwfltr>[\w\-\.\:]+)/$', nwfilter, name='nwfilter'),
url(r'^(?P<compute_id>[0-9]+)/secrets/$', secrets, name='secrets'),
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance, name='create_instance'),
url(r'^(?P<compute_id>[0-9]+)/disk/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
url(r'^(?P<compute_id>[0-9]+)/create/$', create_instance_select_type, name='create_instance_select_type'),
url(r'^(?P<compute_id>[0-9]+)/create/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)$', create_instance, name='create_instance'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines$', get_compute_machine_types, name='machines'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/disks/(?P<disk>[\w\-\.\/]+)/buses$', get_compute_disk_buses, name='buses'),
url(r'^(?P<compute_id>[0-9]+)/archs/(?P<arch>[\w\-\.\/]+)/machines/(?P<machine>[\w\-\.\/]+)/capabilities$', get_dom_capabilities, name='domcaps'),
]

View file

@ -155,7 +155,7 @@ def overview(request, compute_id):
compute.password,
compute.type)
hostname, host_arch, host_memory, logical_cpu, model_cpu, uri_conn = conn.get_node_info()
hypervisor = conn.hypervisor_type()
hypervisor = conn.get_hypervisors_domain_types()
mem_usage = conn.get_memory_usage()
emulator = conn.get_emulator(host_arch)
version = conn.get_version()
@ -198,8 +198,8 @@ def compute_graph(request, compute_id):
@login_required
def get_compute_disk_buses(request, compute_id, disk):
data = {}
def get_compute_disk_buses(request, compute_id, arch, machine, disk):
data = dict()
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmConnect(compute.hostname,
@ -207,7 +207,7 @@ def get_compute_disk_buses(request, compute_id, disk):
compute.password,
compute.type)
disk_device_types = conn.get_disk_device_types()
disk_device_types = conn.get_disk_device_types(arch, machine)
if disk in disk_device_types:
if disk == 'disk':
@ -223,3 +223,51 @@ def get_compute_disk_buses(request, compute_id, disk):
return HttpResponse(json.dumps(data))
@login_required
def get_compute_machine_types(request, compute_id, arch):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
data['machines'] = conn.get_machine_types(arch)
except libvirtError:
pass
return HttpResponse(json.dumps(data))
@login_required
def get_compute_video_models(request, compute_id, arch, machine):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
data['videos'] = conn.get_video_models(arch, machine)
except libvirtError:
pass
return HttpResponse(json.dumps(data))
@login_required
def get_dom_capabilities(request, compute_id, arch, machine):
data = dict()
try:
compute = get_object_or_404(Compute, pk=compute_id)
conn = wvmConnect(compute.hostname,
compute.login,
compute.password,
compute.type)
data['videos'] = conn.get_disk_device_types(arch, machine)
data['bus'] = conn.get_disk_device_types(arch, machine)
except libvirtError:
pass
return HttpResponse(json.dumps(data))

View file

@ -33,8 +33,9 @@ class FlavorAddForm(forms.Form):
class NewVMForm(forms.Form):
name = forms.CharField(error_messages={'required': _('No Virtual Machine name has been entered')},
max_length=64)
firmware = forms.CharField(max_length=50, required=False)
vcpu = forms.IntegerField(error_messages={'required': _('No VCPU has been entered')})
host_model = forms.BooleanField(required=False)
vcpu_mode = forms.CharField(max_length=20, required=False)
disk = forms.IntegerField(required=False)
memory = forms.IntegerField(error_messages={'required': _('No RAM size has been entered')})
networks = forms.CharField(error_messages={'required': _('No Network pool has been choosen')})
@ -48,8 +49,9 @@ class NewVMForm(forms.Form):
virtio = forms.BooleanField(required=False)
qemu_ga = forms.BooleanField(required=False)
mac = forms.CharField(required=False)
console_pass = forms.CharField(required=False,empty_value="", widget=forms.PasswordInput())
video = forms.CharField(error_messages={'required': _('Please select a graphic display')})
console_pass = forms.CharField(required=False, empty_value="", widget=forms.PasswordInput())
graphics = forms.CharField(error_messages={'required': _('Please select a graphics type')})
video = forms.CharField(error_messages={'required': _('Please select a video driver')})
listener_addr = forms.ChoiceField(required=True, widget=forms.RadioSelect, choices=QEMU_CONSOLE_LISTEN_ADDRESSES)
def clean_name(self):

View file

@ -0,0 +1,151 @@
{% extends "base.html" %}
{% load i18n %}
{% load staticfiles %}
{% block title %}{% trans "Create new instance - Select Type" %}{% endblock %}
{% block content %}
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1>
</div>
</div>
<!-- /.row -->
{% include 'errors_block.html' %}
{% include 'pleasewaitdialog.html' %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<div class="row" id="max-width-page">
<div class="col-lg-12">
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active">
<a href="#select_architecture" aria-controls="flavor" role="tab" data-toggle="tab">
{% trans "Architecture" %}
</a>
</li>
<li role="presentation">
<a href="#addFromXML" aria-controls="addFromXML" role="tab" data-toggle="tab">
{% trans "XML" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
<div role="tabpanel" class="tab-pane tab-pane-bordered active" id="select_architecture">
<div class="well">
<div class="center-block">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Architecture" %}</label>
<div class="col-sm-6">
<select class="form-control" id="select_archs" name="archs" onchange="get_machine_types({{ compute_id }}, value);">
{% for hpv in hypervisors %}
<option value="{{ hpv }}" {% if hpv == default_arch %}selected{% endif %}>{{ hpv }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Chipset" %}</label>
<div class="col-sm-6">
<select class="form-control" id="select_chipset" name="chipset">
<!-- fill with script -->
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-6 col-lg-offset-3">
<button class="btn btn-block btn-primary" type="button" name="create_instance" onclick="goto_create()">
{% trans "Next >" %}
</button>
</div>
</div>
</form>
</div>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromXML">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<input type="hidden" name="dom_xml"/>
<textarea id="editor"></textarea>
</div>
<button type="submit" class="btn btn-primary" name="create_xml" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
</form>
</div>
<div class="clearfix"/>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script>
$(document).ready(function () {
let arch = $("#select_archs").val();
get_machine_types({{ compute_id }}, arch);
});
function get_machine_types(compute_id, arch) {
get_machine_type_url = "/computes/" + compute_id + "/archs/" + arch + "/machines";
$.getJSON(get_machine_type_url, function (data) {
$("#select_chipset").find('option').remove();
$("#select_archs").val(arch);
$.each(data['machines'], function(i, item) {
if (item == '{{ default_machine }}') {
var selected = 'selected';
}else{
var selected = '';
}
$("#select_chipset").append('<option value="' + item + '"' + selected +'>' + item + '</option>');
});
});
}
</script>
<script src="{% static "js/ace.js" %}"></script>
<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>
{% if request.user.is_superuser %}
<script>
function goto_create() {
let compute = '{{ compute.id }}';
let arch = $("#select_archs").val();
let machine = $("#select_chipset").val();
create_machine_url = "/computes/" + compute + "/create/archs/" + arch + "/machines/" + machine;
{#url = "{% url 'create_instance' compute.id 'x86_64' 'pc' %}".replace(/x86_64/, arch).replace(/pc/, machine);#}
window.location.href = create_machine_url;
}
</script>
{% endif %}
{% endblock %}

View file

@ -9,7 +9,9 @@
<!-- Page Heading -->
<div class="row">
<div class="col-lg-12">
<h1 class="page-header">{% trans "New instance on" %} {{ compute.name }}</h1>
<h1 class="page-header">
{% trans "New instance on" %} {{ compute.name }}
</h1>
</div>
</div>
<!-- /.row -->
@ -37,6 +39,11 @@
<div role="tabpanel">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li role="presentation">
<a class="pull-right" href="#" role="tab" data-toggle="tab" onclick="goto_compute()">
<span class="glyphicon glyphicon-arrow-left"></span>
</a>
</li>
<li role="presentation" class="active">
<a href="#flavor" aria-controls="flavor" role="tab" data-toggle="tab">
{% trans "Flavor" %}
@ -52,11 +59,6 @@
{% trans "Template" %}
</a>
</li>
<li role="presentation">
<a href="#addFromXML" aria-controls="addFromXML" role="tab" data-toggle="tab">
{% trans "XML" %}
</a>
</li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
@ -112,9 +114,43 @@
<input type="hidden" name="hdd_size" value="{{ flavor.disk }}">
</div>
</div>
{% if firmwares %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Firmware" %}</label>
<div class="col-sm-6">
<select class="form-control" id="select_firmware" name="firmware">
{% for frm in firmwares %}
<option value="{{ frm }}" {% if frm == default_firmware %}selected{% endif %}>{{ frm }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
{% if dom_caps.cpu_modes %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU Config" %}</label>
<div class="col-sm-6">
<select id="vcpu_mode" name="vcpu_mode" class="form-control">
<option value=""> {% trans 'no-mode' %}</option>
{% for mode in dom_caps.cpu_modes %}
{% if mode == 'custom' %}
<optgroup label="Custom CPU Models">
{% for model in dom_caps.cpu_custom_models %}
<option value="{{ model }}"> {% trans model %}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ mode }}" {% ifequal mode default_cpu_mode %}selected {% endifequal %}>
{% trans mode %}
</option>
{% endif %}
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Storage" %}</label>
<div class="col-sm-6">
<input type="hidden" name="cache_mode" value="default">
<select name="storage" class="form-control">
@ -166,6 +202,18 @@
<input type="text" class="form-control" name="mac" maxlength="17" value="{{ mac_auto }}" required pattern="[a-zA-Z0-9:]+">
</div>
</div>
{% if dom_caps.graphics_support == 'yes' %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Graphics" %}</label>
<div class="col-sm-6">
<select name="graphics" class="form-control">
{% for graphics in dom_caps.graphics_types %}
<option value="{{ graphics }}" {% if default_graphics == graphics %}selected{% endif %}>{{ graphics }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-6">
@ -196,13 +244,6 @@
</select>
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<div class="col-sm-6">
<input type="checkbox" name="host_model" value="true" checked>
</div>
<label class="col-lg-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Guest Agent" %}</label>
<div class="col-sm-6">
@ -258,19 +299,47 @@
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="64" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
{% if firmwares %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Firmware" %}</label>
<div class="col-sm-7">
<select class="form-control" id="select_firmware" name="firmware">
{% for frm in firmwares %}
<option value="{{ frm }}" {% if frm == default_firmware %}selected{% endif %}>{{ frm }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-7">
<input type="text" class="form-control" name="vcpu" value="1" maxlength="2" required pattern="[0-9]">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
{% if dom_caps.cpu_modes %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU Config" %}</label>
<div class="col-sm-7">
<input type="checkbox" name="host_model" value="true" checked>
<select id="vcpu_mode" name="vcpu_mode" class="form-control">
<option value=""> {% trans 'no-mode' %}</option>
{% for mode in dom_caps.cpu_modes %}
{% if mode == 'custom' %}
<optgroup label="Custom CPU Models">
{% for model in dom_caps.cpu_custom_models %}
<option value="{{ model }}"> {% trans model %}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ mode }}" {% ifequal mode default_cpu_mode %}selected {% endifequal %}>
{% trans mode %}
</option>
{% endif %}
{% endfor %}
</select>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
<div class="col-sm-7">
@ -351,6 +420,18 @@
</select>
</div>
</div>
{% if dom_caps.graphics_support == 'yes' %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Graphics" %}</label>
<div class="col-sm-7">
<select name="graphics" class="form-control">
{% for graphics in dom_caps.graphics_types %}
<option value="{{ graphics }}" {% if default_graphics == graphics %}selected{% endif %}>{{ graphics }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-7">
@ -393,15 +474,18 @@
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
<div class="form-group">
<div class="col-sm-7 col-sm-offset-3">
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" formnovalidate onclick="showPleaseWaitDialog()" value="1">
<button type="submit" class="btn btn-block btn-primary" name="create" formnovalidate onclick="showPleaseWaitDialog()" value="1">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
<button class="btn btn-block btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</div></div>
</form>
</div>
<div class="clearfix"></div>
@ -416,6 +500,18 @@
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" maxlength="64" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
{% if firmwares %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Firmware" %}</label>
<div class="col-sm-7">
<select class="form-control" id="select_firmware" name="firmware">
{% for frm in firmwares %}
<option value="{{ frm }}" {% if frm == default_firmware %}selected{% endif %}>{{ frm }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "VCPU" %}</label>
<div class="col-sm-7">
@ -423,11 +519,25 @@
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Host-Model" %}</label>
<label class="col-sm-3 control-label">{% trans "VCPU Config" %}</label>
<div class="col-sm-7">
<input type="checkbox" name="host_model" value="true" checked>
<select id="vcpu_mode" name="vcpu_mode" class="form-control">
<option value=""> {% trans 'no-mode' %}</option>
{% for mode in dom_caps.cpu_modes %}
{% if mode == 'custom' %}
<optgroup label="Custom CPU Models">
{% for model in dom_caps.cpu_custom_models %}
<option value="{{ model }}"> {% trans model %}</option>
{% endfor %}
</optgroup>
{% else %}
<option value="{{ mode }}" {% ifequal mode default_cpu_mode %}selected {% endifequal %}>
{% trans mode %}
</option>
{% endif %}
{% endfor %}
</select>
</div>
<label class="col-sm-1 control-label">{% trans "CPU" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "RAM" %}</label>
@ -437,7 +547,7 @@
<label class="col-sm-1 control-label">{% trans "MB" %}</label>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "HDD" %}</label>
<label class="col-sm-3 control-label">{% trans "Template Disk" %}</label>
<input id="images" name="images" type="hidden" value=""/>
<div class="col-sm-3">
<select class="form-control" onchange="get_template_vols({{ compute_id }}, value);">
@ -510,6 +620,18 @@
</select>
</div>
</div>
{% if dom_caps.graphics_support == 'yes' %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Graphics" %}</label>
<div class="col-sm-7">
<select name="graphics" class="form-control">
{% for graphics in dom_caps.graphics_types %}
<option value="{{ graphics }}" {% if default_graphics == graphics %}selected{% endif %}>{{ graphics }}</option>
{% endfor %}
</select>
</div>
</div>
{% endif %}
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Video" %}</label>
<div class="col-sm-7">
@ -552,31 +674,19 @@
<input type="checkbox" name="virtio" value="true" checked>
</div>
</div>
{% if storages %}
<button type="submit" class="btn btn-primary" name="create" value="1" formnovalidate onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</form>
</div>
<div class="clearfix"></div>
</div>
<div role="tabpanel" class="tab-pane tab-pane-bordered" id="addFromXML">
<div class="well">
<form class="form-horizontal" method="post" role="form">{% csrf_token %}
<div class="col-sm-12" id="xmlheight">
<input type="hidden" name="dom_xml"/>
<textarea id="editor"></textarea>
<div class="form-group">
<div class="col-sm-7 col-sm-offset-3">
{% if storages %}
<button type="submit" class="btn btn-block btn-primary" name="create" value="1" formnovalidate onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
{% else %}
<button class="btn btn-primary disabled">
{% trans "Create" %}
</button>
{% endif %}
</div>
</div>
<button type="submit" class="btn btn-primary" name="create_xml" onclick="showPleaseWaitDialog()">
{% trans "Create" %}
</button>
</form>
</div>
<div class="clearfix"></div>
@ -589,7 +699,7 @@
<script src="{% static "js/bootstrap-multiselect.js" %}"></script>
<script>
function toggleValue(string, updated_value, checked) {
var result = '';
let result = '';
if (checked) {
result = string;
if (result != '') result += ',';
@ -620,15 +730,15 @@
return '';
},
onChange: function (element, checked) {
var input_value = toggleValue($('#images').val(), element.val(), checked);
let input_value = toggleValue($('#images').val(), element.val(), checked);
$('#images').val(input_value);
var selected_list_html = '';
var counter = 0;
let selected_list_html = '';
let counter = 0;
if (input_value != '') {
$('#disk_list_div').show();
$.each(input_value.split(','), function (index, value) {
var li = '<li>hdd' + counter + ' - ' +
let li = '<li>hdd' + counter + ' - ' +
'<select name="device' + counter + '" class="image-format" onchange="get_disk_bus_choices({{ compute_id }},' + counter + ', value);">' +
'{% for dev in disk_devices %}' +
'<option value=' + '"{{ dev }}">' + '{% trans dev %}</option>' +
@ -668,13 +778,13 @@
return '100%';
},
onChange: function (element, checked) {
var input_value = toggleValue($('#networks').val(), element.val(), checked);
let input_value = toggleValue($('#networks').val(), element.val(), checked);
$('#networks').val(input_value);
var selected_list_html = '';
var counter = 0;
let selected_list_html = '';
let counter = 0;
if (input_value != '') {
$.each(input_value.split(','), function (index, value) {
var li = '<li>eth' + counter +
let li = '<li>eth' + counter +
' -> ' + value + ' ' +
'<a class="btn-link pull-right" onclick="javascript:$(\'#network-control\').multiselect(\'deselect\', \'' + value + '\', true)"><i class="fa fa-remove"></i></a></a></li>';
selected_list_html += li;
@ -686,6 +796,8 @@
});
});
$("id[vcpu_mode]").multiselect();
function get_cust_vols(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) {
@ -708,11 +820,12 @@
$("#template").removeAttr("disabled");
$("#storage").val(pool).change();
$("#storage").removeAttr("disabled");
}
function get_disk_bus_choices(compute_id, dev_idx, disk_type){
get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses";
let arch = $('select[name="arch"]').val();
let machine = $("select[id='machine-control']").val();
get_diskBus_url = "/computes/" + compute_id + "/archs/" + arch + "/machines/" + machine + "/disks/" + disk_type + "/buses";
$.getJSON(get_diskBus_url, function (data) {
$("#bus" + dev_idx).find('option').remove();
$.each(data['bus'], function(i, item) {
@ -721,15 +834,12 @@
});
}
</script>
<script src="{% static "js/ace.js" %}"></script>
<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>
{% if request.user.is_superuser %}
<script>
function goto_compute() {
let compute = {{ compute.id }}
window.location.href = "{% url 'create_instance_select_type' 1 %}".replace(1, compute);
}
</script>
{% endif %}
{% endblock %}

View file

@ -13,25 +13,76 @@ from libvirt import libvirtError
from webvirtcloud.settings import QEMU_CONSOLE_LISTEN_ADDRESSES
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_CACHE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_BUS
from webvirtcloud.settings import INSTANCE_CPU_DEFAULT_MODE
from webvirtcloud.settings import INSTANCE_MACHINE_DEFAULT_TYPE
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from django.contrib import messages
from logs.views import addlogmsg
@login_required
def create_instance(request, compute_id):
"""
:param request:
:param compute_id:
:return:
"""
def create_instance_select_type(request, compute_id):
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None
error_messages = []
storages = []
networks = []
error_messages = list()
storages = list()
networks = list()
hypervisors = list()
meta_prealloc = False
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmCreate(compute.hostname,
compute.login,
compute.password,
compute.type)
instances = conn.get_instances()
all_hypervisors = conn.get_hypervisors_machines()
# Supported hypervisors by webvirtcloud: i686, x86_64(for now)
supported_arch = ["x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"]
hypervisors = [hpv for hpv in all_hypervisors.keys() if hpv in supported_arch ]
default_machine = INSTANCE_MACHINE_DEFAULT_TYPE
if request.method == 'POST':
if 'create_xml' in request.POST:
xml = request.POST.get('dom_xml', '')
try:
name = util.get_xml_path(xml, '/domain/name')
except util.etree.Error as err:
name = None
if name in instances:
error_msg = _("A virtual machine with this name already exists")
error_messages.append(error_msg)
else:
try:
conn._defineXML(xml)
return HttpResponseRedirect(reverse('instance', args=[compute_id, name]))
except libvirtError as lib_err:
error_messages.append(lib_err.message)
except libvirtError as lib_err:
error_messages.append(lib_err)
return render(request, 'create_instance_w1.html', locals())
@login_required
def create_instance(request, compute_id, arch, machine):
"""
:param request:
:param compute_id:
:return:
"""
if not request.user.is_superuser:
return HttpResponseRedirect(reverse('index'))
conn = None
error_messages = list()
storages = list()
networks = list()
hypervisors = list()
firmwares = list()
meta_prealloc = False
compute = get_object_or_404(Compute, pk=compute_id)
flavors = Flavor.objects.filter().order_by('id')
@ -42,18 +93,34 @@ def create_instance(request, compute_id):
compute.password,
compute.type)
default_cpu_mode = INSTANCE_CPU_DEFAULT_MODE
instances = conn.get_instances()
videos = conn.get_video_models()
videos = conn.get_video_models(arch, machine)
cache_modes = sorted(conn.get_cache_modes().items())
default_cache = INSTANCE_VOLUME_DEFAULT_CACHE
listener_addr = QEMU_CONSOLE_LISTEN_ADDRESSES
mac_auto = util.randomMAC()
disk_devices = conn.get_disk_device_types()
disk_buses = conn.get_disk_bus_types()
disk_devices = conn.get_disk_device_types(arch, machine)
disk_buses = conn.get_disk_bus_types(arch, machine)
default_bus = INSTANCE_VOLUME_DEFAULT_BUS
networks = sorted(conn.get_networks())
nwfilters = conn.get_nwfilters()
storages = sorted(conn.get_storages(only_actives=True))
default_graphics = QEMU_CONSOLE_DEFAULT_TYPE
dom_caps = conn.get_dom_capabilities(arch, machine)
caps = conn.get_capabilities(arch)
hv_supports_uefi = conn.supports_uefi_xml(dom_caps["loader_enums"])
# Add BIOS
label = conn.label_for_firmware_path(arch, None)
if label: firmwares.append(label)
# Add UEFI
loader_path = conn.find_uefi_path_for_arch(arch, dom_caps["loaders"])
label = conn.label_for_firmware_path(arch, loader_path)
if label: firmwares.append(label)
firmwares = list(set(firmwares))
except libvirtError as lib_err:
error_messages.append(lib_err)
@ -81,24 +148,9 @@ def create_instance(request, compute_id):
delete_flavor = Flavor.objects.get(id=flavor_id)
delete_flavor.delete()
return HttpResponseRedirect(request.get_full_path())
if 'create_xml' in request.POST:
xml = request.POST.get('dom_xml', '')
try:
name = util.get_xml_path(xml, '/domain/name')
except util.etree.Error as err:
name = None
if name in instances:
error_msg = _("A virtual machine with this name already exists")
error_messages.append(error_msg)
else:
try:
conn._defineXML(xml)
return HttpResponseRedirect(reverse('instance', args=[compute_id, name]))
except libvirtError as lib_err:
error_messages.append(lib_err.message)
if 'create' in request.POST:
volume_list = []
volume_list = list()
is_disk_created = False
clone_path = ""
form = NewVMForm(request.POST)
if form.is_valid():
@ -124,8 +176,9 @@ def create_instance(request, compute_id):
volume['path'] = path
volume['type'] = conn.get_volume_type(path)
volume['device'] = 'disk'
volume['bus'] = 'virtio'
volume['bus'] = INSTANCE_VOLUME_DEFAULT_BUS
volume_list.append(volume)
is_disk_created = True
except libvirtError as lib_err:
error_messages.append(lib_err.message)
elif data['template']:
@ -140,8 +193,9 @@ def create_instance(request, compute_id):
volume['path'] = clone_path
volume['type'] = conn.get_volume_type(clone_path)
volume['device'] = 'disk'
volume['bus'] = 'virtio'
volume['bus'] = INSTANCE_VOLUME_DEFAULT_BUS
volume_list.append(volume)
is_disk_created = True
else:
if not data['images']:
error_msg = _("First you need to create or select an image")
@ -164,10 +218,15 @@ def create_instance(request, compute_id):
if not error_messages:
uuid = util.randomUUID()
try:
conn.create_instance(data['name'], data['memory'], data['vcpu'], data['host_model'],
uuid, volume_list, data['cache_mode'], data['networks'], data['virtio'],
data["listener_addr"], data["nwfilter"], data["video"], data["console_pass"],
data['mac'], data['qemu_ga'])
conn.create_instance(name=data['name'], memory=data['memory'], vcpu=data['vcpu'],
vcpu_mode=data['vcpu_mode'], uuid=uuid, arch=arch, machine=machine,
firmware=data["firmware"],
images=volume_list, cache_mode=data['cache_mode'],
networks=data['networks'], virtio=data['virtio'],
listen_addr=data["listener_addr"], nwfilter=data["nwfilter"],
graphics=data["graphics"], video=data["video"],
console_pass=data["console_pass"], mac=data['mac'],
qemu_ga=data['qemu_ga'])
create_instance = Instance(compute_id=compute_id, name=data['name'], uuid=uuid)
create_instance.save()
msg = _("Instance is created.")
@ -176,8 +235,9 @@ def create_instance(request, compute_id):
return HttpResponseRedirect(reverse('instance', args=[compute_id, data['name']]))
except libvirtError as lib_err:
if data['hdd_size'] or len(volume_list) > 0:
for vol in volume_list:
conn.delete_volume(vol['path'])
if is_disk_created:
for vol in volume_list:
conn.delete_volume(vol['path'])
error_messages.append(lib_err)
conn.close()
return render(request, 'create_instance.html', locals())
return render(request, 'create_instance_w2.html', locals())

View file

@ -176,8 +176,9 @@
{% if request.user.is_superuser %}
<script>
function goto_compute() {
var compute = $("#compute_select").val();
window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);
let compute = $("#compute_select").val();
{#window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);#}
window.location.href = "{% url 'create_instance_select_type' 1 %}".replace(1, compute);
}
</script>
{% endif %}

View file

@ -10,7 +10,7 @@
<div class="row">
<div class="col-lg-12">
{% if request.user.is_superuser %}
<a href="{% url 'create_instance' compute.id %}" type="button" class="btn btn-success btn-header pull-right" data-toggle="modal">
<a href="{% url 'create_instance_select_type' compute.id %}" type="button" class="btn btn-success btn-header pull-right" data-toggle="modal">
<span class="glyphicon glyphicon-plus" aria-hidden="true"></span>
</a>
{% endif %}
@ -161,7 +161,7 @@
<script src="{% static "js/sortable.min.js" %}"></script>
<script>
function open_console(uuid) {
window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=485");
window.open("{% url 'console' %}?token=" + uuid, "", "width=850,height=685");
}
</script>
<script>
@ -189,12 +189,4 @@
window.location = "/instances/" + compute + "/" + instance + "/#clone";
}
</script>
{% if request.user.is_superuser %}
<script>
function goto_compute() {
var compute = $("#compute_select").val();
window.location.href = "{% url 'create_instance' 1 %}".replace(1, compute);
}
</script>
{% endif %}
{% endblock %}

View file

@ -22,7 +22,7 @@ from vrtManager.connection import connection_manager
from vrtManager.create import wvmCreate
from vrtManager.storage import wvmStorage
from vrtManager.util import randomPasswd
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE
from libvirt import libvirtError, VIR_DOMAIN_XML_SECURE, VIR_DOMAIN_UNDEFINE_KEEP_NVRAM, VIR_DOMAIN_UNDEFINE_NVRAM
from logs.views import addlogmsg
from django.conf import settings
from django.contrib import messages
@ -265,6 +265,8 @@ def instance(request, compute_id, vname):
autostart = conn.get_autostart()
bootmenu = conn.get_bootmenu()
boot_order = conn.get_bootorder()
arch = conn.get_arch()
machine = conn.get_machine_type()
vcpu = conn.get_vcpu()
cur_vcpu = conn.get_cur_vcpu()
vcpus = conn.get_vcpus()
@ -288,6 +290,7 @@ def instance(request, compute_id, vname):
insort(memory_range, memory)
if cur_memory not in memory_range:
insort(memory_range, cur_memory)
nvram = conn.get_nvram()
telnet_port = conn.get_telnet_port()
console_type = conn.get_console_type()
console_port = conn.get_console_port()
@ -330,8 +333,8 @@ def instance(request, compute_id, vname):
# Host resources
vcpu_host = len(vcpu_range)
memory_host = conn.get_max_memory()
bus_host = conn.get_disk_bus_types()
videos_host = conn.get_video_models()
bus_host = conn.get_disk_bus_types(arch, machine)
videos_host = conn.get_video_models(arch, machine)
networks_host = sorted(conn.get_networks())
interfaces_host = sorted(conn.get_ifaces())
nwfilters_host = conn.get_nwfilters()
@ -374,7 +377,11 @@ def instance(request, compute_id, vname):
for snap in snapshots:
conn.snapshot_delete(snap['name'])
conn.delete_all_disks()
conn.delete()
if request.POST.get('delete_nvram', ''):
conn.delete(VIR_DOMAIN_UNDEFINE_NVRAM)
else:
conn.delete(VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)
instance = Instance.objects.get(compute_id=compute_id, name=vname)
instance_name = instance.name

View file

@ -1,6 +1,7 @@
import libvirt
import threading
import socket
import re
from vrtManager import util
from vrtManager.rwlock import ReadWriteLock
from django.conf import settings
@ -344,15 +345,88 @@ class wvmConnect(object):
"""Return xml capabilities"""
return self.wvm.getCapabilities()
def get_dom_cap_xml(self):
""" Return domcapabilities xml"""
arch = self.wvm.getInfo()[0]
machine = self.get_machines(arch)
def get_dom_cap_xml(self, arch, machine):
""" Return domain capabilities xml"""
emulatorbin = self.get_emulator(arch)
virttype = self.hypervisor_type()[arch][0]
virttype = self.get_hypervisors_domain_types()[arch][0]
machine_types = self.get_machine_types(arch)
if not machine or machine not in machine_types:
machine = 'pc' if 'pc' in machine_types else machine_types[0]
return self.wvm.getDomainCapabilities(emulatorbin, arch, machine, virttype)
def get_capabilities(self, arch):
""" Host Capabilities for specified architecture """
def guests(ctx):
result = dict()
for arch_el in ctx.xpath("/capabilities/guest/arch[@name='{}']".format(arch)):
result["wordsize"] = arch_el.find("wordsize").text
result["emulator"] = arch_el.find("emulator").text
result["domain"] = [v for v in arch_el.xpath("domain/@type")]
result["machines"] = []
for m in arch_el.xpath("machine"):
result["machines"].append({"machine": m.text,
"max_cpu": m.get("maxCpus"),
"canonical": m.get("canonical")})
guest_el = arch_el.getparent()
for f in guest_el.xpath("features"):
result["features"] = [t.tag for t in f.getchildren()]
result["os_type"] = guest_el.find("os_type").text
return result
return util.get_xml_path(self.get_cap_xml(), func=guests)
def get_dom_capabilities(self, arch, machine):
"""Return domain capabilities"""
result = dict()
xml = self.get_dom_cap_xml(arch, machine)
result["path"] = util.get_xml_path(xml,"/domainCapabilities/path")
result["domain"] = util.get_xml_path(xml, "/domainCapabilities/domain")
result["machine"] = util.get_xml_path(xml, "/domainCapabilities/machine")
result["vcpu_max"] = util.get_xml_path(xml, "/domainCapabilities/vcpu/@max")
result["iothreads_support"] = util.get_xml_path(xml, "/domainCapabilities/iothreads/@supported")
result["os_support"] = util.get_xml_path(xml, "/domainCapabilities/os/@supported")
result["loader_support"] = util.get_xml_path(xml, "/domainCapabilities/os/loader/@supported")
if result["loader_support"] == 'yes':
result["loaders"] = self.get_os_loaders(arch, machine)
result["loader_enums"] = self.get_os_loader_enums(arch, machine)
result["cpu_modes"] = self.get_cpu_modes(arch, machine)
if "custom" in result["cpu_modes"]:
# supported and unknown cpu models
result["cpu_custom_models"] = self.get_cpu_custom_types(arch, machine)
result["disk_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/disk/@supported")
if result["disk_support"] == 'yes':
result["disk_devices"] = self.get_disk_device_types(arch, machine)
result["disk_bus"] = self.get_disk_bus_types(arch, machine)
result["graphics_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/graphics/@supported")
if result["graphics_support"] == 'yes':
result["graphics_types"] = self.get_graphics_types(arch, machine)
result["video_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/video/@supported")
if result["video_support"] == 'yes':
result["video_types"] = self.get_video_models(arch, machine)
result["hostdev_support"] = util.get_xml_path(xml, "/domainCapabilities/devices/hostdev/@supported")
if result["hostdev_support"] == 'yes':
result["hostdev_types"] = self.get_hostdev_modes(arch, machine)
result["hostdev_startup_policies"] = self.get_hostdev_startup_policies(arch, machine)
result["hostdev_subsys_types"] = self.get_hostdev_subsys_types(arch, machine)
result["features_gic_support"] = util.get_xml_path(xml, "/domainCapabilities/features/gic/@supported")
result["features_genid_support"] = util.get_xml_path(xml, "/domainCapabilities/features/genid/@supported")
result["features_vmcoreinfo_support"] = util.get_xml_path(xml, "/domainCapabilities/features/vmcoreinfo/@supported")
result["features_sev_support"] = util.get_xml_path(xml, "/domainCapabilities/features/sev/@supported")
return result
def get_version(self):
ver = self.wvm.getVersion()
major = ver / 1000000
@ -417,7 +491,7 @@ class wvmConnect(object):
'unsafe': 'Unsafe', # since libvirt 0.9.7
}
def hypervisor_type(self):
def get_hypervisors_domain_types(self):
"""Return hypervisor type"""
def hypervisors(ctx):
result = {}
@ -428,10 +502,34 @@ class wvmConnect(object):
return result
return util.get_xml_path(self.get_cap_xml(), func=hypervisors)
def get_hypervisors_machines(self):
"""Return hypervisor and its machine types"""
def machines(ctx):
result = dict()
for arche in ctx.xpath('/capabilities/guest/arch'):
arch = arche.get("name")
result[arch] = self.get_machine_types(arch)
return result
return util.get_xml_path(self.get_cap_xml(), func=machines)
def get_emulator(self, arch):
"""Return emulator """
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/emulator".format(arch))
def get_machine_types(self, arch):
"""Return canonical(if exist) name of machine types """
def machines(ctx):
result = list()
canonical_name = ctx.xpath("/capabilities/guest/arch[@name='{}']/machine[@canonical]".format(arch))
if not canonical_name:
canonical_name = ctx.xpath("/capabilities/guest/arch[@name='{}']/machine".format(arch))
for archi in canonical_name:
result.append(archi.text)
return result
return util.get_xml_path(self.get_cap_xml(), func=machines)
def get_emulators(self):
def emulators(ctx):
result = {}
@ -442,35 +540,76 @@ class wvmConnect(object):
return result
return util.get_xml_path(self.get_cap_xml(), func=emulators)
def get_machines(self, arch):
""" Return machine type of emulation"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/arch[@name='{}']/machine".format(arch))
def get_os_loaders(self, arch='x86_64', machine='pc'):
"""Get available os loaders list"""
def get_os_loaders(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/os/loader[@supported='yes']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loaders)
def get_disk_bus_types(self):
def get_os_loader_enums(self, arch, machine):
"""Get available os loaders list"""
def get_os_loader_enums(ctx):
result = dict()
enums = [v for v in ctx.xpath("/domainCapabilities/os/loader[@supported='yes']/enum/@name")]
for enum in enums:
path = "/domainCapabilities/os/loader[@supported='yes']/enum[@name='{}']/value".format(enum)
result[enum] = [v.text for v in ctx.xpath(path)]
return result
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_os_loader_enums)
def get_disk_bus_types(self, arch, machine):
"""Get available disk bus types list"""
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 [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='bus']/value")]
# return [ 'ide', 'scsi', 'usb', 'virtio' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_bus_list)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_bus_list)
def get_disk_device_types(self):
def get_disk_device_types(self, arch, machine):
"""Get available disk device type list"""
def get_device_list(ctx):
result = []
for disk_enum in ctx.xpath('/domainCapabilities/devices/disk/enum'):
if disk_enum.xpath("@name")[0] == "diskDevice":
for values in disk_enum: result.append(values.text)
return result
return [v.text for v in ctx.xpath("/domainCapabilities/devices/disk/enum[@name='diskDevice']/value")]
# return [ 'disk', 'cdrom', 'floppy', 'lun' ]
return util.get_xml_path(self.get_dom_cap_xml(), func=get_device_list)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_device_list)
def get_graphics_types(self, arch, machine):
"""Get available graphics types """
def get_graphics_list(ctx):
return [ v.text for v in ctx.xpath("/domainCapabilities/devices/graphics/enum[@name='type']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_graphics_list)
def get_cpu_modes(self, arch, machine):
"""Get available cpu modes """
def get_cpu_modes(ctx):
return [v for v in ctx.xpath("/domainCapabilities/cpu/mode[@supported='yes']/@name")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_cpu_modes)
def get_cpu_custom_types(self, arch, machine):
"""Get available graphics types """
def get_custom_list(ctx):
usable_yes = "/domainCapabilities/cpu/mode[@name='custom'][@supported='yes']/model[@usable='yes']"
usable_unknown = "/domainCapabilities/cpu/mode[@name='custom'][@supported='yes']/model[@usable='unknown']"
result = [v.text for v in ctx.xpath(usable_yes)]
result += [v.text for v in ctx.xpath(usable_unknown)]
return result
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_custom_list)
def get_hostdev_modes(self, arch, machine):
"""Get available nodedev modes """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='mode']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_hostdev_startup_policies(self, arch, machine):
"""Get available hostdev modes """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='startupPolicy']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_hostdev_subsys_types(self, arch, machine):
"""Get available nodedev sub system types """
def get_hostdev_list(ctx):
return [v.text for v in ctx.xpath("/domainCapabilities/devices/hostdev/enum[@name='subsysType']/value")]
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_hostdev_list)
def get_image_formats(self):
"""Get available image formats"""
@ -480,7 +619,7 @@ class wvmConnect(object):
"""Get available image filename extensions"""
return ['img', 'qcow', 'qcow2']
def get_video_models(self):
def get_video_models(self, arch, machine):
""" Get available graphics video types """
def get_video_list(ctx):
result = []
@ -488,7 +627,7 @@ class wvmConnect(object):
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)
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=get_video_list)
def get_iface(self, name):
return self.wvm.interfaceLookupByName(name)
@ -624,3 +763,49 @@ class wvmConnect(object):
# to-do: do not close connection ;)
# self.wvm.close()
pass
def find_uefi_path_for_arch(self, arch, machine):
"""
Search the loader paths for one that matches the passed arch
"""
if not self.arch_can_uefi(arch):
return
loaders = self.get_os_loaders(arch, machine)
patterns = util.uefi_arch_patterns.get(arch)
for pattern in patterns:
for path in loaders:
if re.match(pattern, path):
return path
def label_for_firmware_path(self, arch, path):
"""
Return a pretty label for passed path, based on if we know
about it or not
"""
if not path:
if arch in ["i686", "x86_64"]:
return "BIOS"
return
for arch, patterns in util.uefi_arch_patterns.items():
for pattern in patterns:
if re.match(pattern, path):
return ("UEFI %(arch)s: %(path)s" %
{"arch": arch, "path": path})
return "Custom: %(path)s" % {"path": path}
def arch_can_uefi(self, arch):
"""
Return True if we know how to setup UEFI for the passed arch
"""
return arch in list(util.uefi_arch_patterns.keys())
def supports_uefi_xml(self, loader_enums):
"""
Return True if libvirt advertises support for proper UEFI setup
"""
return ("readonly" in loader_enums and
"yes" in loader_enums.get("readonly"))

View file

@ -1,9 +1,11 @@
import string
from vrtManager import util
from vrtManager.connection import wvmConnect
from webvirtcloud.settings import QEMU_CONSOLE_DEFAULT_TYPE
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as default_owner
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_OWNER as DEFAULT_OWNER
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_FORMAT
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
from webvirtcloud.settings import INSTANCE_VOLUME_DEFAULT_DRIVER_OPTS as OPTS
def get_rbd_storage_data(stg):
@ -11,7 +13,7 @@ def get_rbd_storage_data(stg):
ceph_user = util.get_xml_path(xml, "/pool/source/auth/@username")
def get_ceph_hosts(doc):
hosts = []
hosts = list()
for host in doc.xpath("/pool/source/host"):
name = host.prop("name")
if name:
@ -29,7 +31,7 @@ class wvmCreate(wvmConnect):
"""
Function return all images on all storages
"""
images = []
images = list()
storages = self.get_storages(only_actives=True)
for storage in storages:
stg = self.get_storage(storage)
@ -45,14 +47,14 @@ class wvmCreate(wvmConnect):
return images
def get_os_type(self):
"""Get guest capabilities"""
"""Get guest os type"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/guest/os_type")
def get_host_arch(self):
"""Get guest capabilities"""
return util.get_xml_path(self.get_cap_xml(), "/capabilities/host/cpu/arch")
def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=default_owner):
def create_volume(self, storage, name, size, image_format=image_format, metadata=False, owner=DEFAULT_OWNER):
size = int(size) * 1073741824
stg = self.get_storage(storage)
storage_type = util.get_xml_path(stg.XMLDesc(0), "/pool/@type")
@ -120,7 +122,7 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(vol_path)
return vol.storagePoolLookupByVolume()
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=default_owner):
def clone_from_template(self, clone, template, storage=None, metadata=False, owner=DEFAULT_OWNER):
vol = self.get_volume_by_path(template)
if not storage:
stg = vol.storagePoolLookupByVolume()
@ -163,16 +165,15 @@ class wvmCreate(wvmConnect):
vol = self.get_volume_by_path(path)
vol.delete()
def create_instance(self, name, memory, vcpu, host_model, uuid, images, cache_mode, networks, virtio, listen_addr, nwfilter=None, video="cirrus", console_pass="random", mac=None, qemu_ga=False):
def create_instance(self, name, memory, vcpu, vcpu_mode, uuid, arch, machine, firmware, images, cache_mode, networks, nwfilter, graphics, virtio, listen_addr, video="vga", console_pass="random", mac=None, qemu_ga=False):
"""
Create VM function
"""
memory = int(memory) * 1024
caps = self.get_capabilities(arch)
dom_caps = self.get_dom_capabilities(arch, machine)
if self.is_kvm_supported():
hypervisor_type = 'kvm'
else:
hypervisor_type = 'qemu'
memory = int(memory) * 1024
#hypervisor_type = 'kvm' if self.is_kvm_supported() else 'qemu'
xml = """
<domain type='%s'>
@ -180,23 +181,46 @@ class wvmCreate(wvmConnect):
<description>None</description>
<uuid>%s</uuid>
<memory unit='KiB'>%s</memory>
<vcpu>%s</vcpu>""" % (hypervisor_type, name, uuid, memory, vcpu)
if host_model:
<vcpu>%s</vcpu>""" % (dom_caps["domain"], name, uuid, memory, vcpu)
if dom_caps["os_support"] == 'yes':
xml += """<os>
<type arch='%s' machine='%s'>%s</type>""" % (arch, machine, caps["os_type"])
xml += """ <boot dev='hd'/>
<boot dev='cdrom'/>
<bootmenu enable='yes'/>"""
if 'UEFI' in firmware:
xml += """<loader readonly='yes' type='pflash'>%s</loader>""" % firmware.split(":")[1].strip()
xml += """</os>"""
if caps["features"]:
xml += """<features>"""
if 'acpi' in caps["features"]:
xml += """<acpi/>"""
if 'apic' in caps["features"]:
xml += """<apic/>"""
if 'pae' in caps["features"]:
xml += """<pae/>"""
xml += """</features>"""
if vcpu_mode == "host-model":
xml += """<cpu mode='host-model'/>"""
xml += """<os>
<type arch='%s'>%s</type>
<boot dev='hd'/>
<boot dev='cdrom'/>
<bootmenu enable='yes'/>
</os>""" % (self.get_host_arch(), self.get_os_type())
xml += """<features>
<acpi/><apic/><pae/>
</features>
elif vcpu_mode == "host-passthrough":
xml += """<cpu mode='host-passthrough'/>"""
elif vcpu_mode == "":
pass
else:
xml += """<cpu mode='custom' match='exact' check='none'>
<model fallback='allow'>{}</model>
</cpu>""".format(vcpu_mode)
xml += """
<clock offset="utc"/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>restart</on_crash>
<devices>"""
"""
xml += """<devices>"""
vd_disk_letters = list(string.lowercase)
fd_disk_letters = list(string.lowercase)
@ -212,11 +236,11 @@ class wvmCreate(wvmConnect):
if stg_type == 'rbd':
ceph_user, secret_uuid, ceph_hosts = get_rbd_storage_data(stg)
xml += """<disk type='network' device='disk'>
<driver name='qemu' type='%s' cache='%s'/>
<auth username='%s'>
<driver name='qemu' type='%s' cache='%s' %s />""" % (volume['type'], cache_mode, OPTS.get("network", ''))
xml += """ <auth username='%s'>
<secret type='ceph' uuid='%s'/>
</auth>
<source protocol='rbd' name='%s'>""" % (volume['type'], cache_mode, ceph_user, secret_uuid, volume['path'])
<source protocol='rbd' name='%s'>""" % (ceph_user, secret_uuid, volume['path'])
if isinstance(ceph_hosts, list):
for host in ceph_hosts:
if host.get('port'):
@ -225,12 +249,11 @@ class wvmCreate(wvmConnect):
else:
xml += """
<host name='%s'/>""" % host.get('name')
xml += """
</source>"""
xml += """</source>"""
else:
xml += """<disk type='file' device='%s'>
<driver name='qemu' type='%s' cache='%s'/>
<source file='%s'/>""" % (volume['device'], volume['type'], cache_mode, volume['path'])
xml += """<disk type='file' device='%s'>""" % volume['device']
xml += """ <driver name='qemu' type='%s' cache='%s' %s/>""" % (volume['type'], cache_mode, OPTS.get("file", ''))
xml += """ <source file='%s'/>""" % volume['path']
if volume['bus'] == 'virtio':
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), volume['bus'])
@ -242,12 +265,23 @@ class wvmCreate(wvmConnect):
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), volume['bus'])
xml += """</disk>"""
if add_cd:
xml += """ <disk type='file' device='cdrom'>
xml += """<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file=''/>
<target dev='hd%s' bus='ide'/>
<readonly/>
</disk>""" % (hd_disk_letters.pop(0),)
<source file = '' />
<readonly/>"""
if 'ide' in dom_caps['disk_bus']:
xml += """<target dev='hd%s' bus='%s'/>""" % (hd_disk_letters.pop(0), 'ide')
elif 'sata' in dom_caps['disk_bus']:
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), 'sata')
elif 'scsi' in dom_caps['disk_bus']:
xml += """<target dev='sd%s' bus='%s'/>""" % (sd_disk_letters.pop(0), 'scsi')
else:
xml += """<target dev='vd%s' bus='%s'/>""" % (vd_disk_letters.pop(0), 'virtio')
xml += """</disk>"""
if volume['bus'] == 'scsi':
xml += """<controller type='scsi' model='%s'/>""" % INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER
for net in networks.split(','):
xml += """<interface type='network'>"""
if mac:
@ -265,10 +299,11 @@ class wvmCreate(wvmConnect):
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' %s listen='%s'/>
<console type='pty'/> """ % (QEMU_CONSOLE_DEFAULT_TYPE, console_pass, listen_addr)
xml += """<input type='mouse' bus='virtio'/>"""
xml += """<input type='tablet' bus='virtio'/>"""
xml += """
<graphics type='%s' port='-1' autoport='yes' %s listen='%s'/>
<console type='pty'/> """ % (graphics, console_pass, listen_addr)
if qemu_ga:
xml += """ <channel type='unix'>
@ -278,7 +313,6 @@ class wvmCreate(wvmConnect):
xml += """ <video>
<model type='%s'/>
</video>
<memballoon model='virtio'/>
</devices>
</domain>""" % video
self._defineXML(xml)

View file

@ -149,11 +149,31 @@ SHOW_PROFILE_EDIT_PASSWORD = False
# available: default (grid), list
VIEW_ACCOUNTS_STYLE = 'grid'
# available: default (grouped), nongrouped
# available list style: default (grouped), nongrouped
VIEW_INSTANCES_LIST_STYLE = 'grouped'
# available volume format: raw, qcow2, qcow
INSTANCE_VOLUME_DEFAULT_FORMAT = 'qcow2'
# available bus types: virtio, scsi, ide, usb, sata
INSTANCE_VOLUME_DEFAULT_BUS = 'virtio'
#SCSI types: 'virtio-scsi', 'lsilogic'
INSTANCE_VOLUME_DEFAULT_SCSI_CONTROLLER = 'virtio-scsi'
# Volume optionals: two variable: disk driver type is file and network(rbd, iscsi),
# optionals : discard='unmap|ignore', detect_zeroes='on|off|unmap', copy_on_read='on|off'
# Example: {"file": "discard='unmap' copy_on_read='on'", "network": "detect_zeroes='unmap'"}
INSTANCE_VOLUME_DEFAULT_DRIVER_OPTS = {"file": "", "network": ""}
# available cache types: none, unsafe, writeback, writethrough
INSTANCE_VOLUME_DEFAULT_CACHE = 'directsync'
# up to os, 0=root, 107=qemu or libvirt-bin(for ubuntu)
INSTANCE_VOLUME_DEFAULT_OWNER = {'uid': 0, 'guid': 0}
# Cpu modes: host-model, host-passthrough, custom
INSTANCE_CPU_DEFAULT_MODE = 'host-model'
# Chipset/Machine: pc or q35 for x86_64
INSTANCE_MACHINE_DEFAULT_TYPE = 'q35'