[PATCH] UI: Attach/Replace/Detach a CDROM to/in/from a VM
by Hongliang Wang
Allow user to manipulate CDROM(s) in a VM:
1) Attach one or more CDROM(s);
2) Replace a CDROM with another ISO file path;
3) Detach a CDROM from a VM.
Tested on Chrome.
Signed-off-by: Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
---
ui/css/theme-default/guest-cdrom-add.css | 59 +++++++++++++++++
ui/css/theme-default/guest-cdrom-edit.css | 59 +++++++++++++++++
ui/css/theme-default/guest-edit.css | 90 ++++++++++++++++++++++++--
ui/images/theme-default/guest-icon-sprite.png | Bin 0 -> 6748 bytes
ui/js/src/kimchi.api.js | 68 +++++++++++++++++++
ui/js/src/kimchi.guest_cdrom_add_main.js | 82 +++++++++++++++++++++++
ui/js/src/kimchi.guest_cdrom_edit_main.js | 90 ++++++++++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 58 ++++++++++++++++-
ui/pages/guest-cdrom-add.html.tmpl | 76 ++++++++++++++++++++++
ui/pages/guest-cdrom-edit.html.tmpl | 76 ++++++++++++++++++++++
ui/pages/guest-edit.html.tmpl | 34 ++++++++++
ui/pages/i18n.html.tmpl | 5 ++
12 files changed, 690 insertions(+), 7 deletions(-)
create mode 100644 ui/css/theme-default/guest-cdrom-add.css
create mode 100644 ui/css/theme-default/guest-cdrom-edit.css
create mode 100644 ui/images/theme-default/guest-icon-sprite.png
create mode 100644 ui/js/src/kimchi.guest_cdrom_add_main.js
create mode 100644 ui/js/src/kimchi.guest_cdrom_edit_main.js
create mode 100644 ui/pages/guest-cdrom-add.html.tmpl
create mode 100644 ui/pages/guest-cdrom-edit.html.tmpl
diff --git a/ui/css/theme-default/guest-cdrom-add.css b/ui/css/theme-default/guest-cdrom-add.css
new file mode 100644
index 0000000..622450e
--- /dev/null
+++ b/ui/css/theme-default/guest-cdrom-add.css
@@ -0,0 +1,59 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+#guest-cdrom-add-window {
+ font-size: 13px;
+ height: 600px;
+ width: 700px;
+}
+
+.guest-cdrom-add-fieldset {
+ padding: 1em;
+}
+
+#guest-cdrom-add-window input[type="text"] {
+ font-size: 16px;
+ height: 38px;
+ background: #fff;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ box-shadow: 2px 2px 2px #eee inset;
+ border-top: 1px solid #bbb;
+ border-left: 1px solid #bbb;
+ padding-left: 10px;
+ width: 600px;
+}
+
+#cdrom-error-message {
+ color: red;
+ padding: 5px;
+}
+
+#vm-cdrom-button-add[disabled] {
+ background: #c0c0c0;
+ color: #ddd;
+ padding-left: 26px;
+}
+
+#vm-cdrom-button-add.loading[disabled] {
+ background: url("../../images/theme-default/loading.gif") 7px center no-repeat #c0c0c0;
+ color: #ddd;
+ padding-left: 26px;
+}
diff --git a/ui/css/theme-default/guest-cdrom-edit.css b/ui/css/theme-default/guest-cdrom-edit.css
new file mode 100644
index 0000000..7014577
--- /dev/null
+++ b/ui/css/theme-default/guest-cdrom-edit.css
@@ -0,0 +1,59 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+#guest-cdrom-edit-window {
+ font-size: 13px;
+ height: 600px;
+ width: 700px;
+}
+
+.guest-cdrom-edit-fieldset {
+ padding: 1em;
+}
+
+#guest-cdrom-edit-window input[type="text"] {
+ font-size: 16px;
+ height: 38px;
+ background: #fff;
+ -webkit-border-radius: 5px;
+ border-radius: 5px;
+ box-shadow: 2px 2px 2px #eee inset;
+ border-top: 1px solid #bbb;
+ border-left: 1px solid #bbb;
+ padding-left: 10px;
+ width: 600px;
+}
+
+#cdrom-error-message {
+ color: red;
+ padding: 5px;
+}
+
+#vm-cdrom-button-edit[disabled] {
+ background: #c0c0c0;
+ color: #ddd;
+ padding-left: 26px;
+}
+
+#vm-cdrom-button-edit.loading[disabled] {
+ background: url("../../images/theme-default/loading.gif") 7px center no-repeat #c0c0c0;
+ color: #ddd;
+ padding-left: 26px;
+}
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index 79fca83..aff262a 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -20,13 +20,14 @@
*/
#guest-edit-window {
font-size: 13px;
- height: 380px;
- width: 420px;
+ height: 320px;
+ width: 1000px;
}
.guest-edit-fieldset {
float: left;
padding: 1em;
+ padding-right: 0;
}
.guest-edit-wrapper-label, .guest-edit-wrapper-controls {
@@ -40,16 +41,20 @@
}
.guest-edit-wrapper-label {
- width: 10em;
+ height: 38px;
+ line-height: 38px;
+ margin-top: 5px;
+ vertical-align: top;
+ width: 80px;
}
.guest-edit-wrapper-controls {
- width: 18em;
+ width: 392px;
}
.guest-edit-wrapper-controls input[type="text"] {
+ font-size: 16px;
height: 38px;
- line-height: 38px;
background: #fff;
-webkit-border-radius: 5px;
border-radius: 5px;
@@ -57,7 +62,7 @@
border-top: 1px solid #bbb;
border-left: 1px solid #bbb;
padding-left: 10px;
- width: 100%;
+ width: 300px;
}
.guest-edit-wrapper-controls input[type="text"][disabled] {
@@ -65,3 +70,76 @@
background-color: #fafafa;
cursor: not-allowed;
}
+
+#guest-edit-cdrom-row-container {
+ max-height: 146px;
+ overflow: auto;
+}
+
+.guest-edit-cdrom-button {
+ background: url("../images/theme-default/guest-icon-sprite.png") no-repeat left top transparent;
+ border: none;
+ height: 25px;
+ vertical-align: middle;
+ width: 25px;
+}
+
+.guest-edit-cdrom-button.attach {
+ margin-top: 5px;
+}
+
+.guest-edit-cdrom-button.attach:hover {
+ background-position: 0 -27px;
+}
+
+.guest-edit-cdrom-button.attach:focus {
+ background-position: 0 -54px;
+}
+
+.guest-edit-cdrom-button.attach:active {
+ background-position: 0 -81px;
+}
+
+.guest-edit-cdrom-button.attach[disabled] {
+ background-position: 0 -108px;
+}
+
+.guest-edit-cdrom-button.replace {
+ background-position: -27px 0;
+}
+
+.guest-edit-cdrom-button.replace:hover {
+ background-position: -27px -27px;
+}
+
+.guest-edit-cdrom-button.replace:focus {
+ background-position: -27px -54px;
+}
+
+.guest-edit-cdrom-button.replace:active {
+ background-position: -27px -81px;
+}
+
+.guest-edit-cdrom-button.replace[disabled] {
+ background-position: -27px -108px;
+}
+
+.guest-edit-cdrom-button.detach {
+ background-position: -54px 0;
+}
+
+.guest-edit-cdrom-button.detach:hover {
+ background-position: -54px -27px;
+}
+
+.guest-edit-cdrom-button.detach:focus {
+ background-position: -54px -54px;
+}
+
+.guest-edit-cdrom-button.detach:active {
+ background-position: -54px -81px;
+}
+
+.guest-edit-cdrom-button.detach[disabled] {
+ background-position: -54px -108px;
+}
diff --git a/ui/images/theme-default/guest-icon-sprite.png b/ui/images/theme-default/guest-icon-sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..81edb5945d3bd204313a6d53b55cecf17ee97679
GIT binary patch
literal 6748
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
Bz#9Mn
literal 0
HcmV?d00001
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 6433fe0..52ecdd8 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -731,5 +731,73 @@ var kimchi = {
success : suc,
error : err
});
+ },
+
+ addVMStorage : function(settings, suc, err) {
+ var vm = encodeURIComponent(settings['vm']);
+ delete settings['vm'];
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/' + vm + '/storages',
+ type : 'POST',
+ contentType : 'application/json',
+ data : JSON.stringify(settings),
+ dataType : 'json',
+ success : suc,
+ error : err
+ });
+ },
+
+ retrieveVMStorage : function(settings, suc, err) {
+ var vm = encodeURIComponent(settings['vm']);
+ var dev = encodeURIComponent(settings['dev']);
+ kimchi.requestJSON({
+ url : kimchi.url + "vms/" + vm + '/storages/' + dev,
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ success: suc,
+ error: err
+ });
+ },
+
+ replaceVMStorage : function(settings, suc, err) {
+ var vm = encodeURIComponent(settings['vm']);
+ var dev = encodeURIComponent(settings['dev']);
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/' + vm + '/storages/' + dev,
+ type : 'PUT',
+ contentType : 'application/json',
+ data : JSON.stringify({
+ path: settings['path']
+ }),
+ dataType : 'json',
+ success : suc,
+ error : err
+ });
+ },
+
+ deleteVMStorage : function(settings, suc, err) {
+ var vm = settings['vm'];
+ var dev = settings['dev'];
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/' + encodeURIComponent(vm) +
+ '/storages/' + encodeURIComponent(dev),
+ type : 'DELETE',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err
+ });
+ },
+
+ listVMStorages : function(vm, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/' + encodeURIComponent(vm) + '/storages',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err
+ });
}
};
diff --git a/ui/js/src/kimchi.guest_cdrom_add_main.js b/ui/js/src/kimchi.guest_cdrom_add_main.js
new file mode 100644
index 0000000..8b19a57
--- /dev/null
+++ b/ui/js/src/kimchi.guest_cdrom_add_main.js
@@ -0,0 +1,82 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+kimchi.guest_cdrom_add_main = function() {
+ var cdromAddForm = $('#form-vm-cdrom-add');
+ var submitButton = $('#vm-cdrom-button-add');
+ var nameTextbox = $('input[name="dev"]', cdromAddForm);
+ var typeTextbox = $('input[name="type"]', cdromAddForm);
+ var pathTextbox = $('input[name="path"]', cdromAddForm);
+ var errorMessage = $('#cdrom-error-message');
+
+ var submitForm = function(event) {
+ if(submitButton.prop('disabled')) {
+ return false;
+ }
+
+ var dev = nameTextbox.val();
+ var type = typeTextbox.val();
+ var path = pathTextbox.val();
+ if(!path || path === '') {
+ return false;
+ }
+
+ var formData = cdromAddForm.serializeObject();
+ $.each([submitButton, nameTextbox, pathTextbox], function(i, c) {
+ $(c).prop('disabled', true);
+ });
+ $(submitButton).addClass('loading').text(i18n['KCHVM6004M']);
+ $(errorMessage).text('');
+
+ var settings = {
+ vm: kimchi.selectedGuest,
+ type: type,
+ path: path
+ };
+
+ if(dev && dev !== '') {
+ settings['dev'] = dev;
+ }
+
+ kimchi.addVMStorage(settings, function(result) {
+ kimchi.window.close();
+ kimchi.topic('kimchi/vmCDROMAdded').publish({
+ result: result
+ });
+ }, function(result) {
+ var errText = result['reason'] ||
+ result['responseJSON']['reason'];
+ $(errorMessage).text(errText);
+
+ $.each([submitButton, nameTextbox, pathTextbox], function(i, c) {
+ $(c).prop('disabled', false);
+ });
+ $(submitButton).removeClass('loading').text(i18n['KCHVM6003M']);
+ });
+
+ event.preventDefault();
+ };
+
+ cdromAddForm.on('submit', submitForm);
+ submitButton.on('click', submitForm);
+ pathTextbox.on('input propertychange', function(event) {
+ $(submitButton).prop('disabled', $(this).val() === '');
+ });
+};
diff --git a/ui/js/src/kimchi.guest_cdrom_edit_main.js b/ui/js/src/kimchi.guest_cdrom_edit_main.js
new file mode 100644
index 0000000..76ab459
--- /dev/null
+++ b/ui/js/src/kimchi.guest_cdrom_edit_main.js
@@ -0,0 +1,90 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ */
+kimchi.guest_cdrom_edit_main = function() {
+ var cdromEditForm = $('#form-vm-cdrom-edit');
+ var submitButton = $('#vm-cdrom-button-edit');
+ var nameTextbox = $('input[name="dev"]', cdromEditForm);
+ var typeTextbox = $('input[name="type"]', cdromEditForm);
+ var pathTextbox = $('input[name="path"]', cdromEditForm);
+ var errorMessage = $('#cdrom-error-message');
+ var originalPath = null;
+
+ kimchi.retrieveVMStorage({
+ vm: kimchi.selectedGuest,
+ dev: kimchi.selectedGuestStorage
+ }, function(storage) {
+ for(var prop in storage) {
+ $('input[name="' + prop + '"]', cdromEditForm).val(storage[prop]);
+ }
+
+ originalPath = storage['path'];
+ });
+
+ var submitForm = function(event) {
+ if(submitButton.prop('disabled')) {
+ return false;
+ }
+
+ var path = pathTextbox.val();
+ if(!path || path === '') {
+ return false;
+ }
+
+ $.each([submitButton, nameTextbox, pathTextbox], function(i, c) {
+ $(c).prop('disabled', true);
+ });
+ $(submitButton).addClass('loading').text(i18n['KCHVM6006M']);
+ $(errorMessage).text('');
+
+ var settings = {
+ vm: kimchi.selectedGuest,
+ dev: kimchi.selectedGuestStorage,
+ path: path
+ };
+
+ kimchi.replaceVMStorage(settings, function(result) {
+ kimchi.window.close();
+ kimchi.topic('kimchi/vmCDROMReplaced').publish({
+ result: result
+ });
+ }, function(result) {
+ var errText = result['reason'] ||
+ result['responseJSON']['reason'];
+ $(errorMessage).text(errText);
+
+ $.each([submitButton, nameTextbox, pathTextbox], function(i, c) {
+ $(c).prop('disabled', false);
+ });
+ $(submitButton).removeClass('loading').text(i18n['KCHVM6005M']);
+ });
+
+ event.preventDefault();
+ };
+
+ cdromEditForm.on('submit', submitForm);
+ submitButton.on('click', submitForm);
+ pathTextbox.on('input propertychange', function(event) {
+ var invalid =
+ ($(this).val() === originalPath) ||
+ ($(this).val() === '');
+ $(submitButton).prop('disabled', invalid);
+ });
+};
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 13ea9d0..1244dd0 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -21,14 +21,69 @@
kimchi.guest_edit_main = function() {
var guestEditForm = $('#form-guest-edit');
var saveButton = $('#guest-edit-button-save');
+
+ var refreshCDROMs = function() {
+ kimchi.listVMStorages(kimchi.selectedGuest, function(storages) {
+ var rowHTML = $('#cdrom-row-tmpl').html();
+ var container = $('#guest-edit-cdrom-row-container');
+ $(container).empty();
+
+ $.each(storages, function(index, storage) {
+ if(storage['type'] !== 'cdrom') {
+ return;
+ }
+
+ storage['vm'] = kimchi.selectedGuest;
+ var templated = kimchi.template(rowHTML, storage);
+ container.append(templated);
+ });
+
+ $('.replace', container).on('click', function(e) {
+ event.preventDefault();
+ kimchi.selectedGuestStorage = $(this).data('dev');
+ kimchi.window.open("guest-cdrom-edit.html");
+ });
+
+ $('.detach', container).on('click', function(e) {
+ event.preventDefault();
+ var settings = {
+ title : i18n['KCHAPI6004M'],
+ content : i18n['KCHVM6002M'],
+ confirm : i18n['KCHAPI6002M'],
+ cancel : i18n['KCHAPI6003M']
+ };
+
+ var dev = $(this).data('dev');
+ kimchi.confirm(settings, function() {
+ kimchi.deleteVMStorage({
+ vm: kimchi.selectedGuest,
+ dev: dev
+ }, function() {
+ refreshCDROMs();
+ });
+ });
+ });
+ });
+ };
+
kimchi.retrieveVM(kimchi.selectedGuest, function(guest) {
guest['icon'] = guest['icon'] || 'images/icon-vm.png';
for ( var prop in guest) {
$('input[name="' + prop + '"]', guestEditForm).val(guest[prop]);
}
+
+ refreshCDROMs();
+
+ $('#guest-edit-attach-cdrom-button').on('click', function(event) {
+ event.preventDefault();
+ kimchi.window.open("guest-cdrom-add.html");
+ });
+
+ kimchi.topic('kimchi/vmCDROMAdded').subscribe(refreshCDROMs);
+ kimchi.topic('kimchi/vmCDROMReplaced').subscribe(refreshCDROMs);
});
- $('#guest-edit-button-cancel').on('click', function() {
+ $('#guest-edit-button-cancel').on('click', function(event) {
kimchi.window.close();
});
@@ -45,6 +100,7 @@ kimchi.guest_edit_main = function() {
}, function(err) {
kimchi.message.error(err.responseJSON.reason);
});
+
event.preventDefault();
};
diff --git a/ui/pages/guest-cdrom-add.html.tmpl b/ui/pages/guest-cdrom-add.html.tmpl
new file mode 100644
index 0000000..65663a9
--- /dev/null
+++ b/ui/pages/guest-cdrom-add.html.tmpl
@@ -0,0 +1,76 @@
+#*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ *#
+#unicode UTF-8
+#import gettext
+#from kimchi.cachebust import href
+#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang)
+#silent _ = t.gettext
+#silent _t = t.gettext
+<div id="guest-cdrom-add-window" class="window">
+ <header>
+ <h1 class="title">$_("Attach a CDROM to VM")</h1>
+ <div class="close">X</div>
+ </header>
+ <div class="content">
+ <form id="form-vm-cdrom-add">
+ <section class="form-section">
+ <h2>1. $_("Device Name")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The name used to identify the CDROM. If omitted, a name will be chosen automatically.")
+ </p>
+ <input type="text" class="text" name="dev" />
+ </div>
+ </section>
+ <section class="form-section">
+ <h2>2. $_("Device Type")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The device type. Currently, only \"cdrom\" is supported.")
+ </p>
+ <input type="text" class="text" name="type" value="cdrom" readonly />
+ </div>
+ </section>
+ <section class="form-section">
+ <h2>3. $_("ISO File Path")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The ISO file path in the server.")
+ </p>
+ <input type="text" class="text" name="path" />
+ </div>
+ </section>
+ <section class="form-section">
+ <div id="cdrom-error-message"></div>
+ </section>
+ </form>
+ </div>
+ <footer>
+ <div class="btn-group">
+ <button id="vm-cdrom-button-add" class="btn-normal" disabled="disabled">
+ <span class="text">$_("Attach")</span>
+ </button>
+ </div>
+ </footer>
+</div>
+<script type="text/javascript">
+ kimchi.guest_cdrom_add_main();
+</script>
diff --git a/ui/pages/guest-cdrom-edit.html.tmpl b/ui/pages/guest-cdrom-edit.html.tmpl
new file mode 100644
index 0000000..1af7e7e
--- /dev/null
+++ b/ui/pages/guest-cdrom-edit.html.tmpl
@@ -0,0 +1,76 @@
+#*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013
+ *
+ * Authors:
+ * Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
+ *
+ * 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.
+ *#
+#unicode UTF-8
+#import gettext
+#from kimchi.cachebust import href
+#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang)
+#silent _ = t.gettext
+#silent _t = t.gettext
+<div id="guest-cdrom-edit-window" class="window">
+ <header>
+ <h1 class="title">$_("Replace a CDROM of VM")</h1>
+ <div class="close">X</div>
+ </header>
+ <div class="content">
+ <form id="form-vm-cdrom-edit">
+ <section class="form-section">
+ <h2>1. $_("Device Name")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The name used to identify the CDROM. Read-only.")
+ </p>
+ <input type="text" class="text" name="dev" readonly />
+ </div>
+ </section>
+ <section class="form-section">
+ <h2>2. $_("Device Type")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The device type. Currently, only \"cdrom\" is supported.")
+ </p>
+ <input type="text" class="text" name="type" value="cdrom" readonly />
+ </div>
+ </section>
+ <section class="form-section">
+ <h2>3. $_("ISO File Path")</h2>
+ <div class="field">
+ <p class="text-help">
+ $_("The ISO file path in the server.")
+ </p>
+ <input type="text" class="text" name="path" />
+ </div>
+ </section>
+ <section class="form-section">
+ <div id="cdrom-error-message"></div>
+ </section>
+ </form>
+ </div>
+ <footer>
+ <div class="btn-group">
+ <button id="vm-cdrom-button-edit" class="btn-normal" disabled="disabled">
+ <span class="text">$_("Replace")</span>
+ </button>
+ </div>
+ </footer>
+</div>
+<script type="text/javascript">
+ kimchi.guest_cdrom_edit_main();
+</script>
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 91f780c..dadff21 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -86,6 +86,24 @@
</div>
</div>
</fieldset>
+ <fieldset class="guest-edit-fieldset">
+ <div>
+ <div class="guest-edit-wrapper-label">
+ <label>
+ $_("CDROM")
+ </label>
+ </div>
+ <div class="guest-edit-wrapper-controls">
+ <div id="guest-edit-cdrom-row-container"></div>
+ <div>
+ <button id="guest-edit-attach-cdrom-button"
+ class="guest-edit-cdrom-button attach"
+ title="$_("Attach")">
+ </button>
+ </div>
+ </div>
+ </div>
+ </fieldset>
</form>
</div>
<footer>
@@ -99,6 +117,22 @@
</div>
</footer>
</div>
+<script id="cdrom-row-tmpl" type="text/html">
+<div>
+ <input name="cdrom" type="text"
+ data-vm="{vm}" data-dev="{dev}"
+ value="{path}" readonly="readonly" />
+ <button class="guest-edit-cdrom-button replace"
+ data-vm="{vm}" data-dev="{dev}"
+ title="$_("Replace")">
+ </button>
+ <button class="guest-edit-cdrom-button detach"
+ data-vm="{vm}" data-dev="{dev}"
+ title="$_("Detach")">
+ </button>
+</div>
+</script>
+
<script type="text/javascript">
kimchi.guest_edit_main();
</script>
diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl
index f8af68e..97fc4f4 100644
--- a/ui/pages/i18n.html.tmpl
+++ b/ui/pages/i18n.html.tmpl
@@ -95,6 +95,11 @@ var i18n = {
'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")",
+ 'KCHVM6002M': "$_("This CDROM will be detached permanently and you can re-attach it. Continue to detach it?")",
+ 'KCHVM6003M': "$_("Attach")",
+ 'KCHVM6004M': "$_("Attaching...")",
+ 'KCHVM6005M': "$_("Replace")",
+ 'KCHVM6006M': "$_("Replacing...")",
'KCHNET6001E': "$_("The VLAN id must be between 1 and 4094.")",
--
1.8.1.4
10 years, 9 months
[PATCH v4] UI: Software Update Support
by Hongliang Wang
Added UI support for software updating. The Host Tab will initially
list available updates to user if there are any; or we will disable
"Update All" Button if no updates available.
V3 -> V4:
4a) Added the last update output
(Thanks to Aline's comment)
4b) Disabled horizontal resize function of output textarea
(Thanks to Aline's comment)
4c) Added "Update Progress" label to the output textarea
(Thanks to Aline's comment)
4d) Added refreshing the software grid after updating is finished
(Thanks to Aline's comment)
4e) Added software updates grid cleanup when host tab is unloaded
V2 -> V3:
3a) Fixed "Update All" Button always being disabled issue
(Thanks to Paulo and Aline's comment)
3b) Updated REST API calling according to back-end change
3c) Added in-progress message when system is being updated
(Thanks to Aline's comment)
V1 -> V2:
2a) Fixed "Update All" Button always being disabled issue
(Thanks to Paulo Ricardo Paz Vital's comment)
Signed-off-by: Hongliang Wang <hlwang(a)linux.vnet.ibm.com>
---
ui/css/theme-default/host.css | 29 ++++++++++++++
ui/js/src/kimchi.api.js | 53 ++++++++++++++++++++++++++
ui/js/src/kimchi.host.js | 88 +++++++++++++++++++++++++++++++++++++++++++
ui/pages/i18n.html.tmpl | 10 +++++
ui/pages/tabs/host.html.tmpl | 17 +++++++++
5 files changed, 197 insertions(+)
diff --git a/ui/css/theme-default/host.css b/ui/css/theme-default/host.css
index 470ed1b..0f8b941 100644
--- a/ui/css/theme-default/host.css
+++ b/ui/css/theme-default/host.css
@@ -224,3 +224,32 @@
width: 300px;
}
/* End of Debug Report */
+
+/* Software Updates */
+.host-panel #software-updates-grid {
+ border-color: #ddd;
+ height: 300px;
+ width: 850px;
+}
+
+.software-update-id {
+ width: 30px;
+}
+
+.software-update-name,
+.software-update-repos {
+ width: 220px;
+}
+
+.software-update-version,
+.software-update-arch {
+ width: 190px;
+}
+
+.host-panel #software-updates-progress-textarea {
+ border: 1px solid #ddd;
+ height: 100px;
+ resize: vertical;
+ width: 846px;
+}
+/* End of Software Updates */
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index fdd9cfc..beaf314 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -727,5 +727,58 @@ var kimchi = {
success : suc,
error : err
});
+ },
+
+ listSoftwareUpdates : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'host/packagesupdate',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend: true,
+ success : suc,
+ error : err
+ });
+ },
+
+ updateSoftwares : function(suc, err, progress) {
+ var taskID = -1;
+ var onResponse = function(data) {
+ taskID = data['id'];
+ trackTask();
+ };
+
+ var trackTask = function() {
+ kimchi.getTask(taskID, onTaskResponse, err);
+ };
+
+ var onTaskResponse = function(result) {
+ var taskStatus = result['status'];
+ switch(taskStatus) {
+ case 'running':
+ progress && progress(result);
+ setTimeout(function() {
+ trackTask();
+ }, 200);
+ break;
+ case 'finished':
+ suc(result);
+ break;
+ case 'failed':
+ err(result);
+ break;
+ default:
+ break;
+ }
+ };
+
+ kimchi.requestJSON({
+ url : kimchi.url + 'host/swupdate',
+ type : "POST",
+ contentType : "application/json",
+ dataType : "json",
+ success : onResponse,
+ error : err
+ });
}
};
diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js
index 6e7f333..df27313 100644
--- a/ui/js/src/kimchi.host.js
+++ b/ui/js/src/kimchi.host.js
@@ -128,6 +128,87 @@ kimchi.host_main = function() {
});
};
+ var softwareUpdatesGridID = 'software-updates-grid';
+ var softwareUpdatesGrid = null;
+ var progressAreaID = 'software-updates-progress-textarea';
+ var reloadProgressArea = function(result) {
+ var progressArea = $('#' + progressAreaID)[0];
+ $(progressArea).text(result['message']);
+ var scrollTop = $(progressArea).prop('scrollHeight');
+ $(progressArea).prop('scrollTop', scrollTop);
+ };
+
+ var initSoftwareUpdatesGrid = function(softwareUpdates) {
+ softwareUpdatesGrid = new kimchi.widget.Grid({
+ container: 'software-updates-grid-container',
+ id: softwareUpdatesGridID,
+ title: i18n['KCHUPD6001M'],
+ toolbarButtons: [{
+ id: softwareUpdatesGridID + '-update-button',
+ label: i18n['KCHUPD6006M'],
+ disabled: true,
+ onClick: function(event) {
+ var updateButton = $(this);
+ var progressArea = $('#' + progressAreaID)[0];
+ $('#software-updates-progress-container').removeClass('hidden');
+ $(progressArea).text('');
+ !kimchi.isElementInViewport(progressArea) &&
+ progressArea.scrollIntoView();
+ $(updateButton).text(i18n['KCHUPD6007M']).prop('disabled', true);
+
+ kimchi.updateSoftwares(function(result) {
+ reloadProgressArea(result);
+ $(updateButton).text(i18n['KCHUPD6006M']).prop('disabled', false);
+ kimchi.topic('kimchi/softwareUpdated').publish({
+ result: result
+ });
+ }, function() {}, reloadProgressArea);
+ }
+ }],
+ frozenFields: [{
+ name: 'id',
+ label: ' ',
+ 'class': 'software-update-id'
+ }],
+ fields: [{
+ name: 'package_name',
+ label: i18n['KCHUPD6002M'],
+ 'class': 'software-update-name'
+ }, {
+ name: 'version',
+ label: i18n['KCHUPD6003M'],
+ 'class': 'software-update-version'
+ }, {
+ name: 'arch',
+ label: i18n['KCHUPD6004M'],
+ 'class': 'software-update-arch'
+ }, {
+ name: 'repository',
+ label: i18n['KCHUPD6005M'],
+ 'class': 'software-update-repos'
+ }],
+ data: softwareUpdates
+ });
+ };
+
+ var listSoftwareUpdates = function() {
+ kimchi.listSoftwareUpdates(function(softwareUpdates) {
+ $.each(softwareUpdates, function(i, item) {
+ softwareUpdates[i]['id'] = i + 1;
+ });
+
+ if(softwareUpdatesGrid) {
+ softwareUpdatesGrid.setData(softwareUpdates);
+ }
+ else {
+ initSoftwareUpdatesGrid(softwareUpdates);
+ }
+
+ var updateButton = $('#' + softwareUpdatesGridID + '-update-button');
+ $(updateButton).prop('disabled', softwareUpdates.length === 0);
+ });
+ };
+
var shutdownButtonID = '#host-button-shutdown';
var restartButtonID = '#host-button-restart';
var shutdownHost = function(params) {
@@ -186,6 +267,8 @@ kimchi.host_main = function() {
kimchi.keepMonitoringHost = this['checked'];
});
+ listSoftwareUpdates();
+
kimchi.getCapabilities(function(capabilities) {
if(!capabilities['system_report_tool']) {
return;
@@ -199,6 +282,10 @@ kimchi.host_main = function() {
listDebugReports();
});
+ kimchi.topic('kimchi/softwareUpdated').subscribe(function() {
+ listSoftwareUpdates();
+ });
+
kimchi.getHost(function(data) {
var htmlTmpl = $('#host-tmpl').html();
data['logo'] = data['logo'] || '';
@@ -464,5 +551,6 @@ kimchi.host_main = function() {
delete kimchi.hostTimer;
}
reportGrid && reportGrid.destroy();
+ softwareUpdatesGrid && softwareUpdatesGrid.destroy();
});
};
diff --git a/ui/pages/i18n.html.tmpl b/ui/pages/i18n.html.tmpl
index db6166e..4223403 100644
--- a/ui/pages/i18n.html.tmpl
+++ b/ui/pages/i18n.html.tmpl
@@ -89,6 +89,16 @@ var i18n = {
'KCHDR6010M': "$_("Download")",
'KCHDR6011M': "$_("Report name should contain only letters, digits and/or hyphen ('-').")",
+
+ 'KCHUPD6001M': "$_("Software Updates")",
+ 'KCHUPD6002M': "$_("Package Name")",
+ 'KCHUPD6003M': "$_("Version")",
+ 'KCHUPD6004M': "$_("Architecture")",
+ 'KCHUPD6005M': "$_("Repository")",
+ 'KCHUPD6006M': "$_("Update All")",
+ 'KCHUPD6007M': "$_("Updating...")",
+
+
'KCHVM6001M': "$_("This will delete the virtual machine and its virtual disks. This operation cannot be undone. Would you like to continue?")",
'KCHVM6002M': "$_("Delete Confirmation")",
diff --git a/ui/pages/tabs/host.html.tmpl b/ui/pages/tabs/host.html.tmpl
index 23b9853..f6ecd47 100644
--- a/ui/pages/tabs/host.html.tmpl
+++ b/ui/pages/tabs/host.html.tmpl
@@ -120,6 +120,23 @@
</div>
</div>
</div>
+ <div id="software-update-section" class="host-section">
+ <h3 class="section-header"
+ aria-controls="content-software-update">
+ $_("Software Updates")
+ </h3>
+ <div id="content-software-update" class="section-content">
+ <div class="section-row">
+ <div class="section-value">
+ <div id="software-updates-grid-container"></div>
+ <div id="software-updates-progress-container" class="hidden">
+ <label for="software-updates-progress-textarea">$_("Update Progress")</label>
+ <textarea id="software-updates-progress-textarea" readonly></textarea>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
<div id="debug-report-section" class="host-section hidden">
<h3 class="section-header"
aria-controls="content-sys-reports">
--
1.8.1.4
10 years, 9 months
[PATCHv4 0/4] Add volume reference count
by lvroyce@linux.vnet.ibm.com
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Available volumes will be tracked by volume reference count.
Add this field to storage volume,
so that storage volume/pool action validation can rely on it.
Royce Lv (4):
Add volume ref_cnt: update api.md
Add volume ref_cnt: Update controller and json schema
Add volume ref_cnt: Add model and mockmodel implementation
Add volume ref_cnt: Update test
docs/API.md | 3 +++
src/kimchi/API.json | 25 +++++++++++++++++++++++++
src/kimchi/control/storagevolumes.py | 1 +
src/kimchi/i18n.py | 4 ++++
src/kimchi/mockmodel.py | 3 +++
src/kimchi/model/storagevolumes.py | 31 +++++++++++++++++++++++++++++++
tests/test_model.py | 4 ++++
tests/test_rest.py | 2 ++
8 files changed, 73 insertions(+)
--
1.8.1.2
10 years, 9 months
About the author information in Kimchi source code
by Shu Ming
Hi,
As you have seen, we have author information in Kimchi source code to
specially honour the writer of the original source files and the
succeeding writers with substantial change. Unfortunately, I found the
authors in some files were updated without any substantial change. One
recent example about this is "src/kimchi/model/debugreports.py", the
author was changed to "Aline Manera" and the original author was gone.
And this change was introduced by "refactor model: Create a separated
model for debug report resource" that got a new file "debugreports.py"
split from another huge file without much change. I don't think this
split was deserved to change the author in the new file. Then, how do we
define what is "substantial change". I would say it is a new feature
changing the file with more than 1/3 of the total lines of code. It is a
not necessary a hard rule, but it is obvious that code split should not
be deserved to have a new author for the file.
10 years, 9 months
[PATCH v2 0/2] Github bug #307: Add storage pool list
by Daniel Barboza
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
Changelog:
v2:
- Fixing message ID
These patches fixes the bug where the list of storage pool
types can take a long time to appear. The solution used here
is to detach the view of such list with the return of the
JSON that loads the partition data.
To test this patch I recomend doing the following change in
src/kimchi/model/hosts.py:
+ import time
class PartitionsModel(object):
def __init__(self, **kargs):
pass
def get_list(self):
+ time.sleep(10)
result = disks.get_partitions_names()
return result
The sleep(10) call will ensure that get_list (which returns the
partition data) will take at least 10 seconds to execute. It is
enough time to select the LOGICAL type and see how the UI is
working before and after the JSON call.
Daniel Henrique Barboza (2):
Github bug #307: add storage pool type list - JS changes
Github bug #307: storage pool type list - html and string changes
po/en_US.po | 21 +++-
po/kimchi.pot | 20 +++-
po/pt_BR.po | 21 +++-
po/zh_CN.po | 21 +++-
ui/js/src/kimchi.storagepool_add_main.js | 177 ++++++++++++++++---------------
ui/pages/i18n.html.tmpl | 5 +-
ui/pages/storagepool-add.html.tmpl | 7 +-
7 files changed, 177 insertions(+), 95 deletions(-)
--
1.8.3.1
10 years, 9 months
[PATCH] Fix debug report UI: Error handling and line selection
by Rodrigo Trujillo
If a user tries to delete a report and some error occurs, no message is
showed in the UI. Also, after delete the report, all lines become
unselected but remove and download buttons are enabled. So user is able
to click on remove button again, but will not know which report is going
to be deleted. This patch disables the buttons, so user have to select
the report again to perform the operations, avoiding mistakes.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.host.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ui/js/src/kimchi.host.js b/ui/js/src/kimchi.host.js
index 6e7f333..7974054 100644
--- a/ui/js/src/kimchi.host.js
+++ b/ui/js/src/kimchi.host.js
@@ -66,8 +66,13 @@ kimchi.host_main = function() {
kimchi.deleteReport({
name: report['name']
}, function(result) {
+ $('#' + reportGridID + '-remove-button')
+ .prop('disabled', true);
+ $('#' + reportGridID + '-download-button')
+ .prop('disabled', true);
listDebugReports();
}, function(error) {
+ kimchi.message.error(error.responseJSON.reason);
});
});
}
--
1.8.5.3
10 years, 9 months
[PATCH] issue #66: Auto generate template name in backend when none is provided
by Aline Manera
From: Aline Manera <alinefm(a)br.ibm.com>
The only required parameter to create a template is the cdrom ISO image.
The name must be create automatically on backend when no one is passed.
Fix it.
Signed-off-by: Aline Manera <alinefm(a)br.ibm.com>
---
src/kimchi/mockmodel.py | 8 +++++++-
src/kimchi/model/templates.py | 9 ++++++++-
ui/js/src/kimchi.template_add_main.js | 16 ----------------
3 files changed, 15 insertions(+), 18 deletions(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
index 50b5f0e..f99b4cd 100644
--- a/src/kimchi/mockmodel.py
+++ b/src/kimchi/mockmodel.py
@@ -216,7 +216,13 @@ class MockModel(object):
raise NotFoundError("KCHTMPL0002E", {'name': name})
def templates_create(self, params):
- name = params['name']
+ name = params.get('name', '').strip()
+ if not name:
+ iso = params['cdrom']
+ iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0]
+ name = iso_name + str(int(time.time()*1000))
+ params['name'] = name
+
if name in self._mock_templates:
raise InvalidOperation("KCHTMPL0001E", {'name': name})
diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py
index 7afb0e0..3062bd7 100644
--- a/src/kimchi/model/templates.py
+++ b/src/kimchi/model/templates.py
@@ -19,6 +19,7 @@
import copy
import os
+import time
import libvirt
@@ -34,7 +35,13 @@ class TemplatesModel(object):
self.conn = kargs['conn']
def create(self, params):
- name = params['name']
+ name = params.get('name', '').strip()
+ if not name:
+ iso = params['cdrom']
+ iso_name = os.path.splitext(iso[iso.rfind('/') + 1:])[0]
+ name = iso_name + str(int(time.time()*1000))
+ params['name'] = name
+
conn = self.conn.get()
pool_uri = params.get(u'storagepool', '')
diff --git a/ui/js/src/kimchi.template_add_main.js b/ui/js/src/kimchi.template_add_main.js
index 31326df..098a706 100644
--- a/ui/js/src/kimchi.template_add_main.js
+++ b/ui/js/src/kimchi.template_add_main.js
@@ -219,7 +219,6 @@ kimchi.template_add_main = function() {
return;
}
var data = {
- "name" : kimchi.get_iso_name(isoFile),
"cdrom" : isoFile
};
addTemplate(data);
@@ -331,7 +330,6 @@ kimchi.template_add_main = function() {
return;
}
var data = {
- "name" : kimchi.get_iso_name(isoUrl),
"cdrom" : isoUrl
};
addTemplate(data);
@@ -355,7 +353,6 @@ kimchi.template_add_main = function() {
var successNum = 0;
var addTemplate = function(isoInfo) {
var data = {
- "name" : kimchi.get_iso_name(isoInfo.name),
"os_distro" : isoInfo.os_distro,
"os_version" : isoInfo.os_version,
"cdrom" : isoInfo.path
@@ -419,16 +416,3 @@ kimchi.is_iso_file = function(isoFile) {
}
return false;
};
-
-kimchi.get_iso_name = function(isoPath) {
- if ((isoPath.charAt(isoPath.length - 1) == "/") == true) {
- isoPath = isoPath.substring(0, isoPath.length - 1)
- }
- if (/.iso$/.test(isoPath)) {
- return isoPath.substring(isoPath.lastIndexOf("/") + 1,
- isoPath.lastIndexOf(".")) + new Date().getTime();
- } else {
- return isoPath.substring(isoPath.lastIndexOf("/") + 1) +
- new Date().getTime();
- }
-};
--
1.7.10.4
10 years, 9 months
[PATCH] bug fix: Only check for ISO images in active pools
by Aline Manera
From: Aline Manera <alinefm(a)br.ibm.com>
The wrong exception was expected while searching for ISO files in the storage
pools.
Independent of the exception raised it should not block users to create
a template, so use the generic Exception to catch any error.
Fix it to avoit the error below.
Request Headers:
COOKIE: kimchi=a50f5cb488c331754db1a642eaba89f7fe71c0b8;
userid=alinefm
HOST: localhost:8000
CONNECTION: keep-alive
Remote-Addr: 127.0.0.1
ACCEPT: application/json, text/javascript, */*; q=0.01
USER-AGENT: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:27.0)
Gecko/20100101 Firefox/27.0
X-REQUESTED-WITH: XMLHttpRequest
ACCEPT-LANGUAGE: en-us,en;q=0.7,pt-br;q=0.3
Content-Type: application/json
REFERER: http://localhost:8000/
ACCEPT-ENCODING: gzip, deflate
[28/Feb/2014:12:27:50] HTTP Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/cherrypy/_cprequest.py", line 656, in respond
response.body = self.handler()
File "/usr/lib/python2.7/dist-packages/cherrypy/lib/encoding.py", line 188, in __call__
self.body = self.oldhandler(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/cherrypy/_cpdispatch.py", line 34, in __call__
return self.callable(*self.args, **self.kwargs)
File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 274, in index
return self.get(filter_params)
File "/home/alinefm/kimchi/src/kimchi/control/storagevolumes.py", line 71, in get
res_list = get_list(*self.model_args)
File "/home/alinefm/kimchi/src/kimchi/model/storagevolumes.py", line 170, in get_list
pool.refresh(0)
File "/home/alinefm/kimchi/src/kimchi/model/libvirtconnection.py", line 62, in wrapper
ret = f(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/libvirt.py", line 2220, in refresh
if ret == -1: raise libvirtError ('virStoragePoolRefresh() failed', pool=self)
libvirtError: Requested operation is not valid: storage pool is not active
Signed-off-by: Aline Manera <alinefm(a)br.ibm.com>
---
src/kimchi/model/storagevolumes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py
index 8defdb7..3e21268 100644
--- a/src/kimchi/model/storagevolumes.py
+++ b/src/kimchi/model/storagevolumes.py
@@ -169,7 +169,7 @@ class IsoVolumesModel(object):
pool = StoragePoolModel.get_storagepool(pool_name, self.conn)
pool.refresh(0)
volumes = pool.listVolumes()
- except InvalidOperation:
+ except Exception:
# Skip inactive pools
continue
--
1.7.10.4
10 years, 9 months
[PATCH 0/2] bug fix: Enable NFS path field when a NFS server is provided
by Aline Manera
From: Aline Manera <alinefm(a)br.ibm.com>
Aline Manera (2):
bug fix: Enable NFS path field when a NFS server is provided
Fix typo on KCHAPI6005E message
po/en_US.po | 24 ++++++++++++++++++++++--
po/kimchi.pot | 24 ++++++++++++++++++++++--
po/pt_BR.po | 24 ++++++++++++++++++++++--
po/zh_CN.po | 24 ++++++++++++++++++++++--
ui/js/src/kimchi.storagepool_add_main.js | 2 +-
ui/pages/i18n.html.tmpl | 2 +-
6 files changed, 90 insertions(+), 10 deletions(-)
--
1.7.10.4
10 years, 9 months