[PATCH 0/2] UI: support ldap permission tag
by lvroyce@linux.vnet.ibm.com
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Users added will be validated when press save permission button,
invalid user will be labled as notice icon.
update vm request with invalid user will be rejected.
only user tag is supported for ldap.
This is tested agaist ldap integration v2.
Note:
Due to capabilities query of authentication type is not added.
This patch simply hided PAM permission tag part.
Will address this issue after backend changed.
Royce Lv (2):
UI: support ldap vm permission tag
Change guest edit permission logic
ui/css/theme-default/guest-edit.css | 79 ++++++++++++++-----
ui/js/src/kimchi.api.js | 13 +++-
ui/js/src/kimchi.guest_edit_main.js | 146 ++++++++++++++++++++++++++++++------
ui/pages/guest-edit.html.tmpl | 28 ++++++-
4 files changed, 218 insertions(+), 48 deletions(-)
--
1.8.3.2
9 years, 12 months
[PATCH 0/2 V6] UI for LDAP
by Aline Manera
v5 < v6,
Remove scroll bar in Permission Tab for PAM authentication
Royce Lv (2):
UI: support ldap vm permission tag
Change guest edit permission logic
ui/css/theme-default/guest-edit.css | 96 +++++++++++++++++--------
ui/js/src/kimchi.api.js | 23 ++++--
ui/js/src/kimchi.guest_edit_main.js | 140 +++++++++++++++++++++++++++++-------
ui/pages/guest-edit.html.tmpl | 22 +++++-
4 files changed, 218 insertions(+), 63 deletions(-)
--
1.9.3
9 years, 12 months
[PATCH 0/2 V5] UI for LDAP
by Aline Manera
v1 > v2,
Change according to LDAP backend api change.
v2 > v3,
Change alert icon to red border of input box to be more clear,
commented by YuXin
v3 > v4,
Fix alignment and use place holder to inform user what to fill.
commented by Aline.
v4 > v5,
Use cached capabilities values and rename functions
Royce Lv (2):
UI: support ldap vm permission tag
Change guest edit permission logic
ui/css/theme-default/guest-edit.css | 94 ++++++++++++++++--------
ui/js/src/kimchi.api.js | 23 ++++--
ui/js/src/kimchi.guest_edit_main.js | 140 +++++++++++++++++++++++++++++-------
ui/pages/guest-edit.html.tmpl | 22 +++++-
4 files changed, 217 insertions(+), 62 deletions(-)
--
1.9.3
9 years, 12 months
[PATCHv4 0/2] UI for LDAP
by lvroyce0210@gmail.com
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
v1>v2,
Change according to LDAP backend api change.
v2>v3,
Change alert icon to red border of input box to be more clear,
commented by YuXin
v3>v4,
Fix alignment and use place holder to inform user what to fill.
commented by Aline.
Royce Lv (2):
UI: support ldap vm permission tag
Change guest edit permission logic
ui/css/theme-default/guest-edit.css | 83 ++++++++---
ui/js/src/kimchi.api.js | 15 +-
ui/js/src/kimchi.guest_edit_main.js | 289 ++++++++++++++++++++++++------------
ui/pages/guest-edit.html.tmpl | 22 ++-
4 files changed, 284 insertions(+), 125 deletions(-)
--
1.8.3.2
9 years, 12 months
[PATCH] vmtemplate: allow allocation = 0 for type 'raw'
by Daniel Henrique Barboza
Kimchi was allocating the entire disk when the disk image type wasn't
'qcow2'. Adding 'raw' to this exception list because raw images can
be created as sparse (= size in bytes when creating the disk is
less than its total capacity) too.
Signed-off-by: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
---
src/kimchi/vmtemplate.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index 5dbbdd4..6c449f2 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -197,7 +197,8 @@ class VMTemplate(object):
'capacity': d['size'],
'format': fmt,
'path': '%s/%s' % (storage_path, volume)}
- info['allocation'] = 0 if fmt == 'qcow2' else info['capacity']
+ info['allocation'] = 0 if fmt in ['qcow2', 'raw'] \
+ else info['capacity']
if 'base' in d:
info['base'] = dict()
--
1.9.3
10 years
[v5 1/1] Virtual machine migration
by simonjin@linux.vnet.ibm.com
From: Simon Jin <simonjin(a)linux.vnet.ibm.com>
v5-v4:check whether the vm is already in migration.
Signed-off-by: Simon Jin <simonjin(a)linux.vnet.ibm.com>
---
docs/API.md | 5 ++
src/kimchi/control/vms.py | 1 +
src/kimchi/i18n.py | 8 +++
src/kimchi/model/vms.py | 170 +++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 182 insertions(+), 2 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index 9b866f3..eb02e80 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -132,6 +132,11 @@ the following general conventions:
It emulates the power reset button on a machine. Note that there is a
risk of data loss caused by reset without the guest OS shutdown.
* connect: Prepare the connection for spice or vnc
+* migrate: Migrate a virtual machine to a remote server, only support live mode without block migration.
+ * remoteHost: IP address or hostname of the remote server.
+ * user: User of the remote server.
+ * passwd: Passwd of user on remote server.
+ * secure: Migrate in P2P secure tunel mode.(optional)
* clone: Create a new VM identical to this VM. The new VM's name, UUID and
network MAC addresses will be generated automatically. Each existing
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index a1589ef..e6870e0 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -42,6 +42,7 @@ class VM(Resource):
for ident, node in sub_nodes.items():
setattr(self, ident, node(model, self.ident))
self.start = self.generate_action_handler('start')
+ self.migrate = self.generate_action_handler_task('migrate', ['remoteHost', 'user', 'passwd', 'secure'])
self.poweroff = self.generate_action_handler('poweroff')
self.shutdown = self.generate_action_handler('shutdown')
self.reset = self.generate_action_handler('reset')
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index e823f2b..ffd8e98 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -105,6 +105,14 @@ messages = {
"KCHVM0033E": _("Virtual machine '%(name)s' must be stopped before cloning it."),
"KCHVM0034E": _("Insufficient disk space to clone virtual machine '%(name)s'"),
"KCHVM0035E": _("Unable to clone VM '%(name)s'. Details: %(err)s"),
+ "KCHVM0036E": _("Migrate %(name)s to localhost %(remoteHost)s is not allowed."),
+ "KCHVM0037E": _("vm %(name)s is already in migrating, can not perform migrate on it again."),
+ "KCHVM0038E": _("Can't migrate vm %(name)s on Hypervisor arch %(srcarch)s.to a different Hypervisor arch %(destarch)s."),
+ "KCHVM0039E": _("Can't migrate %(name)s from %(srchyp)s to a different Hypervisor type %(desthyp)s."),
+ "KCHVM0040E": _("Can't migrate vm %(name)s, vm has passthrough device %(hostdevs)s."),
+ "KCHVM0041E": _("Can not migrate vm %(name)s when its in %(state)s state."),
+ "KCHVM0042E": _("Failed Migrate vm %(name)s due error: %(err)s"),
+ "KCHVM0043E": _("Failed to destroy vm %(name)s from migration recover. Details: %(err)s"),
"KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),
"KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."),
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index d194049..2f934a4 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -33,9 +33,9 @@ from cherrypy.process.plugins import BackgroundTask
from kimchi import model, vnc
from kimchi.config import READONLY_POOL_TYPE
from kimchi.exception import InvalidOperation, InvalidParameter
-from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.exception import NotFoundError, OperationFailed, KimchiException
from kimchi.model.config import CapabilitiesModel
-from kimchi.model.tasks import TaskModel
+from kimchi.model.tasks import TaskModel, TasksModel
from kimchi.model.templates import TemplateModel
from kimchi.model.utils import get_vm_name
from kimchi.model.utils import get_metadata_node
@@ -265,6 +265,7 @@ class VMModel(object):
self.groups = import_class('kimchi.model.host.GroupsModel')(**kargs)
self.vms = VMsModel(**kargs)
self.task = TaskModel(**kargs)
+ self.tasks = TasksModel(**kargs)
self.storagepool = model.storagepools.StoragePoolModel(**kargs)
self.storagevolume = model.storagevolumes.StorageVolumeModel(**kargs)
self.storagevolumes = model.storagevolumes.StorageVolumesModel(**kargs)
@@ -790,6 +791,171 @@ class VMModel(object):
except libvirt.libvirtError as e:
raise OperationFailed("KCHVM0019E",
{'name': name, 'err': e.get_error_message()})
+
+ def _preCheck(self, name, destConn, remoteHost):
+ """Pre check before migration"""
+
+ kimchi_log.debug('precheck migrate %s' % name)
+ _destConn = destConn.get()
+ if remoteHost in ['localhost', '127.0.0.1']:
+ kimchi_log.debug('vm %s Can not migrate to localhost.' % name)
+ raise OperationFailed("KCHVM0036E", {'name': name,
+ 'remoteHost': remoteHost})
+ #check if the vm already in migration
+ try:
+ alltasks = self.tasks.get_list()
+ for taskid in alltasks:
+ taskobj = self.task.lookup(taskid)
+ if taskobj['target_uri'] == "/vms/%s/migrate" % name \
+ and taskobj['status'] == "running":
+ kimchi_log.debug('vm %s is already in migrating.' % name)
+ raise OperationFailed("KCHVM0037E", {'name': name})
+ except NotFoundError as e:
+ kimchi_log.debug('No migrate on vm %s.' % name)
+
+ srcarch = self.conn.get().getInfo()[0]
+ destarch = _destConn.getInfo()[0]
+ if srcarch != destarch:
+ kimchi_log.debug('vm %s can not migrate to different arch server.' % (name, destarch))
+ raise OperationFailed("KCHVM0038E", {'name': name,
+ 'srcarch': srcarch,
+ 'destarch': destarch})
+ srchyp = self.conn.get().getType()
+ desthyp = _destConn.getType()
+ if srchyp != desthyp:
+ kimchi_log.debug('vm %s can not migrate to a different hypervisor' % name)
+ raise OperationFailed("KCHVM0039E", {'name': name,
+ 'srchyp': srchyp,
+ 'desthyp':desthyp})
+
+ #model.host import DOM_STATE_MAP, vmhostdevs import host,
+ #to void loop import DOM_STATE_MAP, move import VMHostDevsModel here
+ from kimchi.model.vmhostdevs import VMHostDevsModel
+ #check if there is any passthrough devices belong to this vm
+ vmhostdevs = VMHostDevsModel(conn = self.conn)
+ hostdevs = vmhostdevs.get_list(name)
+ if hostdevs:
+ raise OperationFailed("KCHVM0040E", {'name' : name,
+ 'hostdevs': hostdevs})
+ @staticmethod
+ def _getRemoteConn(remoteHost, user, passwd, transport = 'ssh'):
+ """open destination connect to migrate"""
+
+ duri = 'qemu+%s://%s@%s/system' % (transport, user,remoteHost)
+ conn = LibvirtConnection(duri)
+ return conn
+
+ def _do_migrate(self, cb, params):
+ """Asynchronous function which performs the migrate operation.
+
+ Arguments:
+ cb -- A callback function to signal the Task's progress.
+ params -- A dict with the following values:
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ "remoteHost": The migration destination server.
+ "secure": Migrate in P2P secure tunel mode.(optional)
+ """
+
+ name = params['name'].decode('utf-8')
+ destConn = params['destConn']
+ remoteHost = params['remoteHost']
+ secure = params['secure']
+ _destConn = destConn.get()
+
+ cb('starting a migration')
+ kimchi_log.debug('migrate %s start' % name)
+ dom = self.get_vm(name, self.conn)
+ flag = 0
+ if secure:
+ flag |= (libvirt.VIR_MIGRATE_PEER2PEER |
+ libvirt.VIR_MIGRATE_TUNNELLED)
+
+ state = DOM_STATE_MAP[dom.info()[0]]
+ if state in ['running','paused']:
+ flag |= libvirt.VIR_MIGRATE_LIVE
+ elif state == 'shutoff':
+ flag |= (libvirt.VIR_MIGRATE_OFFLINE|
+ libvirt.VIR_MIGRATE_PERSIST_DEST)
+ else:
+ kimchi_log.debug('Can not migrate vm %s when its in %s.' % (name, state))
+ raise OperationFailed("KCHVM0041E", {'name': name,
+ 'state': state})
+ try:
+ dom.migrate(_destConn, flag, None, None, 0)
+ except libvirt.libvirtError as e:
+ kimchi_log.error('migrate %s to %s failed' % (name, remoteHost))
+ self._recover(name, destConn, cb)
+ self._finish(name, False, cb)
+ raise OperationFailed('KCHVM0042E', {'err': e.message,
+ 'name': name})
+ self._finish(name, True, cb)
+
+ def migrate(self, name, remoteHost, user, passwd = None, secure = False):
+ """Migrate a virtual machine to a remote server.
+
+ Arguments:
+ name -- The name of the virtual machine to be migrated.
+ remoteHost -- Remote server that the VM will be migrated to.
+ user -- Login user on remote server,prefer root.
+ passwd -- The password of the user on remote server, if the local server
+ has ssk key exchanged, then this param can be NULL.
+ secure -- Migrate in P2P secure tunel mode.(optional)
+
+ Return:
+ A Task running the clone operation.
+ """
+ name = name.decode('utf-8')
+ remoteHost = remoteHost.decode('utf-8')
+ user = user.decode('utf-8')
+
+ destConn = self._getRemoteConn(remoteHost, user, passwd)
+ self._preCheck(name, destConn, remoteHost)
+
+ params = {'name': name,
+ 'destConn': destConn,
+ 'remoteHost': remoteHost,
+ 'secure': secure}
+ task_id = add_task('/vms/%s/migrate' % name, self._do_migrate,
+ self.objstore, params)
+
+ return self.task.lookup(task_id)
+
+ def _recover(self, name, destconn, cb):
+ """Recover from a failed migration.
+
+ Arguments:
+ "cb": A callback function to signal the Task's progress.
+ "name": Name of the virtual machine to be migrated.
+ "destConn": A LibvirtConnection to destination server.
+ """
+ try:
+ destvm = self.get_vm(name, destconn)
+ except KimchiException as e:
+ if type(e) == NotFoundError:
+ return
+
+ cb('recover from a failed migration',False)
+ kimchi_log.debug('recover from a failed migrate %s' % name)
+ try:
+ destvm.destroy()
+ kimchi_log.error("Failed to destory migrate vm %s on \
+ destination server" % name)
+ except libvirt.libvirtError as e:
+ raise OperationFailed('KCHVM0043E', {'name' : name,
+ 'err': e.message})
+
+ def _finish(self, name, success, cb):
+ """Clean up after a migration finished and set the task finished.
+
+ Arguments:
+ "name": Name of the virtual machine to be migrated.
+ "success": Whether the migration successed or faild and set the tast
+ finish state according to it.
+ "cb": A callback function to signal the Task's progress.
+ """
+ kimchi_log.debug('finished migrate %s' % name)
+ cb('Migrate finished', success)
def poweroff(self, name):
dom = self.get_vm(name, self.conn)
--
1.8.3.1
10 years
[PATCHv2 0/2] UI for LDAP support
by lvroyce@linux.vnet.ibm.com
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
v1>v2,
Change according to backend API change.
Royce Lv (2):
UI: support ldap vm permission tag
Change guest edit permission logic
ui/css/theme-default/guest-edit.css | 79 +++++++---
ui/js/src/kimchi.api.js | 15 +-
ui/js/src/kimchi.guest_edit_main.js | 300 ++++++++++++++++++++++++------------
ui/pages/guest-edit.html.tmpl | 28 +++-
4 files changed, 299 insertions(+), 123 deletions(-)
--
1.8.3.2
10 years
[v3] UI: CPU Topology
by huoyuxin@linux.vnet.ibm.com
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/template-edit.css | 28 ++++++++++++++++-
ui/js/src/kimchi.api.js | 14 ++++++++
ui/js/src/kimchi.template_edit_main.js | 52 ++++++++++++++++++++++++++++++-
ui/pages/template-edit.html.tmpl | 19 +++++++----
4 files changed, 103 insertions(+), 10 deletions(-)
diff --git a/ui/css/theme-default/template-edit.css b/ui/css/theme-default/template-edit.css
index 094e909..7abee7c 100644
--- a/ui/css/theme-default/template-edit.css
+++ b/ui/css/theme-default/template-edit.css
@@ -142,4 +142,30 @@
#edit-template-tabs .hide {
display: none;
-}
\ No newline at end of file
+}
+
+#form-template-processor select,
+#form-template-processor input[type="text"] {
+ margin-left: 10px;
+}
+
+#form-template-processor input[type="checkbox"] {
+ margin-right: 5px;
+}
+
+#form-template-processor .manual {
+ margin-top: 10px;
+ margin-left: -3px;
+}
+
+#form-template-processor .topology {
+ margin: 10px 30px;
+}
+
+#form-template-processor .topology div {
+ margin-bottom: 10px;
+}
+
+#form-template-processor .topology select {
+ width: 80px;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 78c6d66..7a239c0 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1260,5 +1260,19 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getCPUInfo : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'host/cpuinfo',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js
index d4e19c2..1149541 100644
--- a/ui/js/src/kimchi.template_edit_main.js
+++ b/ui/js/src/kimchi.template_edit_main.js
@@ -200,14 +200,51 @@ kimchi.template_edit_main = function() {
});
});
};
+ var initProcessor = function(){
+ var setCPUValue = function(){
+ if(!$('#cores').hasClass("invalid-field")&&$('#cores').val()!=""){
+ $("#cpus").val(parseInt($("#cores").val())*parseInt($("#threads").val()));
+ }else{
+ $("#cpus").val('');
+ }
+ };
+ $("input:text", "#form-template-processor").on('keyup', function(){
+ $(this).toggleClass("invalid-field", !$(this).val().match('^[0-9]*$'));
+ if($(this).prop('id')=='cores') setCPUValue();
+ });
+ $("input:checkbox", "#form-template-processor").click(function(){
+ $(".topology", "#form-template-processor").toggleClass("hide", !$(this).prop("checked"));
+ $("#cpus").attr("disabled", $(this).prop("checked"));
+ setCPUValue();
+ });
+ $('select', '#form-template-processor').change(function(){
+ setCPUValue();
+ });
+ kimchi.getCPUInfo(function(data){
+ var options = "";
+ for(var i=0;Math.pow(2,i)<=data.threads_per_core;i++){
+ var lastOne = Math.pow(2,i+1)>data.threads_per_core?" selected":"";
+ options += "<option"+lastOne+">"+Math.pow(2,i)+"</option>";
+ }
+ $('select', '#form-template-processor').append(options);
+ if(template.cpus) $("#cpus").val(template.cpus);
+ var topo = template.cpu_info.topology;
+ if(topo&&topo.cores) $("#cores").val(topo.cores);
+ if(topo&&topo.threads){
+ $('select', '#form-template-processor').val(topo.threads);
+ $("input:checkbox", "#form-template-processor").trigger('click');
+ }
+ });
+ };
kimchi.listNetworks(initInterface);
kimchi.listStoragePools(initStorage);
+ initProcessor();
};
kimchi.retrieveTemplate(kimchi.selectedTemplate, initTemplate);
$('#tmpl-edit-button-save').on('click', function() {
- var editableFields = [ 'name', 'cpus', 'memory', 'disks', 'graphics'];
+ var editableFields = [ 'name', 'memory', 'disks', 'graphics'];
var data = {};
//Fix me: Only support one storage pool now
var storages = $('.template-tab-body .item', '#form-template-storage');
@@ -241,7 +278,18 @@ kimchi.template_edit_main = function() {
}
});
data['memory'] = Number(data['memory']);
- data['cpus'] = Number(data['cpus']);
+ data['cpus'] = parseInt($('#cpus').val());
+ if($("input:checkbox", "#form-template-processor").prop("checked")){
+ data['cpu_info'] = {
+ topology: {
+ sockets: 1,
+ cores: parseInt($("#cores").val()),
+ threads: parseInt($("#threads").val())
+ }
+ };
+ }else{
+ data['cpu_info'] = {};
+ }
var networks = $('.template-tab-body .item', '#form-template-interface');
var networkForUpdate = new Array();
$.each(networks, function(index, networkEntities) {
diff --git a/ui/pages/template-edit.html.tmpl b/ui/pages/template-edit.html.tmpl
index 90a6256..7177bf0 100644
--- a/ui/pages/template-edit.html.tmpl
+++ b/ui/pages/template-edit.html.tmpl
@@ -40,6 +40,9 @@
<li>
<a href="#form-template-interface">$_("Interface")</a>
</li>
+ <li>
+ <a href="#form-template-processor">$_("Processor")</a>
+ </li>
</ul>
<form id="form-template-general">
<div class="form-template-inline-wrapper">
@@ -53,9 +56,6 @@
<label for="template-edit-version-textbox">$_("Version")</label>
</div>
<div class="template-edit-wrapper-label">
- <label for="template-edit-cpu-textbox">$_("CPU Number")</label>
- </div>
- <div class="template-edit-wrapper-label">
<label for="template-edit-memory-textbox">$_("Memory (MB)")</label>
</div>
<div class="template-edit-wrapper-label templ-edit-cdrom">
@@ -79,9 +79,6 @@
<input id="template-edit-version-textbox" name="os_version" type="text" disabled="disabled" />
</div>
<div class="template-edit-wrapper-controls">
- <input id="template-edit-cpu-textbox" name="cpus" type="text" />
- </div>
- <div class="template-edit-wrapper-controls">
<input id="template-edit-memory-textbox" name="memory" type="text" />
</div>
<div class="template-edit-wrapper-controls templ-edit-cdrom">
@@ -120,6 +117,14 @@
</div>
<div class="template-tab-body"></div>
</form>
+ <form id="form-template-processor">
+ <div>$_("CPU Number"):<input type="text" value="1" id="cpus"></div>
+ <div class="manual"><input type="checkbox">$_("Manually set CPU topology")</div>
+ <div class="topology hide">
+ <div>$_("Cores"):<input type="text" value="1" id="cores"></div>
+ <div>$_("Threads"):<select id="threads"></select></div>
+ </div>
+ </form>
</div>
</div>
<footer>
@@ -157,4 +162,4 @@
<button class="delete"></button>
</span>
</div>
-</script>
\ No newline at end of file
+</script>
--
1.7.1
10 years
[PATCH v8 0/2] Auto Topology for Guest Templates
by Christy Perez
By this point, the subject isn't exactly accurate, but since v 1-6 had it,
I'm not changing it now.
As the commit message says, this feature is no longer an "auto topology"
feature, but instead just a feature to allow users to configure topology
for a vm template should they so desire.
If we ever want to add an auto-configure option, then version 5 of this
patchset can be resurrected. I would recommend re-adding the auto-element
of the cpu_info for a template, instead of passing in threads=0, however.
New to v8:
- A try/except block in cpuinfo __init__ has been removed
- The license file was fixed in /model/cpuinfo.py
Christy Perez (2):
Parts to allow Kimchi to configure the cpu topology.
Mockmodel test for cpuinfo
docs/API.md | 24 ++++++++
src/kimchi/control/cpuinfo.py | 37 +++++++++++++
src/kimchi/control/host.py | 2 +
src/kimchi/i18n.py | 5 ++
src/kimchi/mockmodel.py | 10 ++++
src/kimchi/model/cpuinfo.py | 124 ++++++++++++++++++++++++++++++++++++++++++
src/kimchi/model/templates.py | 10 ++--
7 files changed, 208 insertions(+), 4 deletions(-)
create mode 100644 src/kimchi/control/cpuinfo.py
create mode 100644 src/kimchi/model/cpuinfo.py
--
1.9.3
10 years
[v3] UI: Guest Snapshot
by huoyuxin@linux.vnet.ibm.com
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/guest-edit.css | 35 +++++++++++-
ui/js/src/kimchi.api.js | 68 ++++++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 106 +++++++++++++++++++++++++++++++++++
ui/pages/guest-edit.html.tmpl | 27 +++++++++
4 files changed, 235 insertions(+), 1 deletions(-)
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index b1d3931..74d72f2 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -96,6 +96,7 @@
}
#form-guest-edit-storage .header,
+.guest-edit-snapshot .header,
.guest-edit-interface .header {
margin-bottom: 8px;
padding-bottom: 2px;
@@ -105,6 +106,8 @@
}
#form-guest-edit-storage .body .item,
+.guest-edit-snapshot .body .item,
+.guest-edit-snapshot .task .item,
.guest-edit-interface .body .item {
margin: 5px 0;
}
@@ -115,6 +118,32 @@
width: 250px;
}
+.guest-edit-snapshot .cell {
+ display: inline-block;
+}
+
+.guest-edit-snapshot .sel {
+ width: 35px;
+ vertical-align: top;
+}
+
+.guest-edit-snapshot .icon {
+ background: url('../../images/theme-default/kimchi-loading15x15.gif') no-repeat;
+ display: block;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+ margin-left: 2px;
+}
+
+.guest-edit-snapshot .name {
+ width: 400px;
+}
+
+.guest-edit-snapshot .created {
+ width: 250px;
+}
+
#form-guest-edit-storage .cell.dev {
width: 60px;
}
@@ -135,6 +164,7 @@
}
#form-guest-edit-storage .action-area,
+.guest-edit-snapshot .action-area,
.guest-edit-interface .action-area {
float: right;
}
@@ -144,12 +174,14 @@
}
#form-guest-edit-storage button,
+.guest-edit-snapshot button,
.guest-edit-interface button {
width: 20px;
height: 20px;
}
#form-guest-edit-storage .header button,
+.guest-edit-snapshot .header button,
.guest-edit-interface .header button {
margin-bottom: 1px;
}
@@ -159,8 +191,9 @@
margin-right: 2px;
}
+.guest-edit-snapshot .hide,
.guest-edit-interface .hide {
- display: none;
+ display: none!important;
}
.guest-edit-permission {
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 78c6d66..8e21d4c 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1260,5 +1260,73 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ listSnapshots : function(vm, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ getCurrentSnapshot : function(vm, suc, err, sync) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/current',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ async : !sync,
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ revertSnapshot : function(vm, snapshot, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/'+encodeURIComponent(snapshot)+'/revert',
+ type : 'POST',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ createSnapshot : function(vm, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots',
+ type : 'POST',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ deleteSnapshot : function(vm, snapshot, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/'+encodeURIComponent(snapshot),
+ type : 'DELETE',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 9d87a73..e033743 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -461,6 +461,111 @@ kimchi.guest_edit_main = function() {
});
};
+ var setupSnapshot = function() {
+ var currentSnapshot;
+ var setCurrentSnapshot = function(aSnapshot){
+ if(!aSnapshot)
+ kimchi.getCurrentSnapshot(kimchi.selectedGuest, function(snapshot){
+ if(snapshot&&snapshot.name) aSnapshot = snapshot.name;
+ }, null, true);
+ if(aSnapshot){
+ if(currentSnapshot) $(".ui-icon-check", "#"+currentSnapshot).addClass("hide");
+ $(".ui-icon-check", "#"+aSnapshot).removeClass("hide");
+ currentSnapshot = aSnapshot;
+ }
+ };
+ var addItem = function(data, container) {
+ var itemNode = $.parseHTML(kimchi.substitute($('#snapshot-tmpl').html(),data));
+ $("."+container, "#form-guest-edit-snapshot").append(itemNode);
+ $(".delete", itemNode).button({
+ icons: { primary: "ui-icon-trash" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ var item = $(this).parent().parent();
+ $("button", "#form-guest-edit-snapshot").button("disable");
+ kimchi.deleteSnapshot(kimchi.selectedGuest, item.prop("id"), function(){
+ item.remove();
+ setCurrentSnapshot();
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ }, function(data){
+ kimchi.message.error(data.responseJSON.reason);
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ });
+ });
+ $(".revert", itemNode).button({
+ icons: { primary: "ui-icon-arrowthick-1-ne" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ var item = $(this).parent().parent();
+ $(".ui-icon-check", item).addClass("hide");
+ $(".icon", item).removeClass("hide");
+ $("button", "#form-guest-edit-snapshot").button("disable");
+ kimchi.revertSnapshot(kimchi.selectedGuest, item.prop("id"), function(){
+ $(".icon", item).addClass("hide");
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ setCurrentSnapshot(item.prop("id"));
+ }, function(data){
+ kimchi.message.error(data.responseJSON.reason);
+ $(".icon", item).addClass("hide");
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ });
+ });
+ };
+ var addOngoingItem = function(task){
+ var uri = task.target_uri;
+ addItem({
+ name: uri.substring(uri.lastIndexOf('/')+1, uri.length),
+ created: "",
+ listMode: "hide",
+ createMode: ""
+ }, 'task');
+ if(kimchi.trackingTasks.indexOf(task.id)==-1)
+ kimchi.trackTask(task.id, function(task){
+ listGeneratingSnapshots();
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ }, function(err){
+ kimchi.message.error(err.message);
+ listGeneratingSnapshots();
+ $("button", "#form-guest-edit-snapshot").button("enable");
+ });
+ };
+ var listGeneratingSnapshots = function(){
+ kimchi.getTasksByFilter('status=running&target_uri='+encodeURIComponent('^/snapshots/*'), function(tasks) {
+ $(".task", "#form-guest-edit-snapshot").empty();
+ for(var i=0;i<tasks.length;i++){
+ addOngoingItem(tasks[i]);
+ }
+ if(tasks.length==0) listSnapshots();
+ });
+ };
+ var listSnapshots = function(){
+ kimchi.listSnapshots(kimchi.selectedGuest, function(data){
+ $(".body", "#form-guest-edit-snapshot").empty();
+ for(var i=0;i<data.length;i++){
+ data[i].created = new Date(data[i].created*1000).toLocaleString();
+ data[i].createMode = "hide";
+ data[i].listMode = "";
+ addItem(data[i], 'body');
+ }
+ setCurrentSnapshot();
+ });
+ };
+ listGeneratingSnapshots();
+ $(".add", "#form-guest-edit-snapshot").button({
+ icons: { primary: "ui-icon-plusthick" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ kimchi.createSnapshot(kimchi.selectedGuest, function(task){
+ $("button", "#form-guest-edit-snapshot").button("disable");
+ addOngoingItem(task);
+ });
+ });
+ if(kimchi.thisVMState=="running") $("button", "#form-guest-edit-snapshot").remove();
+ };
+
var initContent = function(guest) {
guest['icon'] = guest['icon'] || 'images/icon-vm.png';
$('#form-guest-edit-general').fillWithObject(guest);
@@ -496,6 +601,7 @@ kimchi.guest_edit_main = function() {
setupInterface();
setupPermission();
setupPCIDevice();
+ setupSnapshot();
kimchi.topic('kimchi/vmCDROMAttached').subscribe(onAttached);
kimchi.topic('kimchi/vmCDROMReplaced').subscribe(onReplaced);
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 512909a..6233884 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -44,6 +44,9 @@
<li>
<a href="#form-guest-edit-pci">$_("Host PCI Device")</a>
</li>
+ <li>
+ <a href="#form-guest-edit-snapshot">$_("Snapshot")</a>
+ </li>
</ul>
<form id="form-guest-edit-general">
<fieldset class="guest-edit-fieldset">
@@ -160,6 +163,16 @@
<div class="body"></div>
</div>
</form>
+ <form id="form-guest-edit-snapshot" class="guest-edit-snapshot">
+ <div class="header">
+ <span class="cell sel"></span>
+ <span class="cell name">$_("Name")</span>
+ <span class="cell created">$_("Created")</span>
+ <button class="add action-area"></button>
+ </div>
+ <div class="task"></div>
+ <div class="body"></div>
+ </form>
</div>
</div>
<footer>
@@ -251,6 +264,20 @@
<button></button>
</div>
</script>
+<script id="snapshot-tmpl" type="text/html">
+ <div class="item" id="{name}">
+ <span class="cell sel">
+ <span class="icon {createMode}"></span>
+ <span class="ui-icon ui-icon-check hide"></span>
+ </span>
+ <span class="cell name">{name}</span>
+ <span class="cell created">{created}</span>
+ <span class="action-area">
+ <button class="revert {listMode}" title="$_("revert")"></button>
+ <button class="delete {listMode}"></button>
+ </span>
+ <div>
+</script>
<script type="text/javascript">
kimchi.guest_edit_main();
</script>
--
1.7.1
10 years