[PATCH V2] [Wok 0/7] Issue #141 - Handle AsyncTasks in request log
by Lucio Correia
V2: rebase with master to fix conflict
Lucio Correia (7):
Create log_request() method for code simplification
Improve plugin lookup by request
Update Request Logger to handle AsyncTask status
Log AsyncTask success or failure
Save log entry IDs for requests that generate tasks
Put failure details into parenthesis
Change location of User Requests Log
src/wok/asynctask.py | 46 +++++++++++++--
src/wok/config.py.in | 4 ++
src/wok/control/base.py | 75 +++++++++++++-----------
src/wok/i18n.py | 2 +
src/wok/message.py | 2 +-
src/wok/reqlogger.py | 150 +++++++++++++++++++++++++++---------------------
src/wok/root.py | 41 ++-----------
src/wok/utils.py | 14 +++--
8 files changed, 187 insertions(+), 147 deletions(-)
--
1.9.1
8 years, 2 months
[PATCHv3] [Kimchi] Emphasize resource name in dlg
by Socorro Stoppler
From: Socorro <socorro(a)linux.vnet.ibm.com>
v3:
In some distros, the resource name is not being displayed in bold due
a problem with Open Sans per Samuel. There are some cases, mainly with
Firefox in that some style with font weight set to 600 does not work and 700
is usually too bold to set as a fallback. Therefore, just use the same style
as <abbr> and <acronynm> tags which creates a dotted bottom
v2:
Display resource name (with name in bold) in confirmation dialog
v1:
When confirmation dialogs are shown, also display the name of the resource being
acted on (i.e. guest name, network, storage, template) as part of the message.
Signed-off-by: Socorro <socorro(a)linux.vnet.ibm.com>
---
ui/css/kimchi.css | 4 ++++
ui/css/src/modules/_guests.scss | 4 ++++
ui/js/src/kimchi.guest_main.js | 14 +++++++++-----
ui/js/src/kimchi.network.js | 6 ++++--
ui/js/src/kimchi.storage_main.js | 8 +++++---
ui/js/src/kimchi.template_main.js | 5 +++--
ui/pages/i18n.json.tmpl | 20 ++++++++++----------
7 files changed, 39 insertions(+), 22 deletions(-)
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index 59d96e1..f49c955 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -294,6 +294,10 @@
font-size: 32px;
}
+strong {
+ border-bottom: 1px dotted;
+}
+
#guest-add-window .modal-body {
margin: 0;
padding: 0;
diff --git a/ui/css/src/modules/_guests.scss b/ui/css/src/modules/_guests.scss
index 6c24800..65d6792 100644
--- a/ui/css/src/modules/_guests.scss
+++ b/ui/css/src/modules/_guests.scss
@@ -46,6 +46,10 @@
}
}
+strong {
+ border-bottom: 1px dotted;
+}
+
#guest-add-window {
.modal-body {
margin: 0;
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js
index 9c1aa54..34217e4 100644
--- a/ui/js/src/kimchi.guest_main.js
+++ b/ui/js/src/kimchi.guest_main.js
@@ -127,8 +127,9 @@ kimchi.vmpoweroff = function(event) {
var vm_id = vm.attr("id");
var vmObject = vm.data();
var vm_persistent = vmObject.persistent == true;
- var content_msg = vm_persistent ? i18n['KCHVM6003M'] :
- i18n['KCHVM6009M'];
+ var content_msg_1 = i18n['KCHVM6003M'].replace('%1', '<strong>'+vm_id+'</strong>');
+ var content_msg_2 = i18n['KCHVM6009M'].replace('%1', '<strong>'+vm_id+'</strong>');
+ var content_msg = vm_persistent ? content_msg_1 : content_msg_2;
var settings = {
title: i18n['KCHVM6002M'],
content: content_msg,
@@ -153,9 +154,10 @@ kimchi.vmshutdown = function(event) {
var button = event.target;
var vm = $(button).closest('li[name=guest]');
var vm_id = vm.attr("id");
+ var confirmMessage = i18n['KCHVM6007M'].replace('%1', '<strong>'+vm_id+'</strong>');
var settings = {
title: i18n['KCHVM6006M'],
- content: i18n['KCHVM6007M'],
+ content: confirmMessage,
confirm: i18n['KCHAPI6002M'],
cancel: i18n['KCHAPI6003M']
};
@@ -174,9 +176,10 @@ kimchi.vmreset = function(event) {
$(button).addClass('loading');
var vm = $(button).closest('li[name=guest]');
var vm_id = $(vm).attr("id");
+ var confirmMessage = i18n['KCHVM6005M'].replace('%1', '<strong>'+vm_id+'</strong>');
var settings = {
title: i18n['KCHVM6004M'],
- content: i18n['KCHVM6005M'],
+ content: confirmMessage,
confirm: i18n['KCHAPI6002M'],
cancel: i18n['KCHAPI6003M']
};
@@ -204,9 +207,10 @@ kimchi.vmdelete = function(event) {
var button = event.target;
var vm = $(button).closest('li[name=guest]');
var vm_id = $(vm).attr("id");
+ var confirmMessage = i18n['KCHVM6001M'].replace('%1', '<strong>'+vm_id+'</strong>');
var settings = {
title: i18n['KCHVM6008M'],
- content: i18n['KCHVM6001M'],
+ content: confirmMessage,
confirm: i18n['KCHAPI6002M'],
cancel: i18n['KCHAPI6003M']
};
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index ac6bf74..7ce5b28 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -161,9 +161,10 @@ kimchi.addNetworkActions = function(network) {
return false;
}
if (!network.persistent) {
+ var confirmMessage = i18n['KCHNET6004M'].replace('%1', '<strong>'+network.name+'</strong>');
var settings = {
title : i18n['KCHAPI6001M'],
- content : i18n['KCHNET6004M'],
+ content : confirmMessage,
confirm : i18n['KCHAPI6002M'],
cancel : i18n['KCHAPI6003M']
};
@@ -180,9 +181,10 @@ kimchi.addNetworkActions = function(network) {
if (network.state === "up" || network.in_use) {
return false;
}
+ var confirmMessage = i18n['KCHNET6002M'].replace('%1', '<strong>'+network.name+'</strong>');
wok.confirm({
title : i18n['KCHAPI6006M'],
- content : i18n['KCHNET6002M'],
+ content : confirmMessage,
confirm : i18n['KCHAPI6002M'],
cancel : i18n['KCHAPI6003M']
}, function() {
diff --git a/ui/js/src/kimchi.storage_main.js b/ui/js/src/kimchi.storage_main.js
index 6c99f93..18e6eea 100644
--- a/ui/js/src/kimchi.storage_main.js
+++ b/ui/js/src/kimchi.storage_main.js
@@ -310,14 +310,15 @@ kimchi.storageBindClick = function() {
$('.pool-delete').on('click', function(event) {
event.preventDefault();
var $pool = $(this);
+ var poolName = $pool.data('name');
+ var confirmMessage = i18n['KCHPOOL6001M'].replace('%1', '<strong>'+poolName+'</strong>');
var settings = {
title : i18n['KCHAPI6001M'],
- content : i18n['KCHPOOL6001M'],
+ content : confirmMessage,
confirm : i18n['KCHAPI6002M'],
cancel : i18n['KCHAPI6003M']
};
wok.confirm(settings, function() {
- var poolName = $pool.data('name');
kimchi.deleteStoragePool(poolName, function() {
kimchi.doListStoragePools();
}, function(err) {
@@ -339,9 +340,10 @@ kimchi.storageBindClick = function() {
$('.pool-deactivate').on('click', function(event) {
event.preventDefault();
var poolName = $(this).data('name');
+ var confirmMessage = i18n['KCHPOOL6012M'].replace('%1', '<strong>'+poolName+'</strong>');
var settings = {
title : i18n['KCHAPI6001M'],
- content : i18n['KCHPOOL6012M'],
+ content : confirmMessage,
confirm : i18n['KCHAPI6002M'],
cancel : i18n['KCHAPI6003M']
};
diff --git a/ui/js/src/kimchi.template_main.js b/ui/js/src/kimchi.template_main.js
index 302d906..cf98a7b 100644
--- a/ui/js/src/kimchi.template_main.js
+++ b/ui/js/src/kimchi.template_main.js
@@ -124,14 +124,15 @@ kimchi.templateBindClick = function() {
$('.template-delete a').on('click', function(event) {
event.preventDefault();
var $template = $(this);
+ var templateName = $template.data('template');
+ var confirmMessage = i18n['KCHTMPL6003M'].replace('%1', '<strong>'+templateName+'</strong>');
var settings = {
title: i18n['KCHAPI6001M'],
- content: i18n['KCHTMPL6003M'],
+ content: confirmMessage,
confirm: i18n['KCHAPI6002M'],
cancel: i18n['KCHAPI6003M']
};
wok.confirm(settings, function() {
- var templateName = $template.data('template');
kimchi.deleteTemplate(templateName, function() {
kimchi.doListTemplates();
}, function(err) {
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index a5185b1..d29a206 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -47,24 +47,24 @@
"KCHTMPL6001W": "$_("No ISO found")",
"KCHTMPL6002M": "$_("This may take a long time. Do you want to continue?")",
- "KCHTMPL6003M": "$_("This will permanently delete the template. Would you like to continue?")",
+ "KCHTMPL6003M": "$_("This will permanently delete the %1 template. Would you like to continue?")",
"KCHTMPL6004M": "$_("View Table")",
"KCHTMPL6005M": "$_("View Gallery")",
"KCHTMPL6006M": "$_("Not Available")",
"KCHTMPL6007M": "$_("Please check the invalid Storage Pools")",
- "KCHVM6001M": "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")",
+ "KCHVM6001M": "$_("This will delete the %1 virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")",
"KCHVM6002M": "$_("Power off Confirmation")",
"KCHVM6003M": "$_("This action may produce undesirable results, "
- "for example unflushed disk cache in the guest. "
+ "for example unflushed disk cache in the %1 guest. "
"Would you like to continue?")",
"KCHVM6004M": "$_("Reset Confirmation")",
"KCHVM6005M": "$_("There is a risk of data loss caused by reset without"
- " the guest OS shutdown. Would you like to continue?")",
+ " the %1 guest OS shutdown. Would you like to continue?")",
"KCHVM6006M": "$_("Shut Down Confirmation")",
- "KCHVM6007M": "$_("Note the guest OS may ignore this request. Would you like to continue?")",
+ "KCHVM6007M": "$_("Note the %1 guest OS may ignore this request. Would you like to continue?")",
"KCHVM6008M": "$_("Virtual Machine delete Confirmation")",
- "KCHVM6009M": "$_("This virtual machine is not persistent. Power Off will delete it. Continue?")",
+ "KCHVM6009M": "$_("The %1 virtual machine is not persistent. Power Off will delete it. Continue?")",
"KCHVM0001E": "$_("Input is not a number")",
"KCHVM0002E": "$_("Memory value cannot be higher than Max Memory value")",
@@ -89,11 +89,11 @@
"KCHVMED6012M": "$_("Following devices will be affected, confirm?")",
"KCHNET6001M": "$_("unavailable")",
- "KCHNET6002M": "$_("This action will interrupt network connectivity for any virtual machine that depend on this network.")",
- "KCHNET6004M": "$_("This network is not persistent. Instead of stop, this action will permanently delete it. Would you like to continue?")",
+ "KCHNET6002M": "$_("This action will interrupt network connectivity for any virtual machine that depend on the %1 network.")",
+ "KCHNET6004M": "$_("The %1 network is not persistent. Instead of stop, this action will permanently delete it. Would you like to continue?")",
"KCHNET6001W": "$_("The bridged VLAN tag may not work well with NetworkManager enabled. You should consider disabling it.")",
- "KCHPOOL6001M": "$_("This will permanently delete the storage pool. Would you like to continue?")",
+ "KCHPOOL6001M": "$_("This will permanently delete the %1 storage pool. Would you like to continue?")",
"KCHPOOL6002M": "$_("This storage pool is empty.")",
"KCHPOOL6003M": "$_("It will format your disk and you will loose any data in there, are you sure to continue? ")",
"KCHPOOL6004M": "$_("SCSI Fibre Channel")",
@@ -108,7 +108,7 @@
"KCHPOOL6006E": "$_("No logical device selected.")",
"KCHPOOL6009E": "$_("This is not a valid Server Name or IP. Please, modify it.")",
"KCHPOOL6011M": "$_("No available partitions found.")",
- "KCHPOOL6012M": "$_("This storage pool is not persistent. Instead of deactivate, this action will permanently delete it. Would you like to continue?")",
+ "KCHPOOL6012M": "$_("The %1 storage pool is not persistent. Instead of deactivate, this action will permanently delete it. Would you like to continue?")",
"KCHPOOL6013M": "$_("Unable to retrieve partitions information.")",
"KCHPOOL6014M": "$_("In progress...")",
"KCHPOOL6015M": "$_("Failed!")",
--
2.7.4
8 years, 2 months
[PATCH v2] [Kimchi] Issue #921: Peers button disappears
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This patch moves peers list from the drop-down on the top navigation bar to a DataTable that will be rendered on Host Dashboard if the user has Gingerbased installed.
Samuel Guimarães (1):
Issue #921: Peers button disappears
root.py | 6 ++
ui/js/kimchi.peers.js | 145 ++++++++++++++++++++++++++++++++++++++++++++++++
ui/js/src/kimchi.api.js | 14 -----
ui/pages/i18n.json.tmpl | 4 +-
4 files changed, 154 insertions(+), 15 deletions(-)
create mode 100644 ui/js/kimchi.peers.js
--
2.5.5
8 years, 2 months
[PATCH] [Kimchi 0/2] Kimchi Issues #606 and #939
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
-Issue #939 requires patch "Minor fixes in form fields" applied in Wok
-Issue #606 requires patch "Icons for Kimchi issue #606" applied in Wok
Samuel Guimarães (2):
Issue #939: [UI] Guest tab is not rendered correctly if guests are not
in 'running' or ''shutoff' state
Issue #606: Change icon to distinguish image generated template and
iso generated template
ui/css/kimchi.css | 169 +++++++++++---------
ui/css/src/modules/_guests.scss | 147 ++++++++++--------
ui/css/src/modules/_iso-list.scss | 2 +-
ui/css/src/modules/_templates.scss | 62 +++++---
ui/js/src/kimchi.template_add_main.js | 5 +
ui/js/src/kimchi.template_edit_main.js | 206 +++++++++++++------------
ui/pages/guest.html.tmpl | 6 +-
ui/pages/template-add.html.tmpl | 2 +-
ui/pages/template-edit.html.tmpl | 272 +++++++++++++++++----------------
9 files changed, 485 insertions(+), 386 deletions(-)
--
2.5.5
8 years, 2 months
[PATCH] [Kimchi 0/3] Peers, paused guests and iso generated template icons
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
Multiple patches for Kimchi:
-Issue #921 v3: Changed container to element generated by script
-Issue #939 v2: Rebased
-Issue #606 v2: Moved icons from Wok to Kimchi (probably won't apply anyway because the SVGs have lines longer than 998 characters)
Samuel Guimarães (3):
Issue #921: Peers button disappears
Issue #939: [UI] Guest tab is not rendered correctly if guests are not
in 'running' or ''shutoff' state
Issue #606: Change icon to distinguish image generated template and
iso generated template
root.py | 6 +
ui/css/kimchi.css | 169 +++++++++++---------
ui/css/src/modules/_guests.scss | 147 ++++++++++--------
ui/css/src/modules/_iso-list.scss | 2 +-
ui/css/src/modules/_templates.scss | 65 +++++---
ui/images/file-o-img.svg | 149 ++++++++++++++++++
ui/images/file-o-iso.svg | 149 ++++++++++++++++++
ui/js/kimchi.peers.js | 140 +++++++++++++++++
ui/js/src/kimchi.api.js | 14 --
ui/js/src/kimchi.template_add_main.js | 5 +
ui/js/src/kimchi.template_edit_main.js | 206 +++++++++++++------------
ui/pages/guest.html.tmpl | 6 +-
ui/pages/i18n.json.tmpl | 4 +-
ui/pages/template-add.html.tmpl | 2 +-
ui/pages/template-edit.html.tmpl | 272 +++++++++++++++++----------------
15 files changed, 935 insertions(+), 401 deletions(-)
create mode 100644 ui/images/file-o-img.svg
create mode 100644 ui/images/file-o-iso.svg
create mode 100644 ui/js/kimchi.peers.js
--
2.5.5
8 years, 2 months
[PATCH v2] [Wok] Removing Kimchi Peers dropdown from Wok navbar
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This patch moves peers list from the drop-down on the top navigation bar to a DataTable that will be rendered on Host Dashboard if the user has Gingerbased installed. Additional patches are necessary (Kimchi and Gingerbase).
Samuel Guimarães (1):
Removing Kimchi Peers dropdown from Wok navbar
ui/js/src/wok.main.js | 29 +----------------------------
1 file changed, 1 insertion(+), 28 deletions(-)
--
2.5.5
8 years, 2 months
[PATCH 0/2] [Wok] Minor fixes in form fields
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
Patches required for issues #606 and #939
Samuel Guimarães (2):
Minor fixes in form fields
Icons for Kimchi issue #606
ui/css/src/modules/_wok-forms.scss | 11 +++
ui/css/wok.css | 11 +++
ui/images/theme-default/file-o-img.svg | 149 +++++++++++++++++++++++++++++++++
ui/images/theme-default/file-o-iso.svg | 149 +++++++++++++++++++++++++++++++++
4 files changed, 320 insertions(+)
create mode 100644 ui/images/theme-default/file-o-img.svg
create mode 100644 ui/images/theme-default/file-o-iso.svg
--
2.5.5
8 years, 2 months
[PATCH] [Wok 0/2] Peers drop-down and minor Wok forms issues
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
v2: Rebased and removed ISO and IMG icons from Wok and moved to Kimchi.
Samuel Guimarães (2):
Removing Kimchi Peers dropdown from Wok navbar
Minor fixes in form fields
ui/css/src/modules/_wok-forms.scss | 11 +++++++++++
ui/css/wok.css | 11 +++++++++++
ui/js/src/wok.main.js | 29 +----------------------------
3 files changed, 23 insertions(+), 28 deletions(-)
--
2.5.5
8 years, 2 months
[PATCH] [Kimchi] /plugins/kimchi/ovsbridges API
by sureshab@linux.vnet.ibm.com
From: Suresh Babu Angadi <sureshab(a)in.ibm.com>
As Per RFC:
[Kimchi-devel] [RFC] listing of ovs bridges
this patch adds new API /plugins/kimchi/ovsbridges
to list ovsbridges
Signed-off-by: Suresh Babu Angadi <sureshab(a)in.ibm.com>
---
control/ovsbridges.py | 29 +++++++++++++++++++++++++++++
docs/API.md | 8 ++++++++
model/ovsbridges.py | 31 +++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+)
create mode 100644 control/ovsbridges.py
create mode 100644 model/ovsbridges.py
diff --git a/control/ovsbridges.py b/control/ovsbridges.py
new file mode 100644
index 0000000..974e954
--- /dev/null
+++ b/control/ovsbridges.py
@@ -0,0 +1,29 @@
+#
+# Project Kimchi
+#
+# Copyright IBM Corp, 2016
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from wok.control.base import SimpleCollection
+from wok.control.utils import UrlSubNode
+
+
+@UrlSubNode("ovsbridges", True)
+class OVSBridges(SimpleCollection):
+ def __init__(self, model):
+ super(OVSBridges, self).__init__(model)
+ self.role_key = 'ovsbridges'
+ self.admin_methods = ['GET']
diff --git a/docs/API.md b/docs/API.md
index 7bd677f..49540b9 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -1028,3 +1028,11 @@ List of available groups.
* **GET**: Return the list of Kimchi peers in the same network
(It uses openSLP for discovering)
+
+### Simple Collection: OVSBridges
+
+**URI:** /plugins/kimchi/ovsbridges
+
+**Methods:**
+
+* **GET**: Return list of OVS bridges of the host.
diff --git a/model/ovsbridges.py b/model/ovsbridges.py
new file mode 100644
index 0000000..212520f
--- /dev/null
+++ b/model/ovsbridges.py
@@ -0,0 +1,31 @@
+#
+# Project Kimchi
+#
+# Copyright IBM Corp, 2016
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from wok.plugins.gingerbase.netinfo import ovs_bridges
+
+
+class OVSBridgesModel(object):
+
+ def get_list(self):
+ """
+
+ Returns: list of ovs bridge names
+
+ """
+ return ovs_bridges()
--
1.8.3.1
8 years, 2 months
[PATCH V2] Issue #992 : Create template on s390x without libvirt storage
by harshalp@linux.vnet.ibm.com
From: Harshal Patil <harshalp(a)linux.vnet.ibm.com>
V1:
This patch adds support for creating templates on s390x arch
without using libvirt related storage calls
V2:
Review comments
Signed-off-by: Harshal Patil <harshalp(a)linux.vnet.ibm.com>
---
docs/API.md | 2 +
i18n.py | 3 ++
model/storagepools.py | 17 ++++++--
model/storagevolumes.py | 1 -
model/templates.py | 27 +++++++++----
model/vms.py | 9 ++++-
osinfo.py | 47 ++++++++++++++++++++--
utils.py | 34 ++++++++++++++++
vmtemplate.py | 105 ++++++++++++++++++++++++++++++------------------
9 files changed, 189 insertions(+), 56 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index 7bd677f..d8d191a 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -427,6 +427,7 @@ A interface represents available network interface on VM.
over current will be used exclusively for memory hotplug
* cdrom: A volume name or URI to an ISO image
* storagepool: URI of the storagepool where template allocates vm storage.
+ * path : Storage path to store virtual disks without libvirt
* networks *(optional)*: list of networks will be assigned to the new VM.
* disks: An array of requested disks with the following optional fields
(either *size* or *volume* must be specified):
@@ -481,6 +482,7 @@ A interface represents available network interface on VM.
* format: Format of the image. Valid formats: qcow, qcow2, qed, raw, vmdk, vpc.
* pool: Storage pool information
* name: URI of the storagepool where template allocates vm disk.
+ * path (optional): Either pool or path to store the virtual disks should be specified
* graphics *(optional)*: A dict of graphics paramenters of this template
* type: The type of graphics. It can be VNC or spice or None.
* vnc: Graphical display using the Virtual Network
diff --git a/i18n.py b/i18n.py
index ea2c4ab..7e60079 100644
--- a/i18n.py
+++ b/i18n.py
@@ -190,6 +190,9 @@ messages = {
"KCHTMPL0031E": _("Memory value (%(mem)sMiB) must be equal or lesser than maximum memory value (%(maxmem)sMiB)"),
"KCHTMPL0032E": _("Unable to update template due error: %(err)s"),
"KCHTMPL0033E": _("Parameter 'disks' requires at least one disk object"),
+ "KCHTMPL0034E": _("Storage without libvirt pool is not supported on this architecture"),
+ "KCHTMPL0035E": _("Error while creating the virtual disk for the guest. Details: %(err)s"),
+ "KCHTMPL0036E": _("When setting template disks without libvirt, following parameters are required: 'index', 'format', 'path', 'size'"),
"KCHPOOL0001E": _("Storage pool %(name)s already exists"),
"KCHPOOL0002E": _("Storage pool %(name)s does not exist"),
diff --git a/model/storagepools.py b/model/storagepools.py
index af92ec9..08e6426 100644
--- a/model/storagepools.py
+++ b/model/storagepools.py
@@ -33,7 +33,7 @@ from wok.plugins.kimchi.model.host import DeviceModel
from wok.plugins.kimchi.model.libvirtstoragepool import StoragePoolDef
from wok.plugins.kimchi.osinfo import defaults as tmpl_defaults
from wok.plugins.kimchi.scan import Scanner
-from wok.plugins.kimchi.utils import pool_name_from_uri
+from wok.plugins.kimchi.utils import pool_name_from_uri, is_s390x
ISO_POOL_NAME = u'kimchi_isos'
@@ -57,6 +57,7 @@ STORAGE_SOURCES = {'netfs': {'addr': '/pool/source/host/@name',
class StoragePoolsModel(object):
+
def __init__(self, **kargs):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
@@ -72,6 +73,13 @@ class StoragePoolsModel(object):
def _check_default_pools(self):
pools = {}
+ if 'disks' not in tmpl_defaults or len(tmpl_defaults['disks']) == 0 \
+ or (not tmpl_defaults.get('disks')[0].get(
+ 'pool') and is_s390x()):
+ # Since libvirt pools are not supported on s390x
+ # No need to check for default pools on s390x
+ return
+ tmpl_defaults.get('disks')
default_pool = tmpl_defaults['disks'][0]['pool']['name']
default_pool = default_pool.split('/')[-1]
@@ -437,9 +445,10 @@ class StoragePoolModel(object):
for tmpl in templates:
t_info = session.get('template', tmpl)
for disk in t_info['disks']:
- t_pool = disk['pool']['name']
- if pool_name_from_uri(t_pool) == pool_name:
- return True
+ if 'pool' in disk:
+ t_pool = disk['pool']['name']
+ if pool_name_from_uri(t_pool) == pool_name:
+ return True
return False
def deactivate(self, name):
diff --git a/model/storagevolumes.py b/model/storagevolumes.py
index 4708674..d721d3b 100644
--- a/model/storagevolumes.py
+++ b/model/storagevolumes.py
@@ -44,7 +44,6 @@ from wok.plugins.kimchi.model.diskutils import set_disk_used_by
from wok.plugins.kimchi.model.storagepools import StoragePoolModel
from wok.plugins.kimchi.utils import get_next_clone_name
-
VOLUME_TYPE_MAP = {0: 'file',
1: 'block',
2: 'directory',
diff --git a/model/templates.py b/model/templates.py
index 8df8c3b..c749f4b 100644
--- a/model/templates.py
+++ b/model/templates.py
@@ -28,13 +28,15 @@ import urlparse
from wok.exception import InvalidOperation, InvalidParameter
from wok.exception import NotFoundError, OperationFailed
-from wok.utils import probe_file_permission_as_user, run_setfacl_set_attr
+from wok.utils import probe_file_permission_as_user
+from wok.utils import run_setfacl_set_attr
from wok.xmlutils.utils import xpath_get_text
from wok.plugins.kimchi.config import get_kimchi_version
from wok.plugins.kimchi.kvmusertests import UserTests
from wok.plugins.kimchi.model.cpuinfo import CPUInfoModel
from wok.plugins.kimchi.utils import is_libvirtd_up, pool_name_from_uri
+from wok.plugins.kimchi.utils import create_disk_image
from wok.plugins.kimchi.vmtemplate import VMTemplate
ISO_TYPE = "ISO 9660 CD-ROM"
@@ -400,15 +402,26 @@ class LibvirtVMTemplate(VMTemplate):
def fork_vm_storage(self, vm_uuid):
# Provision storages:
- vol_list = self.to_volume_list(vm_uuid)
+ disk_and_vol_list = self.to_volume_list(vm_uuid)
try:
- for v in vol_list:
- pool = self._get_storage_pool(v['pool'])
- # outgoing text to libvirt, encode('utf-8')
- pool.createXML(v['xml'].encode('utf-8'), 0)
+ for v in disk_and_vol_list:
+ if v['pool'] is not None:
+ pool = self._get_storage_pool(v['pool'])
+ # outgoing text to libvirt, encode('utf-8')
+ pool.createXML(v['xml'].encode('utf-8'), 0)
+ else:
+ capacity = v['capacity']
+ format_type = v['format']
+ path = v['path']
+ create_disk_image(
+ format_type=format_type,
+ path=path,
+ capacity=capacity)
+
except libvirt.libvirtError as e:
raise OperationFailed("KCHVMSTOR0008E", {'error': e.message})
- return vol_list
+
+ return disk_and_vol_list
def set_cpu_info(self):
# undefined topology: consider these values to calculate maxvcpus
diff --git a/model/vms.py b/model/vms.py
index 7f607f5..3ad722e 100644
--- a/model/vms.py
+++ b/model/vms.py
@@ -61,7 +61,7 @@ from wok.plugins.kimchi.model.utils import remove_metadata_node
from wok.plugins.kimchi.model.utils import set_metadata_node
from wok.plugins.kimchi.osinfo import defaults, MEM_DEV_SLOTS
from wok.plugins.kimchi.screenshot import VMScreenshot
-from wok.plugins.kimchi.utils import get_next_clone_name
+from wok.plugins.kimchi.utils import get_next_clone_name, is_s390x
from wok.plugins.kimchi.utils import template_name_from_uri
from wok.plugins.kimchi.xmlutils.bootorder import get_bootorder_node
from wok.plugins.kimchi.xmlutils.bootorder import get_bootmenu_node
@@ -1395,6 +1395,13 @@ class VMModel(object):
except libvirt.libvirtError as e:
wok_log.error('Unable to get storage volume by path: %s' %
e.message)
+ try:
+ if is_s390x() and os.path.exists(path):
+ os.remove(path)
+ except Exception as e:
+ wok_log.error('Unable to delete storage path: %s' %
+ e.message)
+
except Exception as e:
raise OperationFailed('KCHVOL0017E', {'err': e.message})
diff --git a/osinfo.py b/osinfo.py
index 528cf14..d60cc15 100644
--- a/osinfo.py
+++ b/osinfo.py
@@ -158,6 +158,12 @@ def _get_tmpl_defaults():
'maxmemory': _get_default_template_mem()}
tmpl_defaults['storage']['disk.0'] = {'size': 10, 'format': 'qcow2',
'pool': 'default'}
+ is_on_s390x = True if _get_arch() == 's390x' else False
+
+ if is_on_s390x:
+ tmpl_defaults['storage']['disk.0']['path'] = '/var/lib/libvirt/images/'
+ del tmpl_defaults['storage']['disk.0']['pool']
+
tmpl_defaults['processor']['vcpus'] = 1
tmpl_defaults['processor']['maxvcpus'] = 1
tmpl_defaults['graphics'] = {'type': 'vnc', 'listen': '127.0.0.1'}
@@ -165,7 +171,12 @@ def _get_tmpl_defaults():
default_config = ConfigObj(tmpl_defaults)
# Load template configuration file
- config_file = os.path.join(kimchiPaths.sysconf_dir, 'template.conf')
+ if is_on_s390x:
+ config_file = os.path.join(
+ kimchiPaths.sysconf_dir,
+ 'template_s390x.conf')
+ else:
+ config_file = os.path.join(kimchiPaths.sysconf_dir, 'template.conf')
config = ConfigObj(config_file)
# Merge default configuration with file configuration
@@ -186,11 +197,39 @@ def _get_tmpl_defaults():
# Parse storage section to get disks values
storage_section = default_config.pop('storage')
defaults['disks'] = []
- for disk in storage_section.keys():
+
+ pool_exists = False
+ path_exists = False
+ for index, disk in enumerate(storage_section.keys()):
data = storage_section[disk]
data['index'] = int(disk.split('.')[1])
- data['pool'] = {"name": '/plugins/kimchi/storagepools/' +
- storage_section[disk].pop('pool')}
+ # Right now 'Path' is only supported on s390x
+ if storage_section[disk].get('path') and is_on_s390x:
+ path_exists = True
+ data['path'] = storage_section[disk].pop('path')
+ if 'size' not in storage_section[disk]:
+ data['size'] = tmpl_defaults['storage']['disk.0']['size']
+ else:
+ data['size'] = storage_section[disk].pop('size')
+
+ if 'format' not in storage_section[disk]:
+ data['format'] = tmpl_defaults['storage']['disk.0']['format']
+ else:
+ data['format'] = storage_section[disk].pop('format')
+ else:
+ storage_section[disk].get('pool')
+ pool_exists = True
+ data['pool'] = {"name": '/plugins/kimchi/storagepools/' +
+ storage_section[disk].pop('pool')}
+
+ # If both pool and path don't exist, pick the defaults
+ if index == len(storage_section.keys()) - 1 and \
+ (not pool_exists and not path_exists):
+ if is_on_s390x: # Special handling for s390x
+ data['path'] = tmpl_defaults['storage']['disk.0']['path']
+ else:
+ data['pool'] = {"name": '/plugins/kimchi/storagepools/default'}
+
defaults['disks'].append(data)
# Parse processor section to get vcpus and cpu_topology values
diff --git a/utils.py b/utils.py
index 26d3cf6..0fca191 100644
--- a/utils.py
+++ b/utils.py
@@ -24,6 +24,7 @@ import platform
import re
import sqlite3
import time
+import os
import urllib2
from httplib import HTTPConnection, HTTPException
from urlparse import urlparse
@@ -31,6 +32,7 @@ from urlparse import urlparse
from wok.exception import InvalidParameter, OperationFailed
from wok.plugins.kimchi import config
from wok.plugins.kimchi.osinfo import get_template_default
+from wok.stringutils import encode_value
from wok.utils import run_command, wok_log
from wok.xmlutils.utils import xpath_get_text
@@ -272,3 +274,35 @@ def is_libvirtd_up():
output, error, rc = run_command(cmd, silent=True)
return True if output == 'active\n' else False
+
+
+def is_s390x():
+ """
+ Check if current arch is 's390x'
+ Returns:
+ """
+ if os.uname()[4] == 's390x':
+ return True
+
+ return False
+
+
+def create_disk_image(format_type, path, capacity):
+ """
+ Create a disk image for the Guest
+ Args:
+ format: Format of the storage. e.g. qcow2
+ path: Path where the virtual disk will be created
+ capacity: Capacity of the virtual disk in GBs
+
+ Returns:
+
+ """
+ out, err, rc = run_command(
+ ["/usr/bin/qemu-img", "create", "-f", format_type, "-o",
+ "preallocation=metadata", path, encode_value(capacity) + "G"])
+
+ if rc != 0:
+ raise OperationFailed("KCHTMPL0035E", {'err': err})
+
+ return
\ No newline at end of file
diff --git a/vmtemplate.py b/vmtemplate.py
index babf050..3b9eb32 100644
--- a/vmtemplate.py
+++ b/vmtemplate.py
@@ -31,7 +31,8 @@ from wok.exception import MissingParameter, OperationFailed
from wok.plugins.kimchi import imageinfo
from wok.plugins.kimchi import osinfo
from wok.plugins.kimchi.isoinfo import IsoImage
-from wok.plugins.kimchi.utils import check_url_path, pool_name_from_uri
+from wok.plugins.kimchi.utils import check_url_path, is_s390x
+from wok.plugins.kimchi.utils import pool_name_from_uri
from wok.plugins.kimchi.xmlutils.bootorder import get_bootorder_xml
from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml
from wok.plugins.kimchi.xmlutils.disk import get_disk_xml
@@ -42,6 +43,7 @@ from wok.plugins.kimchi.xmlutils.serial import get_serial_xml
class VMTemplate(object):
+
def __init__(self, args, scan=False, netboot=False):
"""
Construct a VM Template from a widely variable amount of information.
@@ -95,35 +97,49 @@ class VMTemplate(object):
disks = self.info.get('disks')
basic_disk = ['index', 'format', 'pool', 'size']
+ basic_path_disk = ['index', 'format', 'path', 'size']
ro_disk = ['index', 'format', 'pool', 'volume']
base_disk = ['index', 'base', 'pool', 'size', 'format']
for index, disk in enumerate(disks):
disk_info = dict(default_disk)
-
- pool = disk.get('pool', default_disk['pool'])
- pool_type = self._get_storage_type(pool['name'])
-
- if pool_type in ['iscsi', 'scsi']:
- disk_info = {'index': 0, 'format': 'raw', 'volume': None}
-
- disk_info.update(disk)
- pool_name = disk_info.get('pool', {}).get('name')
- if pool_name is None:
- raise MissingParameter('KCHTMPL0028E')
-
- keys = sorted(disk_info.keys())
- if ((keys != sorted(basic_disk)) and (keys != sorted(ro_disk)) and
- (keys != sorted(base_disk))):
- raise MissingParameter('KCHTMPL0028E')
-
- if pool_type in ['logical', 'iscsi', 'scsi']:
- if disk_info['format'] != 'raw':
- raise InvalidParameter('KCHTMPL0029E')
-
- disk_info['pool']['type'] = pool_type
- disk_info['index'] = disk_info.get('index', index)
- self.info['disks'][index] = disk_info
+ if disk.get('pool'):
+ pool = disk.get('pool', default_disk.get('pool'))
+ pool_type = self._get_storage_type(pool['name'])
+ if pool_type in ['iscsi', 'scsi']:
+ disk_info = {'index': 0, 'format': 'raw', 'volume': None}
+
+ disk_info.update(disk)
+ pool_name = disk_info.get('pool', {}).get('name')
+ if pool_name is None:
+ raise MissingParameter('KCHTMPL0028E')
+
+ keys = sorted(disk_info.keys())
+ if ((keys != sorted(basic_disk)) and
+ (keys != sorted(ro_disk)) and
+ (keys != sorted(base_disk)) and
+ (keys != basic_path_disk)):
+ raise MissingParameter('KCHTMPL0028E')
+
+ if pool_type in ['logical', 'iscsi', 'scsi']:
+ if disk_info['format'] != 'raw':
+ raise InvalidParameter('KCHTMPL0029E')
+
+ disk_info['pool']['type'] = pool_type
+ disk_info['index'] = disk_info.get('index', index)
+ self.info['disks'][index] = disk_info
+ elif is_s390x():
+ # For now support 'path' only on s390x
+ path = disk.get('path', default_disk['path'])
+ disk_info.update(disk)
+ keys = sorted(disk_info.keys())
+ if keys != sorted(basic_path_disk):
+ raise MissingParameter('KCHTMPL0036E')
+ disk_info['path'] = path
+ disk_info['index'] = disk_info.get('index', index)
+ self.info['disks'][index] = disk_info
+ else:
+ raise InvalidParameter('KCHTMPL0034E')
def _get_os_info(self, args, scan):
distro = version = 'unknown'
@@ -217,8 +233,9 @@ class VMTemplate(object):
params = dict(base_disk_params)
params['format'] = disk['format']
params['index'] = index
- params.update(locals().get('%s_disk_params' %
- disk['pool']['type'], {}))
+ if disk.get('pool'):
+ params.update(locals().get('%s_disk_params' %
+ disk['pool']['type'], {}))
volume = disk.get('volume')
if volume is not None:
@@ -226,9 +243,13 @@ class VMTemplate(object):
volume)
else:
img = "%s-%s.img" % (vm_uuid, params['index'])
- storage_path = self._get_storage_path(disk['pool']['name'])
+ if disk.get('pool'):
+ storage_path = self._get_storage_path(disk['pool']['name'])
+ params['pool_type'] = disk['pool']['type']
+ elif disk.get('path'):
+ storage_path = disk.get('path')
+ params['pool_type'] = None
params['path'] = os.path.join(storage_path, img)
- params['pool_type'] = disk['pool']['type']
disks_xml += get_disk_xml(params)[1]
return unicode(disks_xml, 'utf-8')
@@ -237,20 +258,24 @@ class VMTemplate(object):
ret = []
for i, d in enumerate(self.info['disks']):
# Create only .img. If storagepool is (i)SCSI, volumes will be LUNs
- if d['pool']['type'] in ["iscsi", "scsi"]:
+ if 'pool' in d and d['pool']['type'] in ["iscsi", "scsi"]:
continue
index = d.get('index', i)
volume = "%s-%s.img" % (vm_uuid, index)
- storage_path = self._get_storage_path(d['pool']['name'])
+ if 'path' in d:
+ storage_path = d['path']
+ else:
+ storage_path = self._get_storage_path(d['pool']['name'])
+
info = {'name': volume,
'capacity': d['size'],
'format': d['format'],
'path': '%s/%s' % (storage_path, volume),
- 'pool': d['pool']['name']}
+ 'pool': d['pool']['name'] if 'pool' in d else None}
- if 'logical' == d['pool']['type'] or \
+ if ('pool' in d and 'logical' == d['pool']['type']) or \
info['format'] not in ['qcow2', 'raw']:
info['allocation'] = info['capacity']
else:
@@ -447,8 +472,9 @@ class VMTemplate(object):
def validate(self):
for disk in self.info.get('disks'):
- pool_uri = disk.get('pool', {}).get('name')
- self._get_storage_pool(pool_uri)
+ if 'pool' in disk:
+ pool_uri = disk.get('pool', {}).get('name')
+ self._get_storage_pool(pool_uri)
self._network_validate()
self._iso_validate()
self.cpuinfo_validate()
@@ -499,10 +525,11 @@ class VMTemplate(object):
# validate storagepools and image-based templates integrity
for disk in self.info['disks']:
- pool_uri = disk['pool']['name']
- pool_name = pool_name_from_uri(pool_uri)
- if pool_name not in self._get_active_storagepools_name():
- invalid['storagepools'] = [pool_name]
+ if 'pool' in disk:
+ pool_uri = disk['pool']['name']
+ pool_name = pool_name_from_uri(pool_uri)
+ if pool_name not in self._get_active_storagepools_name():
+ invalid['storagepools'] = [pool_name]
if disk.get("base") is None:
continue
--
1.9.1
8 years, 2 months