[PATCH V2] Create new storage volume when attaching disk to a guest.
by pvital@linux.vnet.ibm.com
From: Paulo Vital <pvital(a)linux.vnet.ibm.com>
Changes in V2:
* Updated test cases
* Modified dictionary return of parameters
V1:
Add back-end support to create new storage volume (new virtual disk) when
attaching disk to a guest created before.
There are three essential parameters to create the new volume:
* vol: Storage volume name of disk image, that should be 'new_vol'.
* capacity: The total space which can be used to store new volumes.
The unit is bytes.
* format: The format of the defined Storage Volume. Only used when creating
a storage volume with 'capacity'.
To test this patch:
$ curl -k -u test -H "Content-Type: application/json" -H "Accept: application/json" 'https://localhost:8001/plugins/kimchi/vms/kimchi-vm-new/storages' -X POST -d '{ "vol": "new_vol", "type": "disk", "pool": "default", "format": "qcow2", "capacity": 1024 }'
Enter host password for user 'test':
{
"bus":"virtio",
"path":"/var/lib/libvirt/images/2477bfd8-a9e2-4887-a683-e89015b3ba11
-1.img",
"type":"disk",
"dev":"vdb",
"format":"qcow2"
}
$ curl -k -u test -H "Content-Type: application/json" -H "Accept: application/json" 'https://localhost:8001/plugins/kimchi/vms/kimchi-vm-new/storages' -X GET
Enter host password for user 'test':
[
{
"bus":"virtio",
"path":"/var/lib/libvirt/images/2477bfd8-a9e2-4887-a683
-e89015b3ba11-0.img",
"type":"disk",
"dev":"vda",
"format":"qcow2"
},
{
"bus":"virtio",
"path":"/var/lib/libvirt/images/2477bfd8-a9e2-4887-a683
-e89015b3ba11-1.img",
"type":"disk",
"dev":"vdb",
"format":"qcow2"
},
{
"bus":"ide",
"path":"/var/lib/kimchi/tests/ubuntu14.04.iso",
"type":"cdrom",
"dev":"hdc",
"format":"raw"
}
]
Paulo Vital (1):
Create new storage volume when attaching disk to a guest.
src/wok/plugins/kimchi/API.json | 12 ++++++++++++
src/wok/plugins/kimchi/docs/API.md | 6 +++++-
src/wok/plugins/kimchi/i18n.py | 1 +
src/wok/plugins/kimchi/model/vmstorages.py | 28 +++++++++++++++++++++++++++-
src/wok/plugins/kimchi/tests/test_model.py | 25 +++++++++++++++++++++++++
5 files changed, 70 insertions(+), 2 deletions(-)
--
2.4.3
8 years, 11 months
Re: [Kimchi-devel] Guest disk: Allocate storage volume when attaching disk to vm
by Aline Manera
Adding Samuel and Andre to this discussion and also forward to Kimchi ML.
On 16/11/2015 14:21, Aline Manera wrote:
>
> Hi Socorro,
>
> On 12/11/2015 21:20, Socorro Stoppler wrote:
>> Hi Aline,
>>
>> I chatted w/Paulo about the UI portion of this. He stated that the
>> user has the option to select to add a new volume (in which the the
>> UI must execute two requests: one to create a volume, and other to
>> attach the new volume to the guest being edited). So in this screen
>> (I know it's the old UI but this shows better than current new UI :)):
>>
>>
>>
>> What do you think of 3. being radio buttons? -
>
> I think 2 should be that radio buttons.
>
> So:
>
> 1. Device type
> <disk>
>
> 2. ( ) Create a new disk image
>
> ( ) Select existing disk image
>
> 3. Storage pool
> <pools>
>
> 4. Capacity or Volume (depends on selection on 2.)
>
> Below is how virt-manage does it.
>
>
>
>> <New Volume> -
>> Volume: Storage volume name of 'new_vol' --- we prob do not
>> need to show this in the UI, right?
>> Capacity: in bytes
>> Format: qcow2 -- is this the only option we want to provide?
>> <Create Button>
>>
>> <Existing Volume> - which consists of current combobox above
>>
>> Also, if user selects to do the new volume, then created it, but
>> decided that they didn't want to attach it, is that ok? Not sure of
>> the different scenarios we have
>> or if I'm totally missing the intent here.
>>
>> Let me know if you had envisioned something else for this UI.
>>
>> Thanks
>> -Socorro
>>
>>
>
8 years, 11 months
[PATCH 1/1] Library files for bootgrid plugin
by atreyee@linux.vnet.ibm.com
From: Atreyee Mukhopadhyay <atreyee(a)linux.vnet.ibm.com>
Bootgrid is grid implemention for bootstrap and jquery with sorting,
multiselect,searching featuers.
---
configure.ac | 4 ++++
ui/libs/Makefile.am | 2 +-
ui/libs/jquery-bootgrid/LICENSE | 17 +++++++++++++++++
ui/libs/jquery-bootgrid/Makefile.am | 20 ++++++++++++++++++++
ui/libs/jquery-bootgrid/dist/Makefile.am | 20 ++++++++++++++++++++
ui/libs/jquery-bootgrid/dist/css/Makefile.am | 22 ++++++++++++++++++++++
.../dist/css/jquery.bootgrid.min.css | 5 +++++
ui/libs/jquery-bootgrid/dist/js/Makefile.am | 22 ++++++++++++++++++++++
.../jquery-bootgrid/dist/js/jquery.bootgrid.min.js | 6 ++++++
ui/pages/wok-ui.html.tmpl | 2 ++
10 files changed, 119 insertions(+), 1 deletion(-)
create mode 100644 ui/libs/jquery-bootgrid/LICENSE
create mode 100644 ui/libs/jquery-bootgrid/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/css/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css
create mode 100644 ui/libs/jquery-bootgrid/dist/js/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js
diff --git a/configure.ac b/configure.ac
index dd3955e..f88fe04 100644
--- a/configure.ac
+++ b/configure.ac
@@ -125,6 +125,10 @@ AC_CONFIG_FILES([
ui/libs/bootstrap-select/dist/Makefile
ui/libs/bootstrap-select/dist/js/Makefile
ui/libs/bootstrap-select/dist/css/Makefile
+ ui/libs/jquery-bootgrid/Makefile
+ ui/libs/jquery-bootgrid/dist/Makefile
+ ui/libs/jquery-bootgrid/dist/js/Makefile
+ ui/libs/jquery-bootgrid/dist/css/Makefile
ui/libs/es5-shim/Makefile
ui/libs/jquery/Makefile
ui/libs/jquery-i18n/Makefile
diff --git a/ui/libs/Makefile.am b/ui/libs/Makefile.am
index d746df4..085e1f0 100644
--- a/ui/libs/Makefile.am
+++ b/ui/libs/Makefile.am
@@ -17,4 +17,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SUBDIRS = bootstrap jquery jquery-ui bootstrap-select es5-shim jquery-i18n
+SUBDIRS = bootstrap jquery jquery-ui bootstrap-select jquery-bootgrid es5-shim jquery-i18n
diff --git a/ui/libs/jquery-bootgrid/LICENSE b/ui/libs/jquery-bootgrid/LICENSE
new file mode 100644
index 0000000..0e3a7e1
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/LICENSE
@@ -0,0 +1,17 @@
+The MIT License (MIT)
+Copyright (c) 2014-2015 Rafael J. Staib
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/ui/libs/jquery-bootgrid/Makefile.am b/ui/libs/jquery-bootgrid/Makefile.am
new file mode 100644
index 0000000..858b32a
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Project Wok
+#
+# Copyright IBM, Corp. 2015
+#
+# Code derived from Project Kimchi
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SUBDIRS = dist
diff --git a/ui/libs/jquery-bootgrid/dist/Makefile.am b/ui/libs/jquery-bootgrid/dist/Makefile.am
new file mode 100644
index 0000000..bb579e0
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/dist/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Project Wok
+#
+# Copyright IBM, Corp. 2015
+#
+# Code derived from Project Kimchi
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SUBDIRS = css js
diff --git a/ui/libs/jquery-bootgrid/dist/css/Makefile.am b/ui/libs/jquery-bootgrid/dist/css/Makefile.am
new file mode 100644
index 0000000..d5ed1c2
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/dist/css/Makefile.am
@@ -0,0 +1,22 @@
+#
+# Project Wok
+#
+# Copyright IBM, Corp. 2015
+#
+# Code derived from Project Kimchi
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jquerybootgridcssdir = $(datadir)/wok/ui/libs/jquery-bootgrid/dist/css
+
+dist_jquerybootgridcss_DATA = $(wildcard *.css) $(NULL)
diff --git a/ui/libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css b/ui/libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css
new file mode 100644
index 0000000..4782a15
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css
@@ -0,0 +1,5 @@
+/*!
+ * jQuery Bootgrid v1.3.1 - 09/11/2015
+ * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com)
+ * Licensed under MIT http://www.opensource.org/licenses/MIT
+ */.bootgrid-footer,.bootgrid-header{margin:15px 0}.bootgrid-footer a,.bootgrid-header a{outline:0}.bootgrid-footer .search,.bootgrid-header .search{display:inline-block;margin:0 20px 0 0;vertical-align:middle;width:180px}.bootgrid-footer .search .glyphicon,.bootgrid-header .search .glyphicon{top:0}.bootgrid-footer .search .fa,.bootgrid-header .search .fa{display:table-cell}.bootgrid-footer .search .search-field::-ms-clear,.bootgrid-footer .search.search-field::-ms-clear,.bootgrid-header .search .search-field::-ms-clear,.bootgrid-header .search.search-field::-ms-clear{display:none}.bootgrid-footer .pagination,.bootgrid-header .pagination{margin:0!important}.bootgrid-footer .infoBar,.bootgrid-header .actionBar{text-align:right}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu{text-align:left}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item,.bootgrid-header .actionBar .b!
tn-group>.btn-group .dropdown-menu .dropdown-item{cursor:pointer;display:block;margin:0;padding:3px 20px;white-space:nowrap}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item:focus,.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item:hover,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item:focus,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox,.bootgrid-footer .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item .dropdown-item-checkbox,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item.dropdown-item-checkbox{margin:0 2px 4px 0;vertical-align:middle}.bootgrid-foo!
ter .infoBar .btn-group>.btn-group .dropdown-menu .dropdown-item.disabled,.bootgrid-header .actionBar .btn-group>.btn-group .dropdown-menu .dropdown-item.disabled{cursor:not-allowed}.bootgrid-table{table-layout:fixed}.bootgrid-table a{outline:0}.bootgrid-table th>.column-header-anchor{color:#333;cursor:not-allowed;display:block;position:relative;text-decoration:none}.bootgrid-table th>.column-header-anchor.sortable{cursor:pointer}.bootgrid-table th>.column-header-anchor>.text{display:block;margin:0 16px 0 0;overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}.bootgrid-table th>.column-header-anchor>.icon{display:block;position:absolute;right:0;top:2px}.bootgrid-table th:active,.bootgrid-table th:hover{background:#fafafa}.bootgrid-table td{overflow:hidden;-ms-text-overflow:ellipsis;-o-text-overflow:ellipsis;text-overflow:ellipsis;white-space:nowrap}.bootgrid-table td.loading,.bootgrid-table td.no-results{background:#!
fff;text-align:center}.bootgrid-table td.select-cell,.bootgrid-table th!
.select-cell{text-align:center;width:30px}.bootgrid-table td.select-cell .select-box,.bootgrid-table th.select-cell .select-box{margin:0;outline:0}.table-responsive .bootgrid-table{table-layout:inherit!important}.table-responsive .bootgrid-table td,.table-responsive .bootgrid-table th>.column-header-anchor>.text{overflow:inherit!important;-ms-text-overflow:inherit!important;-o-text-overflow:inherit!important;text-overflow:inherit!important;white-space:inherit!important}
\ No newline at end of file
diff --git a/ui/libs/jquery-bootgrid/dist/js/Makefile.am b/ui/libs/jquery-bootgrid/dist/js/Makefile.am
new file mode 100644
index 0000000..86ad534
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/dist/js/Makefile.am
@@ -0,0 +1,22 @@
+#
+# Project Wok
+#
+# Copyright IBM, Corp. 2015
+#
+# Code derived from Project Kimchi
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+jquerybootgridjsdir = $(datadir)/wok/ui/libs/jquery-bootgrid/dist/js
+
+dist_jquerybootgridjs_DATA = $(wildcard *.js) $(NULL)
diff --git a/ui/libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js b/ui/libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js
new file mode 100644
index 0000000..9c14ff3
--- /dev/null
+++ b/ui/libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js
@@ -0,0 +1,6 @@
+/*!
+ * jQuery Bootgrid v1.3.1 - 09/11/2015
+ * Copyright (c) 2014-2015 Rafael Staib (http://www.jquery-bootgrid.com)
+ * Licensed under MIT http://www.opensource.org/licenses/MIT
+ */
+!function(a,b,c){"use strict";function d(a){function b(b){return c.identifier&&b[c.identifier]===a[c.identifier]}var c=this;return this.rows.contains(b)?!1:(this.rows.push(a),!0)}function e(b){var c=this.footer?this.footer.find(b):a(),d=this.header?this.header.find(b):a();return a.merge(c,d)}function f(b){return b?a.extend({},this.cachedParams,{ctx:b}):this.cachedParams}function g(){var b={current:this.current,rowCount:this.rowCount,sort:this.sortDictionary,searchPhrase:this.searchPhrase},c=this.options.post;return c=a.isFunction(c)?c():c,this.options.requestHandler(a.extend(!0,b,c))}function h(b){return"."+a.trim(b).replace(/\s+/gm,".")}function i(){var b=this.options.url;return a.isFunction(b)?b():b}function j(){this.element.trigger("initialize"+H),m.call(this),this.selection=this.options.selection&&null!=this.identifier,o.call(this),q.call(this),C.call(this),A.call(this),r.call(this),n.call(this),this.element.trigger("initialized"+H)}function k(a){this.options.highlightR!
ows}function l(a){return a.visible}function m(){var b=this,c=this.element.find("thead > tr").first(),d=!1;c.children().each(function(){var c=a(this),e=c.data(),f={id:e.columnId,identifier:null==b.identifier&&e.identifier||!1,converter:b.options.converters[e.converter||e.type]||b.options.converters.string,text:c.text(),align:e.align||"left",headerAlign:e.headerAlign||"left",cssClass:e.cssClass||"",headerCssClass:e.headerCssClass||"",formatter:b.options.formatters[e.formatter]||null,order:d||"asc"!==e.order&&"desc"!==e.order?null:e.order,searchable:!(e.searchable===!1),sortable:!(e.sortable===!1),visible:!(e.visible===!1),visibleInSelection:!(e.visibleInSelection===!1),width:a.isNumeric(e.width)?e.width+"px":"string"==typeof e.width?e.width:null};b.columns.push(f),null!=f.order&&(b.sortDictionary[f.id]=f.order),f.identifier&&(b.identifier=f.id,b.converter=f.converter),b.options.multiSort||null===f.order||(d=!0)})}function n(){function c(a){for(var b,c=new RegExp(e.searchPhras!
e,e.options.caseSensitive?"g":"gi"),d=0;d<e.columns.length;d++)if(b=e.columns[d],b.searchable&&b.visible&&b.converter.to(a[b.id]).search(c)>-1)return!0;return!1}function d(a,b){e.currentRows=a,p.call(e,b),e.options.keepSelection||(e.selectedRows=[]),y.call(e,a),t.call(e),v.call(e),e.element._bgBusyAria(!1).trigger("loaded"+H)}var e=this;if(this.element._bgBusyAria(!0).trigger("load"+H),F.call(this),this.options.ajax){var f=g.call(this),h=i.call(this);if(null==h||"string"!=typeof h||0===h.length)throw new Error("Url setting must be a none empty string or a function that returns one.");this.xqr&&this.xqr.abort();var j={url:h,data:f,success:function(b){e.xqr=null,"string"==typeof b&&(b=a.parseJSON(b)),b=e.options.responseHandler(b),e.current=b.current,d(b.rows,b.total)},error:function(a,b,c){e.xqr=null,"abort"!==b&&(u.call(e),e.element._bgBusyAria(!1).trigger("loaded"+H))}};j=a.extend(this.options.ajaxSettings,j),this.xqr=a.ajax(j)}else{var k=this.searchPhrase.length>0?this.ro!
ws.where(c):this.rows,l=k.length;-1!==this.rowCount&&(k=k.page(this.cur!
rent,this.rowCount)),b.setTimeout(function(){d(k,l)},10)}}function o(){if(!this.options.ajax){var b=this,c=this.element.find("tbody > tr");c.each(function(){var c=a(this),e=c.children("td"),f={};a.each(b.columns,function(a,b){f[b.id]=b.converter.from(e.eq(a).text())}),d.call(b,f)}),p.call(this,this.rows.length),G.call(this)}}function p(a){this.total=a,this.totalPages=-1===this.rowCount?1:Math.ceil(this.total/this.rowCount)}function q(){var b=this.options.templates,c=this.element.parent().hasClass(this.options.css.responsiveTable)?this.element.parent():this.element;this.element.addClass(this.options.css.table),0===this.element.children("tbody").length&&this.element.append(b.body),1&this.options.navigation&&(this.header=a(b.header.resolve(f.call(this,{id:this.element._bgId()+"-header"}))),c.before(this.header)),2&this.options.navigation&&(this.footer=a(b.footer.resolve(f.call(this,{id:this.element._bgId()+"-footer"}))),c.after(this.footer))}function r(){if(0!==this.options.na!
vigation){var b=this.options.css,c=h(b.actions),d=e.call(this,c);if(d.length>0){var g=this,i=this.options.templates,j=a(i.actions.resolve(f.call(this)));if(this.options.ajax){var k=i.icon.resolve(f.call(this,{iconCss:b.iconRefresh})),l=a(i.actionButton.resolve(f.call(this,{content:k,text:this.options.labels.refresh}))).on("click"+H,function(a){a.stopPropagation(),g.current=1,n.call(g)});j.append(l)}x.call(this,j),s.call(this,j),E.call(this,d,j)}}}function s(b){if(this.options.columnSelection&&this.columns.length>1){var c=this,d=this.options.css,e=this.options.templates,g=e.icon.resolve(f.call(this,{iconCss:d.iconColumns})),i=a(e.actionDropDown.resolve(f.call(this,{content:g}))),j=h(d.dropDownItem),k=h(d.dropDownItemCheckbox),m=h(d.dropDownMenuItems);a.each(this.columns,function(b,g){if(g.visibleInSelection){var o=a(e.actionDropDownCheckboxItem.resolve(f.call(c,{name:g.id,label:g.text,checked:g.visible}))).on("click"+H,j,function(b){b.stopPropagation();var d=a(this),e=d.find!
(k);if(!e.prop("disabled")){g.visible=e.prop("checked");var f=c.columns!
.where(l).length>1;d.parents(m).find(j+":has("+k+":checked)")._bgEnableAria(f).find(k)._bgEnableField(f),c.element.find("tbody").empty(),C.call(c),n.call(c)}});i.find(h(d.dropDownMenuItems)).append(o)}}),b.append(i)}}function t(){if(0!==this.options.navigation){var b=h(this.options.css.infos),c=e.call(this,b);if(c.length>0){var d=this.current*this.rowCount,g=a(this.options.templates.infos.resolve(f.call(this,{end:0===this.total||-1===d||d>this.total?this.total:d,start:0===this.total?0:d-this.rowCount+1,total:this.total})));E.call(this,c,g)}}}function u(){var a=this.element.children("tbody").first(),b=this.options.templates,c=this.columns.where(l).length;this.selection&&(c+=1),a.html(b.noResults.resolve(f.call(this,{columns:c})))}function v(){if(0!==this.options.navigation){var b=h(this.options.css.pagination),c=e.call(this,b)._bgShowAria(-1!==this.rowCount);if(-1!==this.rowCount&&c.length>0){var d=this.options.templates,g=this.current,i=this.totalPages,j=a(d.pagination.reso!
lve(f.call(this))),k=i-g,l=-1*(this.options.padding-g),m=k>=this.options.padding?Math.max(l,1):Math.max(l-this.options.padding+k,1),n=2*this.options.padding+1,o=i>=n?n:i;w.call(this,j,"first","«","first")._bgEnableAria(g>1),w.call(this,j,"prev","<","prev")._bgEnableAria(g>1);for(var p=0;o>p;p++){var q=p+m;w.call(this,j,q,q,"page-"+q)._bgEnableAria()._bgSelectAria(q===g)}0===o&&w.call(this,j,1,1,"page-1")._bgEnableAria(!1)._bgSelectAria(),w.call(this,j,"next",">","next")._bgEnableAria(i>g),w.call(this,j,"last","»","last")._bgEnableAria(i>g),E.call(this,c,j)}}}function w(b,c,d,e){var g=this,i=this.options.templates,j=this.options.css,k=f.call(this,{css:e,text:d,page:c}),l=a(i.paginationItem.resolve(k)).on("click"+H,h(j.paginationButton),function(b){b.stopPropagation(),b.preventDefault();var c=a(this),d=c.parent();if(!d.hasClass("active")&&!d.hasClass("disabled")){var e={first:1,prev:g.current-1,next:g.current+1,last:g.totalPages},f=c.data("page");g.current=e!
[f]||f,n.call(g)}c.trigger("blur")});return b.append(l),l}function x(b)!
{function c(a){return-1===a?d.options.labels.all:a}var d=this,e=this.options.rowCount;if(a.isArray(e)){var g=this.options.css,i=this.options.templates,j=a(i.actionDropDown.resolve(f.call(this,{content:c(this.rowCount)}))),k=h(g.dropDownMenu),l=h(g.dropDownMenuText),m=h(g.dropDownMenuItems),o=h(g.dropDownItemButton);a.each(e,function(b,e){var g=a(i.actionDropDownItem.resolve(f.call(d,{text:c(e),action:e})))._bgSelectAria(e===d.rowCount).on("click"+H,o,function(b){b.preventDefault();var e=a(this),f=e.data("action");f!==d.rowCount&&(d.current=1,d.rowCount=f,e.parents(m).children().each(function(){var b=a(this),c=b.find(o).data("action");b._bgSelectAria(c===f)}),e.parents(k).find(l).text(c(f)),n.call(d))});j.find(m).append(g)}),b.append(j)}}function y(b){if(b.length>0){var c=this,d=this.options.css,e=this.options.templates,g=this.element.children("tbody").first(),i=!0,j="";a.each(b,function(b,g){var h="",k=' data-row-id="'+(null==c.identifier?b:g[c.identifier])+'"',l="";if(c.se!
lection){var m=-1!==a.inArray(g[c.identifier],c.selectedRows),n=e.select.resolve(f.call(c,{type:"checkbox",value:g[c.identifier],checked:m}));h+=e.cell.resolve(f.call(c,{content:n,css:d.selectCell})),i=i&&m,m&&(l+=d.selected,k+=' aria-selected="true"')}var o=null!=g.status&&c.options.statusMapping[g.status];o&&(l+=o),a.each(c.columns,function(b,i){if(i.visible){var j=a.isFunction(i.formatter)?i.formatter.call(c,i,g):i.converter.to(g[i.id]),k=i.cssClass.length>0?" "+i.cssClass:"";h+=e.cell.resolve(f.call(c,{content:null==j||""===j?" ":j,css:("right"===i.align?d.right:"center"===i.align?d.center:d.left)+k,style:null==i.width?"":"width:"+i.width+";"}))}}),l.length>0&&(k+=' class="'+l+'"'),j+=e.row.resolve(f.call(c,{attr:k,cells:h}))}),c.element.find("thead "+h(c.options.css.selectBox)).prop("checked",i),g.html(j),z.call(this,g)}else u.call(this)}function z(b){var c=this,d=h(this.options.css.selectBox);this.selection&&b.off("click"+H,d).on("click"+H,d,function(b){b.stopPro!
pagation();var d=a(this),e=c.converter.from(d.val());d.prop("checked")?!
c.select([e]):c.deselect([e])}),b.off("click"+H,"> tr").on("click"+H,"> tr",function(b){b.stopPropagation();var d=a(this),e=null==c.identifier?d.data("row-id"):c.converter.from(d.data("row-id")+""),f=null==c.identifier?c.currentRows[e]:c.currentRows.first(function(a){return a[c.identifier]===e});c.selection&&c.options.rowSelect&&(d.hasClass(c.options.css.selected)?c.deselect([e]):c.select([e])),c.element.trigger("click"+H,[c.columns,f])})}function A(){if(0!==this.options.navigation){var c=this.options.css,d=h(c.search),g=e.call(this,d);if(g.length>0){var i=this,j=this.options.templates,k=null,l="",m=h(c.searchField),n=a(j.search.resolve(f.call(this))),o=n.is(m)?n:n.find(m);o.on("keyup"+H,function(c){c.stopPropagation();var d=a(this).val();(l!==d||13===c.which&&""!==d)&&(l=d,(13===c.which||0===d.length||d.length>=i.options.searchSettings.characters)&&(b.clearTimeout(k),k=b.setTimeout(function(){B.call(i,d)},i.options.searchSettings.delay)))}),E.call(this,g,n)}}}function B(a)!
{this.searchPhrase!==a&&(this.current=1,this.searchPhrase=a,n.call(this))}function C(){var b=this,c=this.element.find("thead > tr"),d=this.options.css,e=this.options.templates,g="",i=this.options.sorting;if(this.selection){var j=this.options.multiSelect?e.select.resolve(f.call(b,{type:"checkbox",value:"all"})):"";g+=e.rawHeaderCell.resolve(f.call(b,{content:j,css:d.selectCell}))}if(a.each(this.columns,function(a,c){if(c.visible){var h=b.sortDictionary[c.id],j=i&&h&&"asc"===h?d.iconUp:i&&h&&"desc"===h?d.iconDown:"",k=e.icon.resolve(f.call(b,{iconCss:j})),l=c.headerAlign,m=c.headerCssClass.length>0?" "+c.headerCssClass:"";g+=e.headerCell.resolve(f.call(b,{column:c,icon:k,sortable:i&&c.sortable&&d.sortable||"",css:("right"===l?d.right:"center"===l?d.center:d.left)+m,style:null==c.width?"":"width:"+c.width+";"}))}}),c.html(g),i){var k=h(d.sortable);c.off("click"+H,k).on("click"+H,k,function(c){c.preventDefault(),D.call(b,a(this)),G.call(b),n.call(b)})}if(this.selection&&this.op!
tions.multiSelect){var l=h(d.selectBox);c.off("click"+H,l).on("click"+H!
,l,function(c){c.stopPropagation(),a(this).prop("checked")?b.select():b.deselect()})}}function D(a){var b=this.options.css,c=h(b.icon),d=a.data("column-id")||a.parents("th").first().data("column-id"),e=this.sortDictionary[d],f=a.find(c);if(this.options.multiSort||(a.parents("tr").first().find(c).removeClass(b.iconDown+" "+b.iconUp),this.sortDictionary={}),e&&"asc"===e)this.sortDictionary[d]="desc",f.removeClass(b.iconUp).addClass(b.iconDown);else if(e&&"desc"===e)if(this.options.multiSort){var g={};for(var i in this.sortDictionary)i!==d&&(g[i]=this.sortDictionary[i]);this.sortDictionary=g,f.removeClass(b.iconDown)}else this.sortDictionary[d]="asc",f.removeClass(b.iconDown).addClass(b.iconUp);else this.sortDictionary[d]="asc",f.addClass(b.iconUp)}function E(b,c){b.each(function(b,d){a(d).before(c.clone(!0)).remove()})}function F(){var a=this;b.setTimeout(function(){if("true"===a.element._bgAria("busy")){var b=a.options.templates,c=a.element.children("thead").first(),d=a.elem!
ent.children("tbody").first(),e=d.find("tr > td").first(),g=a.element.height()-c.height()-(e.height()+20),h=a.columns.where(l).length;a.selection&&(h+=1),d.html(b.loading.resolve(f.call(a,{columns:h}))),-1!==a.rowCount&&g>0&&d.find("tr > td").css("padding","20px 0 "+g+"px")}},250)}function G(){function a(c,d,e){function f(a){return"asc"===h.order?a:-1*a}e=e||0;var g=e+1,h=b[e];return c[h.id]>d[h.id]?f(1):c[h.id]<d[h.id]?f(-1):b.length>g?a(c,d,g):0}var b=[];if(!this.options.ajax){for(var c in this.sortDictionary)(this.options.multiSort||0===b.length)&&b.push({id:c,order:this.sortDictionary[c]});b.length>0&&this.rows.sort(a)}}var H=".rs.jquery.bootgrid",I=function(b,c){this.element=a(b),this.origin=this.element.clone(),this.options=a.extend(!0,{},I.defaults,this.element.data(),c);var d=this.options.rowCount=this.element.data().rowCount||c.rowCount||this.options.rowCount;this.columns=[],this.current=1,this.currentRows=[],this.identifier=null,this.selection=!1,this.converter=nu!
ll,this.rowCount=a.isArray(d)?d[0]:d,this.rows=[],this.searchPhrase="",!
this.selectedRows=[],this.sortDictionary={},this.total=0,this.totalPages=0,this.cachedParams={lbl:this.options.labels,css:this.options.css,ctx:{}},this.header=null,this.footer=null,this.xqr=null};if(I.defaults={navigation:3,padding:2,columnSelection:!0,rowCount:[10,25,50,-1],selection:!1,multiSelect:!1,rowSelect:!1,keepSelection:!1,highlightRows:!1,sorting:!0,multiSort:!1,searchSettings:{delay:250,characters:1},ajax:!1,ajaxSettings:{method:"POST"},post:{},url:"",caseSensitive:!0,requestHandler:function(a){return a},responseHandler:function(a){return a},converters:{numeric:{from:function(a){return+a},to:function(a){return a+""}},string:{from:function(a){return a},to:function(a){return a}}},css:{actions:"actions btn-group",center:"text-center",columnHeaderAnchor:"column-header-anchor",columnHeaderText:"text",dropDownItem:"dropdown-item",dropDownItemButton:"dropdown-item-button",dropDownItemCheckbox:"dropdown-item-checkbox",dropDownMenu:"dropdown btn-group",dropDownMenuItems:"!
dropdown-menu pull-right",dropDownMenuText:"dropdown-text",footer:"bootgrid-footer container-fluid",header:"bootgrid-header container-fluid",icon:"icon glyphicon",iconColumns:"glyphicon-th-list",iconDown:"glyphicon-chevron-down",iconRefresh:"glyphicon-refresh",iconSearch:"glyphicon-search",iconUp:"glyphicon-chevron-up",infos:"infos",left:"text-left",pagination:"pagination",paginationButton:"button",responsiveTable:"table-responsive",right:"text-right",search:"search form-group",searchField:"search-field form-control",selectBox:"select-box",selectCell:"select-cell",selected:"active",sortable:"sortable",table:"bootgrid-table table"},formatters:{},labels:{all:"All",infos:"Showing {{ctx.start}} to {{ctx.end}} of {{ctx.total}} entries",loading:"Loading...",noResults:"No results found!",refresh:"Refresh",search:"Search"},statusMapping:{0:"success",1:"info",2:"warning",3:"danger"},templates:{actionButton:'<button class="btn btn-default" type="button" title="{{ctx.text}}">{{ctx.con!
tent}}</button>',actionDropDown:'<div class="{{css.dropDownMenu}}"><but!
ton class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown"><span class="{{css.dropDownMenuText}}">{{ctx.content}}</span> <span class="caret"></span></button><ul class="{{css.dropDownMenuItems}}" role="menu"></ul></div>',actionDropDownItem:'<li><a data-action="{{ctx.action}}" class="{{css.dropDownItem}} {{css.dropDownItemButton}}">{{ctx.text}}</a></li>',actionDropDownCheckboxItem:'<li><label class="{{css.dropDownItem}}"><input name="{{ctx.name}}" type="checkbox" value="1" class="{{css.dropDownItemCheckbox}}" {{ctx.checked}} /> {{ctx.label}}</label></li>',actions:'<div class="{{css.actions}}"></div>',body:"<tbody></tbody>",cell:'<td class="{{ctx.css}}" style="{{ctx.style}}">{{ctx.content}}</td>',footer:'<div id="{{ctx.id}}" class="{{css.footer}}"><div class="row"><div class="col-sm-6"><p class="{{css.pagination}}"></p></div><div class="col-sm-6 infoBar"><p class="{{css.infos}}"></p></div></div></div>',header:'<div id="{{ctx.id}}" class="{{css.header}}"><!
div class="row"><div class="col-sm-12 actionBar"><p class="{{css.search}}"></p><p class="{{css.actions}}"></p></div></div></div>',headerCell:'<th data-column-id="{{ctx.column.id}}" class="{{ctx.css}}" style="{{ctx.style}}"><a href="javascript:void(0);" class="{{css.columnHeaderAnchor}} {{ctx.sortable}}"><span class="{{css.columnHeaderText}}">{{ctx.column.text}}</span>{{ctx.icon}}</a></th>',icon:'<span class="{{css.icon}} {{ctx.iconCss}}"></span>',infos:'<div class="{{css.infos}}">{{lbl.infos}}</div>',loading:'<tr><td colspan="{{ctx.columns}}" class="loading">{{lbl.loading}}</td></tr>',noResults:'<tr><td colspan="{{ctx.columns}}" class="no-results">{{lbl.noResults}}</td></tr>',pagination:'<ul class="{{css.pagination}}"></ul>',paginationItem:'<li class="{{ctx.css}}"><a data-page="{{ctx.page}}" class="{{css.paginationButton}}">{{ctx.text}}</a></li>',rawHeaderCell:'<th class="{{ctx.css}}">{{ctx.content}}</th>',row:"<tr{{ctx.attr}}>{{ctx.cells}}</tr>",search:'<div class="{{css.s!
earch}}"><div class="input-group"><span class="{{css.icon}} input-group!
-addon {{css.iconSearch}}"></span> <input type="text" class="{{css.searchField}}" placeholder="{{lbl.search}}" /></div></div>',select:'<input name="select" type="{{ctx.type}}" class="{{css.selectBox}}" value="{{ctx.value}}" {{ctx.checked}} />'}},I.prototype.append=function(a){if(this.options.ajax);else{for(var b=[],c=0;c<a.length;c++)d.call(this,a[c])&&b.push(a[c]);G.call(this),k.call(this,b),n.call(this),this.element.trigger("appended"+H,[b])}return this},I.prototype.clear=function(){if(this.options.ajax);else{var b=a.extend([],this.rows);this.rows=[],this.current=1,this.total=0,n.call(this),this.element.trigger("cleared"+H,[b])}return this},I.prototype.destroy=function(){return a(b).off(H),1&this.options.navigation&&this.header.remove(),2&this.options.navigation&&this.footer.remove(),this.element.before(this.origin).remove(),this},I.prototype.reload=function(){return this.current=1,n.call(this),this},I.prototype.remove=function(a){if(null!=this.identifier){if(this.options!
.ajax);else{a=a||this.selectedRows;for(var b,c=[],d=0;d<a.length;d++){b=a[d];for(var e=0;e<this.rows.length;e++)if(this.rows[e][this.identifier]===b){c.push(this.rows[e]),this.rows.splice(e,1);break}}this.current=1,n.call(this),this.element.trigger("removed"+H,[c])}}return this},I.prototype.search=function(a){if(a=a||"",this.searchPhrase!==a){var b=h(this.options.css.searchField),c=e.call(this,b);c.val(a)}return B.call(this,a),this},I.prototype.select=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e=[];b.length>0&&(this.options.multiSelect||1!==e.length);)if(c=b.pop(),-1===a.inArray(c,this.selectedRows))for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){e.push(this.currentRows[d]),this.selectedRows.push(c);break}if(e.length>0){var f=h(this.options.css.selectBox),g=this.selectedRows.length>=this.currentRows.length;for(d=0;!this.options.keepSelection&&g&&d<this.currentRows.length;)g=-1!==a.inArray(t!
his.currentRows[d++][this.identifier],this.selectedRows);for(this.eleme!
nt.find("thead "+f).prop("checked",g),this.options.multiSelect||this.element.find("tbody > tr "+f+":checked").trigger("click"+H),d=0;d<this.selectedRows.length;d++)this.element.find('tbody > tr[data-row-id="'+this.selectedRows[d]+'"]').addClass(this.options.css.selected)._bgAria("selected","true").find(f).prop("checked",!0);this.element.trigger("selected"+H,[e])}}return this},I.prototype.deselect=function(b){if(this.selection){b=b||this.currentRows.propValues(this.identifier);for(var c,d,e,f=[];b.length>0;)if(c=b.pop(),e=a.inArray(c,this.selectedRows),-1!==e)for(d=0;d<this.currentRows.length;d++)if(this.currentRows[d][this.identifier]===c){f.push(this.currentRows[d]),this.selectedRows.splice(e,1);break}if(f.length>0){var g=h(this.options.css.selectBox);for(this.element.find("thead "+g).prop("checked",!1),d=0;d<f.length;d++)this.element.find('tbody > tr[data-row-id="'+f[d][this.identifier]+'"]').removeClass(this.options.css.selected)._bgAria("selected","false").find(g).prop(!
"checked",!1);this.element.trigger("deselected"+H,[f])}}return this},I..prototype.sort=function(b){var c=b?a.extend({},b):{};return c===this.sortDictionary?this:(this.sortDictionary=c,C.call(this),G.call(this),n.call(this),this)},I.prototype.getColumnSettings=function(){return a.merge([],this.columns)},I.prototype.getCurrentPage=function(){return this.current},I.prototype.getCurrentRows=function(){return a.merge([],this.currentRows)},I.prototype.getRowCount=function(){return this.rowCount},I.prototype.getSearchPhrase=function(){return this.searchPhrase},I.prototype.getSelectedRows=function(){return a.merge([],this.selectedRows)},I.prototype.getSortDictionary=function(){return a.extend({},this.sortDictionary)},I.prototype.getTotalPageCount=function(){return this.totalPages},I.prototype.getTotalRowCount=function(){return this.total},a.fn.extend({_bgAria:function(a,b){return b?this.attr("aria-"+a,b):this.attr("aria-"+a)},_bgBusyAria:function(a){return null==a||a?this._bgAria("!
busy","true"):this._bgAria("busy","false")},_bgRemoveAria:function(a){re!
turn this.removeAttr("aria-"+a)},_bgEnableAria:function(a){return null==a||a?this.removeClass("disabled")._bgAria("disabled","false"):this.addClass("disabled")._bgAria("disabled","true")},_bgEnableField:function(a){return null==a||a?this.removeAttr("disabled"):this.attr("disabled","disable")},_bgShowAria:function(a){return null==a||a?this.show()._bgAria("hidden","false"):this.hide()._bgAria("hidden","true")},_bgSelectAria:function(a){return null==a||a?this.addClass("active")._bgAria("selected","true"):this.removeClass("active")._bgAria("selected","false")},_bgId:function(a){return a?this.attr("id",a):this.attr("id")}}),!String.prototype.resolve){var J={checked:function(a){return"boolean"==typeof a?a?'checked="checked"':"":a}};String.prototype.resolve=function(b,c){var d=this;return a.each(b,function(b,e){if(null!=e&&"function"!=typeof e)if("object"==typeof e){var f=c?a.extend([],c):[];f.push(b),d=d.resolve(e,f)+""}else{J&&J[b]&&"function"==typeof J[b]&&(e=J[b](e)),b=c?c.joi!
n(".")+"."+b:b;var g=new RegExp("\\{\\{"+b+"\\}\\}","gm");d=d.replace(g,e.replace?e.replace(/\$/gi,"$"):e)}}),d}}Array.prototype.first||(Array.prototype.first=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return c}return null}),Array.prototype.contains||(Array.prototype.contains=function(a){for(var b=0;b<this.length;b++){var c=this[b];if(a(c))return!0}return!1}),Array.prototype.page||(Array.prototype.page=function(a,b){var c=(a-1)*b,d=c+b;return this.length>c?this.length>d?this.slice(c,d):this.slice(c):[]}),Array.prototype.where||(Array.prototype.where=function(a){for(var b=[],c=0;c<this.length;c++){var d=this[c];a(d)&&b.push(d)}return b}),Array.prototype.propValues||(Array.prototype.propValues=function(a){for(var b=[],c=0;c<this.length;c++)b.push(this[c][a]);return b});var K=a.fn.bootgrid;a.fn.bootgrid=function(b){var c=Array.prototype.slice.call(arguments,1),d=null,e=this.each(function(e){var f=a(this),g=f.data(H),h="object"==typeof b&&b;if((g||"des!
troy"!==b)&&(g||(f.data(H,g=new I(this,h)),j.call(g)),"string"==typeof !
b))if(0===b.indexOf("get")&&0===e)d=g[b].apply(g,c);else if(0!==b.indexOf("get"))return g[b].apply(g,c)});return"string"==typeof b&&0===b.indexOf("get")?d:e},a.fn.bootgrid.Constructor=I,a.fn.bootgrid.noConflict=function(){return a.fn.bootgrid=K,this},a('[data-toggle="bootgrid"]').bootgrid()}(jQuery,window);
\ No newline at end of file
diff --git a/ui/pages/wok-ui.html.tmpl b/ui/pages/wok-ui.html.tmpl
index 395c661..d5bac63 100644
--- a/ui/pages/wok-ui.html.tmpl
+++ b/ui/pages/wok-ui.html.tmpl
@@ -54,6 +54,7 @@
<link rel="stylesheet" href="$href('css/jquery-ui.custom.css')">
<link rel="stylesheet" href="$href('css/bootstrap.custom.css')">
<link rel="stylesheet" href="$href('libs/bootstrap-select/dist/css/bootstrap-select.min.css')">
+ <link rel="stylesheet" href="$href('libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css')">
<link rel="stylesheet" href="$href('css/fontawesome/fontawesome.css')">
<link rel="stylesheet" href="$href('css/opensans/opensans.css')">
<link rel="stylesheet" href="$href('css/bootstrap-select.custom.css')">
@@ -65,6 +66,7 @@
<script src="$href('libs/jquery-i18n/jquery.i18n.min.js')"></script>
<script src="$href('libs/bootstrap/bootstrap.min.js')"></script>
<script src="$href('libs/bootstrap-select/dist/js/bootstrap-select.min.js')"></script>
+ <script src="$href('libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js')"></script>
<script src="$href('base64/jquery.base64.js')"></script>
<script src="$href('js/wok.min.js')"></script>
<!-- This is used for detecting if the UI needs to be built -->
--
2.4.3
9 years
[PATCH 0/1] Library files for bootgrid plugin
by atreyee@linux.vnet.ibm.com
From: Chandra Shekhar Reddy Potula <chandra(a)linux.vnet.ibm.com>
Bootgrid is grid implemention for bootstrap and jquery with sorting,
multiselect,searching featuers.
Atreyee Mukhopadhyay (1):
Library files for bootgrid plugin
configure.ac | 4 ++++
ui/libs/Makefile.am | 2 +-
ui/libs/jquery-bootgrid/LICENSE | 17 +++++++++++++++++
ui/libs/jquery-bootgrid/Makefile.am | 20 ++++++++++++++++++++
ui/libs/jquery-bootgrid/dist/Makefile.am | 20 ++++++++++++++++++++
ui/libs/jquery-bootgrid/dist/css/Makefile.am | 22 ++++++++++++++++++++++
.../dist/css/jquery.bootgrid.min.css | 5 +++++
ui/libs/jquery-bootgrid/dist/js/Makefile.am | 22 ++++++++++++++++++++++
.../jquery-bootgrid/dist/js/jquery.bootgrid.min.js | 6 ++++++
ui/pages/wok-ui.html.tmpl | 2 ++
10 files changed, 119 insertions(+), 1 deletion(-)
create mode 100644 ui/libs/jquery-bootgrid/LICENSE
create mode 100644 ui/libs/jquery-bootgrid/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/css/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/css/jquery.bootgrid.min.css
create mode 100644 ui/libs/jquery-bootgrid/dist/js/Makefile.am
create mode 100644 ui/libs/jquery-bootgrid/dist/js/jquery.bootgrid.min.js
--
2.4.3
9 years
[PATCH 00/10] FVT testcases base framework
by archus@linux.vnet.ibm.com
From: Archana Singh <archus(a)linux.vnet.ibm.com>
Patch for adding FVT testcases base framework which can be used across plugins.
The changes are:
1) Added fvt package inside tests directory.
2) Added config file inside fvt package to have session details.
3) restapilib.py to have common classes/methods for REST API calls.
4) fvt_base.py, a base test class to take care of doing
common setup and treadown required for any fvt test cases like
creating/destroying session using config file and restapilib.py.
5) run_test.sh.in script to install all the dependencies and to run all FVT.
6) make file changes to have 'make check-fvt' for running all the FVT.
Archana Singh (10):
Package for functional verification testcases
Wok level config file to have sections required for functional
verification test common across plugins.
Lists all dependecies for fvt testcases.
Common classes/methods for API calls as per config file
configuration.
Base test class, takes care common actions required for any FVT
test cases like creating/destoring session, authorization using
wok level fvt config file, creating JSON validator and logging.
Install all the dependencies from requirements.txt in python
virtualenv and runs all the FVT test cases.
Makefile needed for build and run fvt.
Added fvt as subdirs and check-fvt to run fvt testcases using
make.
Added FVT Makefile in AC_CONFIG_FILES list.
Added check-fvt to run FVT testcases using make and venv dir to be
cleaned.
Makefile.am | 8 +-
configure.ac | 1 +
tests/Makefile.am | 6 +
tests/fvt/Makefile.am | 43 +++
tests/fvt/__init__.py | 18 ++
tests/fvt/config | 7 +
tests/fvt/fvt_base.py | 92 ++++++
tests/fvt/requirements.txt | 23 ++
tests/fvt/restapilib.py | 760 +++++++++++++++++++++++++++++++++++++++++++++
tests/fvt/run_tests.sh.in | 92 ++++++
10 files changed, 1048 insertions(+), 2 deletions(-)
create mode 100644 tests/fvt/Makefile.am
create mode 100644 tests/fvt/__init__.py
create mode 100644 tests/fvt/config
create mode 100644 tests/fvt/fvt_base.py
create mode 100644 tests/fvt/requirements.txt
create mode 100644 tests/fvt/restapilib.py
create mode 100755 tests/fvt/run_tests.sh.in
--
2.1.0
9 years
[RFC] UI changes for Linux bridge/OVS bridge support
by Lucio Correia
Hi team,
This is a proposal for UI changes to reflect the new API options for
"add network".
Network tab -> User clicks on add (+) button
Currently, there are 3 options in "Network Type": Isolated, NAT, Bridged
(which sets macvtap bridges as of now).
The API is changing for Kimchi 2.0:
"Bridged" option will now be used for creating networks tied to Linux
bridges, while a 4th option "macvtap" will be added for creating
networks tied to macvtap bridges.
Changes in Bridged option:
1) Change description of "Bridged" option to refer to Linux bridges
2) When "Bridged" is selected, "Destination" will show all the
interfaces it currently shows plus OVS bridges
3) "Enable VLAN" is not showed
4) "Bridged" option will call network_create API with "bridge" as value
for "connection" parameter
New macvtap option:
5) Add a new option "macvtap" to Network Type
6) When macvtap option is selected, "Destination" will show the same
that is currently showed for "Bridged" option (no OVS bridges)
7) "Enable VLAN" option is showed
8) macvtap option will call network create API with "macvtap" as
"connection" parameter value
Please let me know your thoughts.
Thanks,
--
Lucio Correia
Software Engineer
IBM LTC Brazil
9 years
[PATCH 0/4 - v333] Add multiple disks in Template
by Rodrigo Trujillo
V3 - Addresses Aline's comments
* fixes backend, API.md and API.json
* fixes test cases and add new test
V2 - Rebase over latest new UI patches
V1 - Initial version
Rodrigo Trujillo (4):
Fix Template backend create/update for multiple disks
UI - Implement multiple disks support in Template edit window
Test: Fix and add test related to disks in templates
Fix pep8 problems
src/wok/plugins/kimchi/API.json | 19 +-
src/wok/plugins/kimchi/docs/API.md | 3 +
src/wok/plugins/kimchi/model/templates.py | 9 +-
src/wok/plugins/kimchi/model/vms.py | 6 +-
src/wok/plugins/kimchi/osinfo.py | 1 +
src/wok/plugins/kimchi/tests/test_rest.py | 8 +-
src/wok/plugins/kimchi/tests/test_template.py | 36 ++-
.../kimchi/ui/js/src/kimchi.template_edit_main.js | 246 ++++++++++-----------
.../kimchi/ui/pages/template-edit.html.tmpl | 5 +-
src/wok/plugins/kimchi/vmtemplate.py | 13 +-
10 files changed, 204 insertions(+), 142 deletions(-)
--
2.1.0
9 years
[PATCH] Update VCPU by using libvirt function
by Jose Ricardo Ziviani
From: Crístian Deives <cristiandeives(a)gmail.com>
Currently, the VCPU count is updated by editing the configuration XML
(tag: <vcpu>). However, in some cases, editing that tag will only update
the maximum VCPU count, not the current one. For example, if the VM has
the following XML tag:
<vcpu current='2'>8</vcpu>
and the user updates the VCPU value to 10, Kimchi will update the XML
tag to:
<vcpu current='2'>10</vcpu>
Use the libvirt function "setVcpusFlags" to update the current and the
maximum VCPU values to the one specified by the user.
* This patch also changes vcpu setting when guest is being
created. Now 'current' is always set and is the total of cpus
from the Template and maxVcpu is the >= 255 or (sockets * cores
* threads) in PPC. In x86 it is the value of libvirt getMaxVcpu.
* Change NUMA management. Sets all cpus to 0. There is not
impact to the guest.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
Signed-off-by: Crístian Deives <cristiandeives(a)gmail.com>
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/wok/plugins/kimchi/i18n.py | 3 +
src/wok/plugins/kimchi/mockmodel.py | 51 +++++------
src/wok/plugins/kimchi/model/vms.py | 144 +++++++++++++++++++++++++++---
src/wok/plugins/kimchi/tests/test_rest.py | 10 ++-
src/wok/plugins/kimchi/vmtemplate.py | 26 +++++-
5 files changed, 186 insertions(+), 48 deletions(-)
diff --git a/src/wok/plugins/kimchi/i18n.py b/src/wok/plugins/kimchi/i18n.py
index de7962a..29a7222 100644
--- a/src/wok/plugins/kimchi/i18n.py
+++ b/src/wok/plugins/kimchi/i18n.py
@@ -128,6 +128,9 @@ messages = {
"KCHVM0069E": _("Password field must be a string."),
"KCHVM0070E": _("Error creating local host ssh rsa key of user 'root'."),
"KCHVM0071E": _("Memory value %(mem)s must be aligned to %(alignment)sMiB."),
+ "KCHVM0072E": _("Unable to update the following parameters while the VM is offline: %(params)s"),
+ "KCHVM0073E": _("Unable to update the following parameters while the VM is online: %(params)s"),
+ "KCHVM0074E": _("Cannot change VCPU value because '%(vm)s' has a topology defined - sockets: %(sockets)s, cores: %(cores)s, threads: %(threads)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/wok/plugins/kimchi/mockmodel.py b/src/wok/plugins/kimchi/mockmodel.py
index 9186f78..cca38e5 100644
--- a/src/wok/plugins/kimchi/mockmodel.py
+++ b/src/wok/plugins/kimchi/mockmodel.py
@@ -21,6 +21,8 @@ import libvirt
import lxml.etree as ET
import os
import time
+
+from collections import defaultdict
from lxml import objectify
from lxml.builder import E
@@ -60,10 +62,9 @@ storagevolumes.VALID_RAW_CONTENT = ['dos/mbr boot sector',
class MockModel(Model):
- _mock_vms = {}
+ _mock_vms = defaultdict(list)
_mock_snapshots = {}
_XMLDesc = libvirt.virDomain.XMLDesc
- _defineXML = libvirt.virConnect.defineXML
_undefineDomain = libvirt.virDomain.undefine
_libvirt_get_vol_path = LibvirtVMTemplate._get_volume_path
@@ -81,7 +82,6 @@ class MockModel(Model):
cpuinfo.get_topo_capabilities = MockModel.get_topo_capabilities
vmifaces.getDHCPLeases = MockModel.getDHCPLeases
- libvirt.virConnect.defineXML = MockModel.domainDefineXML
libvirt.virDomain.XMLDesc = MockModel.domainXMLDesc
libvirt.virDomain.undefine = MockModel.undefineDomain
libvirt.virDomain.attachDeviceFlags = MockModel.attachDeviceFlags
@@ -124,7 +124,7 @@ class MockModel(Model):
imageinfo.probe_image = self._probe_image
def reset(self):
- MockModel._mock_vms = {}
+ MockModel._mock_vms = defaultdict(list)
MockModel._mock_snapshots = {}
if hasattr(self, 'objstore'):
@@ -157,21 +157,15 @@ class MockModel(Model):
return ET.fromstring(xml)
@staticmethod
- def domainDefineXML(conn, xml):
- name = objectify.fromstring(xml).name.text
- try:
- dom = conn.lookupByName(name)
- if not dom.isActive():
- MockModel._mock_vms[name] = xml
- except:
- pass
+ def domainXMLDesc(dom, flags=0):
+ xml = MockModel._XMLDesc(dom, flags)
+ root = objectify.fromstring(xml)
- return MockModel._defineXML(conn, xml)
+ for dev_xml in MockModel._mock_vms.get(dom.name(), []):
+ dev = objectify.fromstring(dev_xml)
+ root.devices.append(dev)
- @staticmethod
- def domainXMLDesc(dom, flags=0):
- return MockModel._mock_vms.get(dom.name(),
- MockModel._XMLDesc(dom, flags))
+ return ET.tostring(root, encoding="utf-8")
@staticmethod
def undefineDomain(dom):
@@ -182,12 +176,7 @@ class MockModel(Model):
@staticmethod
def attachDeviceFlags(dom, xml, flags=0):
- old_xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
- root = objectify.fromstring(old_xml)
- dev = objectify.fromstring(xml)
- root.devices.append(dev)
-
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ MockModel._mock_vms[dom.name()].append(xml)
@staticmethod
def _get_device_node(dom, xml):
@@ -213,16 +202,18 @@ class MockModel(Model):
@staticmethod
def detachDeviceFlags(dom, xml, flags=0):
- root, dev = MockModel._get_device_node(dom, xml)
- root.devices.remove(dev)
-
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ node = ET.fromstring(xml)
+ xml = ET.tostring(node, encoding="utf-8", pretty_print=True)
+ if xml in MockModel._mock_vms[dom.name()]:
+ MockModel._mock_vms[dom.name()].remove(xml)
@staticmethod
def updateDeviceFlags(dom, xml, flags=0):
- root, old_dev = MockModel._get_device_node(dom, xml)
- root.devices.replace(old_dev, objectify.fromstring(xml))
- MockModel._mock_vms[dom.name()] = ET.tostring(root, encoding="utf-8")
+ _, old_dev = MockModel._get_device_node(dom, xml)
+ old_xml = ET.tostring(old_dev, encoding="utf-8", pretty_print=True)
+ if old_xml in MockModel._mock_vms[dom.name()]:
+ MockModel._mock_vms[dom.name()].remove(old_xml)
+ MockModel._mock_vms[dom.name()].append(xml)
@staticmethod
def volResize(vol, size, flags=0):
diff --git a/src/wok/plugins/kimchi/model/vms.py b/src/wok/plugins/kimchi/model/vms.py
index 7e12b91..a67044d 100644
--- a/src/wok/plugins/kimchi/model/vms.py
+++ b/src/wok/plugins/kimchi/model/vms.py
@@ -56,6 +56,7 @@ from wok.plugins.kimchi.model.utils import get_ascii_nonascii_name, get_vm_name
from wok.plugins.kimchi.model.utils import get_metadata_node
from wok.plugins.kimchi.model.utils import remove_metadata_node
from wok.plugins.kimchi.model.utils import set_metadata_node
+from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
from wok.plugins.kimchi.screenshot import VMScreenshot
from wok.plugins.kimchi.utils import template_name_from_uri
from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml
@@ -71,10 +72,15 @@ DOM_STATE_MAP = {0: 'nostate',
6: 'crashed',
7: 'pmsuspended'}
-VM_STATIC_UPDATE_PARAMS = {'name': './name',
- 'cpus': './vcpu'}
+VM_STATIC_UPDATE_PARAMS = {'name': './name'}
VM_LIVE_UPDATE_PARAMS = {}
+# update parameters which are updatable when the VM is online
+VM_ONLINE_UPDATE_PARAMS = ['graphics', 'groups', 'memory', 'users']
+# update parameters which are updatable when the VM is offline
+VM_OFFLINE_UPDATE_PARAMS = ['cpus', 'graphics', 'groups', 'memory', 'name',
+ 'users']
+
XPATH_DOMAIN_DISK = "/domain/devices/disk[@device='disk']/source/@file"
XPATH_DOMAIN_DISK_BY_FILE = "./devices/disk[@device='disk']/source[@file='%s']"
XPATH_DOMAIN_NAME = '/domain/name'
@@ -84,9 +90,12 @@ XPATH_DOMAIN_MAC_BY_ADDRESS = "./devices/interface[@type='network']/"\
XPATH_DOMAIN_MEMORY = '/domain/memory'
XPATH_DOMAIN_MEMORY_UNIT = '/domain/memory/@unit'
XPATH_DOMAIN_UUID = '/domain/uuid'
+XPATH_DOMAIN_DEV_CPU_ID = '/domain/devices/spapr-cpu-socket/@id'
XPATH_NUMA_CELL = './cpu/numa/cell'
+XPATH_TOPOLOGY = './cpu/topology'
+
# key: VM name; value: lock object
vm_locks = {}
@@ -98,6 +107,17 @@ class VMsModel(object):
self.caps = CapabilitiesModel(**kargs)
self.task = TaskModel(**kargs)
+ def _get_host_maxcpu(self):
+ if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
+ cpu_model = CPUInfoModel(conn=self.conn)
+ max_vcpu_val = (cpu_model.cores_available *
+ cpu_model.threads_per_core)
+ if max_vcpu_val > 255:
+ max_vcpu_val = 255
+ else:
+ max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
+ return max_vcpu_val
+
def create(self, params):
t_name = template_name_from_uri(params['template'])
vm_list = self.get_list()
@@ -163,7 +183,8 @@ class VMsModel(object):
xml = t.to_vm_xml(name, vm_uuid,
libvirt_stream_protocols=stream_protocols,
graphics=graphics,
- volumes=vol_list)
+ volumes=vol_list,
+ max_vcpus=self._get_host_maxcpu())
cb('Defining new VM')
try:
@@ -231,6 +252,30 @@ class VMModel(object):
self.vmsnapshots = cls(**kargs)
self.stats = {}
+ def has_topology(self, dom):
+ xml = dom.XMLDesc(0)
+ sockets = xpath_get_text(xml, XPATH_TOPOLOGY + '/@sockets')
+ cores = xpath_get_text(xml, XPATH_TOPOLOGY + '/@cores')
+ threads = xpath_get_text(xml, XPATH_TOPOLOGY + '/@threads')
+ return sockets and cores and threads
+
+ def get_vm_max_sockets(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@sockets')[0])
+
+ def get_vm_sockets(self, dom):
+ current_vcpu = dom.vcpusFlags(libvirt.VIR_DOMAIN_AFFECT_CURRENT)
+ return (current_vcpu / self.get_vm_cores(dom) /
+ self.get_vm_threads(dom))
+
+ def get_vm_cores(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@cores')[0])
+
+ def get_vm_threads(self, dom):
+ return int(xpath_get_text(dom.XMLDesc(0),
+ XPATH_TOPOLOGY + '/@threads')[0])
+
def update(self, name, params):
lock = vm_locks.get(name)
if lock is None:
@@ -247,8 +292,20 @@ class VMModel(object):
'alignment': str(PPC_MEM_ALIGN)})
dom = self.get_vm(name, self.conn)
- vm_name, dom = self._static_vm_update(name, dom, params)
+
+ if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
+ ext_params = set(params.keys()) - set(VM_OFFLINE_UPDATE_PARAMS)
+ if len(ext_params) > 0:
+ raise InvalidParameter('KCHVM0072E',
+ {'params': ', '.join(ext_params)})
+ else:
+ ext_params = set(params.keys()) - set(VM_ONLINE_UPDATE_PARAMS)
+ if len(ext_params) > 0:
+ raise InvalidParameter('KCHVM0073E',
+ {'params': ', '.join(ext_params)})
+
self._live_vm_update(dom, params)
+ vm_name, dom = self._static_vm_update(name, dom, params)
return vm_name
def clone(self, name):
@@ -713,23 +770,34 @@ class VMModel(object):
params['name'], nonascii_name = get_ascii_nonascii_name(name)
for key, val in params.items():
+ change_numa = True
if key in VM_STATIC_UPDATE_PARAMS:
if type(val) == int:
val = str(val)
xpath = VM_STATIC_UPDATE_PARAMS[key]
- new_xml = xml_item_update(new_xml, xpath, val)
+ attrib = None
+ if key == 'cpus':
+ if self.has_topology(dom) or dom.isActive():
+ change_numa = False
+ continue
+ # Update maxvcpu firstly
+ new_xml = xml_item_update(new_xml, xpath,
+ str(self._get_host_maxcpu()),
+ attrib)
+ # Update current vcpu
+ attrib = 'current'
+ new_xml = xml_item_update(new_xml, xpath, val, attrib)
# Updating memory and NUMA if necessary, if vm is offline
if not dom.isActive():
if 'memory' in params:
new_xml = self._update_memory_config(new_xml, params)
- elif 'cpus' in params and \
+ elif 'cpus' in params and change_numa and \
(xpath_get_text(new_xml, XPATH_NUMA_CELL + '/@memory') != []):
- vcpus = params['cpus']
new_xml = xml_item_update(
new_xml,
XPATH_NUMA_CELL,
- value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
+ value='0',
attr='cpus')
if 'graphics' in params:
@@ -764,6 +832,8 @@ class VMModel(object):
raise OperationFailed("KCHVM0008E", {'name': vm_name,
'err': e.get_error_message()})
+ if name is not None:
+ vm_name = name
return (nonascii_name if nonascii_name is not None else vm_name, dom)
def _update_memory_config(self, xml, params):
@@ -775,21 +845,20 @@ class VMModel(object):
vcpus = params.get('cpus')
if numa_mem == []:
if vcpus is None:
- vcpus = int(xpath_get_text(xml,
- VM_STATIC_UPDATE_PARAMS['cpus'])[0])
+ vcpus = int(xpath_get_text(xml, 'vcpu')[0])
cpu = root.find('./cpu')
if cpu is None:
- cpu = get_cpu_xml(vcpus, params['memory'] << 10)
+ cpu = get_cpu_xml(0, params['memory'] << 10)
root.insert(0, ET.fromstring(cpu))
else:
- numa_element = get_numa_xml(vcpus, params['memory'] << 10)
+ numa_element = get_numa_xml(0, params['memory'] << 10)
cpu.insert(0, ET.fromstring(numa_element))
else:
if vcpus is not None:
xml = xml_item_update(
xml,
XPATH_NUMA_CELL,
- value='0-' + str(vcpus - 1) if vcpus > 1 else '0',
+ value='0',
attr='cpus')
root = ET.fromstring(xml_item_update(xml, XPATH_NUMA_CELL,
str(params['memory'] << 10),
@@ -853,11 +922,60 @@ class VMModel(object):
return new_xml
return ET.tostring(root, encoding="utf-8")
+ def _get_host_maxcpu(self):
+ if os.uname()[4] in ['ppc', 'ppc64', 'ppc64le']:
+ cpu_model = CPUInfoModel(conn=self.conn)
+ max_vcpu_val = (cpu_model.cores_available *
+ cpu_model.threads_per_core)
+ if max_vcpu_val > 255:
+ max_vcpu_val = 255
+ else:
+ max_vcpu_val = self.conn.get().getMaxVcpus('kvm')
+ return max_vcpu_val
+
def _live_vm_update(self, dom, params):
self._vm_update_access_metadata(dom, params)
if 'memory' in params and dom.isActive():
self._update_memory_live(dom, params)
+ if 'cpus' in params:
+ cpus = params['cpus']
+
+ if DOM_STATE_MAP[dom.info()[0]] == 'shutoff':
+
+ # user cannot change vcpu if topology is defined. In this case
+ # vcpu must always be sockets * cores * threads.
+ current_vcpu = dom.vcpusFlags(libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
+
+ if self.has_topology(dom):
+ if current_vcpu != cpus:
+ raise InvalidOperation('KCHVM0074E',
+ {'vm': dom.name(),
+ 'sockets':
+ self.get_vm_max_sockets(),
+ 'cores':
+ self.get_vm_cores(),
+ 'threads':
+ self.get_vm_threads()})
+
+ # do not need to update vcpu if the value edit did not
+ # change
+ else:
+ return
+
+ try:
+ # set maximum VCPU count
+ max_vcpus = self._get_host_maxcpu()
+ dom.setVcpusFlags(max_vcpus,
+ libvirt.VIR_DOMAIN_AFFECT_CONFIG |
+ libvirt.VIR_DOMAIN_VCPU_MAXIMUM)
+
+ # set current VCPU count
+ dom.setVcpusFlags(cpus, libvirt.VIR_DOMAIN_AFFECT_CONFIG)
+ except libvirt.libvirtError, e:
+ raise OperationFailed('KCHVM0008E', {'name': dom.name(),
+ 'err': e.message})
+
def _update_memory_live(self, dom, params):
# Check if host supports memory device
if not self.caps.mem_hotplug_support:
diff --git a/src/wok/plugins/kimchi/tests/test_rest.py b/src/wok/plugins/kimchi/tests/test_rest.py
index 544f2e6..9fa8c8d 100644
--- a/src/wok/plugins/kimchi/tests/test_rest.py
+++ b/src/wok/plugins/kimchi/tests/test_rest.py
@@ -144,6 +144,10 @@ class RestTests(unittest.TestCase):
vm = json.loads(self.request('/plugins/kimchi/vms/vm-1').read())
self.assertEquals('vm-1', vm['name'])
+ req = json.dumps({'cpus': 3})
+ resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
+ self.assertEquals(200, resp.status)
+
resp = self.request('/plugins/kimchi/vms/vm-1/start', '{}', 'POST')
self.assertEquals(200, resp.status)
@@ -155,9 +159,10 @@ class RestTests(unittest.TestCase):
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
self.assertEquals(400, resp.status)
- req = json.dumps({'cpus': 3})
+ # Unable to do CPU hotplug
+ req = json.dumps({'cpus': 5})
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
- self.assertEquals(200, resp.status)
+ self.assertEquals(400, resp.status)
# Check if there is support to memory hotplug, once vm is running
resp = self.request('/plugins/kimchi/config/capabilities').read()
@@ -171,6 +176,7 @@ class RestTests(unittest.TestCase):
req = json.dumps({"graphics": {'passwd': "abcdef"}})
resp = self.request('/plugins/kimchi/vms/vm-1', req, 'PUT')
+ self.assertEquals(200, resp.status)
info = json.loads(resp.read())
self.assertEquals('abcdef', info["graphics"]["passwd"])
self.assertEquals(None, info["graphics"]["passwdValidTo"])
diff --git a/src/wok/plugins/kimchi/vmtemplate.py b/src/wok/plugins/kimchi/vmtemplate.py
index 3097b66..8d669db 100644
--- a/src/wok/plugins/kimchi/vmtemplate.py
+++ b/src/wok/plugins/kimchi/vmtemplate.py
@@ -298,7 +298,7 @@ class VMTemplate(object):
cpu_info = self.info.get('cpu_info')
if cpu_info is not None:
cpu_topo = cpu_info.get('topology')
- return get_cpu_xml(self.info.get('cpus'),
+ return get_cpu_xml(0,
self.info.get('memory') << 10,
cpu_topo)
@@ -311,7 +311,6 @@ class VMTemplate(object):
params['qemu-namespace'] = ''
params['cdroms'] = ''
params['qemu-stream-cmdline'] = ''
- params['cpu_info'] = self._get_cpu_xml()
params['disks'] = self._get_disks_xml(vm_uuid)
params['serial'] = get_serial_xml(params)
@@ -322,6 +321,8 @@ class VMTemplate(object):
libvirt_stream_protocols = kwargs.get('libvirt_stream_protocols', [])
cdrom_xml = self._get_cdrom_xml(libvirt_stream_protocols)
+ max_vcpus = kwargs.get('max_vcpus', 1)
+
if not urlparse.urlparse(self.info.get('cdrom', "")).scheme in \
libvirt_stream_protocols and \
params.get('iso_stream', False):
@@ -344,6 +345,25 @@ class VMTemplate(object):
if distro == "IBM_PowerKVM":
params['slots'] = 32
+ cpu_topo = self.info.get('cpu_info').get('topology')
+ if (cpu_topo is not None):
+ sockets = int(max_vcpus / (cpu_topo['cores'] *
+ cpu_topo['threads']))
+ self.info['cpu_info']['topology']['sockets'] = sockets
+
+ # Reduce maxvcpu to fit number of sockets if necessary
+ total_max_vcpu = sockets * cpu_topo['cores'] * cpu_topo['threads']
+ if total_max_vcpu != max_vcpus:
+ max_vcpus = total_max_vcpu
+
+ params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
+ (params['cpus'], max_vcpus)
+ else:
+ params['vcpus'] = "<vcpu current='%s'>%d</vcpu>" % \
+ (params['cpus'], max_vcpus)
+
+ params['cpu_info'] = self._get_cpu_xml()
+
xml = """
<domain type='%(domain)s'>
%(qemu-stream-cmdline)s
@@ -351,7 +371,7 @@ class VMTemplate(object):
<uuid>%(uuid)s</uuid>
<maxMemory slots='%(slots)s' unit='KiB'>%(max_memory)s</maxMemory>
<memory unit='MiB'>%(memory)s</memory>
- <vcpu>%(cpus)s</vcpu>
+ %(vcpus)s
%(cpu_info)s
<os>
<type arch='%(arch)s'>hvm</type>
--
1.9.1
9 years
[PATCH 0/5I -V4] Add multiple disks in Template, add max vcpu and topology support
by Rodrigo Trujillo
V4 - Include patch with support to max vcpu and other changes in vm backend to support
cpu hot plug in the future.
- Fixes vm update problem (offline update was not atomic)
V3 - Addresses Aline's comments
* fixes backend, API.md and API.json
* fixes test cases and add new test
V2 - Rebase over latest new UI patches
V1 - Initial version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
*** BLURB HERE ***
Rodrigo Trujillo (5):
Fix Template backend create/update for multiple disks
Enable create guests with multiple disks from different pools
UI - Implement multiple disks support in Template edit window
Test: Fix and add test related to disks in templates
Update VCPU by using libvirt function
src/wok/plugins/kimchi/API.json | 19 +-
src/wok/plugins/kimchi/docs/API.md | 6 +
src/wok/plugins/kimchi/i18n.py | 4 +
src/wok/plugins/kimchi/mockmodel.py | 57 +++--
src/wok/plugins/kimchi/model/templates.py | 30 ++-
src/wok/plugins/kimchi/model/vms.py | 130 +++++++++--
src/wok/plugins/kimchi/osinfo.py | 1 +
src/wok/plugins/kimchi/tests/test_model.py | 13 ++
src/wok/plugins/kimchi/tests/test_rest.py | 15 +-
src/wok/plugins/kimchi/tests/test_template.py | 44 +++-
.../kimchi/ui/js/src/kimchi.template_edit_main.js | 246 ++++++++++-----------
.../kimchi/ui/pages/template-edit.html.tmpl | 5 +-
src/wok/plugins/kimchi/vmtemplate.py | 81 +++++--
13 files changed, 425 insertions(+), 226 deletions(-)
--
2.1.0
9 years
Ginger Base has its own repository!
by Aline Manera
Hi all,
As planned, Ginger Base now has its own repository:
https://github.com/kimchi-project/gingerbase
And it is now part of Ginger community. Please, follow the Ginger
community instructions to submit patches to Ginger Base.
All the Ginger Base code was removed from Kimchi repository.
Please, use git-submodule to get access to the Ginger Base code through
Kimchi repository.
Let me know on any problem on that.
Regards,
Aline Manera
9 years