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

Merge pull request #208 from catborise/master

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@
<p style="font-weight:bold;">{% trans "Volume parameters" %}</p> <p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label> <label class="col-sm-3 control-label">{% trans "Storage" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="storage" class="form-control image-format"> <select name="storage" class="form-control image-format">
{% for storage in storages %} {% for storage in storages %}
@ -35,13 +35,13 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Name" %}</label> <label class="col-sm-3 control-label">{% trans "Name" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+"> <input type="text" class="form-control" name="name" placeholder="{% trans "Name" %}" required pattern="[a-zA-Z0-9\.\-_]+">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Format" %}</label> <label class="col-sm-3 control-label">{% trans "Format" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="format" class="form-control image-format"> <select name="format" class="form-control image-format">
{% for format in formats %} {% for format in formats %}
@ -51,14 +51,14 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Size" %}</label> <label class="col-sm-3 control-label">{% trans "Size" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" class="form-control" name="size" value="10" maxlength="3" required pattern="[0-9]+"> <input type="text" class="form-control" name="size" value="10" maxlength="5" required pattern="[0-9]+">
</div> </div>
<label class="col-sm-1 control-label">{% trans "GB" %}</label> <label class="col-sm-1 control-label">{% trans "GB" %}</label>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label> <label class="col-sm-3 control-label">{% trans "Bus" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="bus" class="form-control image-format"> <select name="bus" class="form-control image-format">
{% for bus in busses %} {% for bus in busses %}
@ -68,7 +68,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label> <label class="col-sm-3 control-label">{% trans "Cache" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="cache" class="form-control image-format"> <select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %} {% for mode, name in cache_modes %}
@ -78,14 +78,14 @@
</div> </div>
</div> </div>
<div class="form-group meta-prealloc"> <div class="form-group meta-prealloc">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Metadata" %}</label> <label class="col-sm-3 control-label">{% trans "Metadata" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="checkbox" name="meta_prealloc" value="true"> <input type="checkbox" name="meta_prealloc" value="true">
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{% ifequal status 5 %} {% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="addnewvol">{% trans "Add Volume" %}</button> <button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
{% else %} {% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button> <button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %} {% endifequal %}
@ -97,7 +97,7 @@
<div class="modal-body"> <div class="modal-body">
<p style="font-weight:bold;">{% trans "Volume parameters" %}</p> <p style="font-weight:bold;">{% trans "Volume parameters" %}</p>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Storage" %}</label> <label class="col-sm-3 control-label">{% trans "Storage" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="dropdown"> <div class="dropdown">
<button id="select_storage" class="btn btn-default dropdown-toggle form-control" type="button" data-toggle="dropdown">{% trans 'Select Pool...' %} <button id="select_storage" class="btn btn-default dropdown-toggle form-control" type="button" data-toggle="dropdown">{% trans 'Select Pool...' %}
@ -112,7 +112,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Volume" %}</label> <label class="col-sm-3 control-label" >{% trans "Volume" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select id="vols" name="vols" class="form-control" disabled> <select id="vols" name="vols" class="form-control" disabled>
<option value="" selected>{% trans 'None' %}</option> <option value="" selected>{% trans 'None' %}</option>
@ -121,7 +121,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Bus" %}</label> <label class="col-sm-3 control-label">{% trans "Bus" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="bus" class="form-control image-format"> <select name="bus" class="form-control image-format">
{% for bus in busses %} {% for bus in busses %}
@ -131,7 +131,7 @@
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="col-sm-3 control-label" style="font-weight:normal;">{% trans "Cache" %}</label> <label class="col-sm-3 control-label">{% trans "Cache" %}</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select name="cache" class="form-control image-format"> <select name="cache" class="form-control image-format">
{% for mode, name in cache_modes %} {% for mode, name in cache_modes %}
@ -143,7 +143,7 @@
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{% ifequal status 5 %} {% ifequal status 5 %}
<button type="submit" class="btn btn-lg btn-success pull-right" name="addexistingvol">{% trans "Add Volume" %}</button> <button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
{% else %} {% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button> <button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %} {% endifequal %}

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -205,12 +205,23 @@ def storage(request, compute_id, pool):
else: else:
for msg_err in form.errors.values(): for msg_err in form.errors.values():
error_messages.append(msg_err.as_text()) error_messages.append(msg_err.as_text())
if request.method == 'GET':
if 'get_volumes' in request.GET:
conn.close()
return HttpResponse(json.dumps(sorted(volumes)))
conn.close() conn.close()
return render(request, 'storage.html', locals()) return render(request, 'storage.html', locals())
@login_required
def get_volumes(request, compute_id, pool):
data = {}
compute = get_object_or_404(Compute, pk=compute_id)
try:
conn = wvmStorage(compute.hostname,
compute.login,
compute.password,
compute.type,
pool)
except libvirtError as liberr:
pass
data['vols'] = sorted(conn.get_volumes())
return HttpResponse(json.dumps(data))

View file

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

View file

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

View file

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