[Kimchi-devel] [WIP PATCH] Storage DL/UL
Aline Manera
alinefm at linux.vnet.ibm.com
Mon Sep 8 18:02:54 UTC 2014
On 09/05/2014 02:48 PM, Hongliang Wang wrote:
> Volume.
>
> Known kssues:
> after add a remote URL and click "OK" button, manual click on the storage pool
> to refresh the volumes.
>
> Signed-off-by: Hongliang Wang <hlwang at linux.vnet.ibm.com>
> ---
> src/kimchi/model/host.py | 2 +-
> ui/css/theme-default/sp-add-volume.css | 36 ++++++++
> ui/css/theme-default/storage.css | 34 +++++++-
> ui/js/src/kimchi.api.js | 38 +++++++++
> ui/js/src/kimchi.sp_add_volume_main.js | 152 +++++++++++++++++++++++++++++++++
> ui/js/src/kimchi.storage_main.js | 19 ++++-
> ui/pages/i18n.json.tmpl | 3 +
> ui/pages/sp-add-volume.html.tmpl | 80 +++++++++++++++++
> ui/pages/tabs/storage.html.tmpl | 12 ++-
> 9 files changed, 369 insertions(+), 7 deletions(-)
> create mode 100644 ui/css/theme-default/sp-add-volume.css
> create mode 100644 ui/js/src/kimchi.sp_add_volume_main.js
> create mode 100644 ui/pages/sp-add-volume.html.tmpl
>
> diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
> index 933a142..2052f58 100644
> --- a/src/kimchi/model/host.py
> +++ b/src/kimchi/model/host.py
> @@ -280,7 +280,7 @@ class DevicesModel(object):
> def __init__(self, **kargs):
> self.conn = kargs['conn']
> self.cap_map = \
> - {'fc_host': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST,
> + {#'fc_host': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST,
I think you used it only for your tests, right? =)
> 'net': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET,
> 'pci': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV,
> 'scsi': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI,
> diff --git a/ui/css/theme-default/sp-add-volume.css b/ui/css/theme-default/sp-add-volume.css
> new file mode 100644
> index 0000000..2c31b9a
> --- /dev/null
> +++ b/ui/css/theme-default/sp-add-volume.css
> @@ -0,0 +1,36 @@
> +/*
> + * Project Kimchi
> + *
> + * Copyright IBM, Corp. 2013-2014
> + *
The copyright refers to the year the file was created followed by the
modifications.
So if a file was created in 2014 and never modified the copyright will
be only 2014.
if the files was created in 2013 and modified in 2014 the copyright will
be 2013-2014
So in this case you need to keep only 2014
> + * 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.
> + */
> +#sp-add-volume-window {
> + height: 400px;
> + width: 500px;
> +}
> +
> +#sp-add-volume-window .textbox-wrapper input[type="text"] {
> + box-sizing: border-box;
> + width: 100%;
> +}
> +
> +#sp-add-volume-window .textbox-wrapper label {
> + vertical-align: middle;
> +}
> +
> +#sp-add-volume-window input[type="text"][disabled] {
> + color: #bbb;
> + background-color: #fafafa;
> + cursor: not-allowed;
> +}
> diff --git a/ui/css/theme-default/storage.css b/ui/css/theme-default/storage.css
> index f635c2f..02c92e9 100644
> --- a/ui/css/theme-default/storage.css
> +++ b/ui/css/theme-default/storage.css
> @@ -336,7 +336,6 @@
> float: left;
> padding: 4px;
> margin-bottom: 5px;
> - height: 40px;
> width: 130px;
> }
>
> @@ -622,3 +621,36 @@
> #iSCSITarget input {
> width: 493px;
> }
> +
> +/* Progress bar */
> +.volume-progress {
> + clear: both;
> + width: 140px;
> +}
> +
> +.volume-progress .progress-bar-outer {
> + background: #ccc;
> + height: 4px;
> + overflow: hidden;
> + width: 100%;
> +}
> +
> +.volume-progress .progress-bar-inner {
> + background: #090;
> + height: 100%;
> + width: 0%;
> +}
> +
> +.volume-progress .progress-label {
> + color: #999;
> + font-size: 10px;
> + line-height: 16px;
> +}
> +
> +.volume-progress .progress-status {
> +}
> +
> +.volume-progress .progress-downloaded {
> + float: right;
> +}
> +/* End of Progress bar */
> diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
> index 5fc456d..766df6a 100644
> --- a/ui/js/src/kimchi.api.js
> +++ b/ui/js/src/kimchi.api.js
> @@ -1130,5 +1130,43 @@ var kimchi = {
> kimchi.message.error(data.responseJSON.reason);
> }
> });
> + },
> +
> + /**
> + * Add a volume to a given storage pool.
> + */
> + uploadVolumeToSP: function(settings, suc, err) {
> + var fd = settings['formData'];
> + var sp = encodeURIComponent(settings['sp']);
> + kimchi.requestJSON({
> + url : kimchi.url + 'storagepools/' + sp + '/storagevolumes',
> + type : 'POST',
> + data : fd,
> + processData : false,
> + contentType : false,
> + success : suc,
> + error : err
> + });
> + },
> +
I am getting the following error while trying to upload a file into a
storage pool:
After some investigation I realized it is because you set the
contentType as false (ie, '*/*') so Kimchi server will try to return a
HTML according to the resource.
In this case a Task resource. So it looks for a file:
/home/alinefm/kimchi/ui/pages/Task.tmpl which does not exist and then
the 404 error is reported.
Check src/kimchi/template.py
def can_accept_html():
return can_accept('text/html') or \
can_accept('application/xaml+xml') or \
can_accept('*/*')
To solve this issue, we can change AsyncCollection
(src/kimchi/control/base.py) to always return a JSON (see patch below)
diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py
index 6391a1a..9ab6696 100644
--- a/src/kimchi/control/base.py
+++ b/src/kimchi/control/base.py
@@ -344,7 +344,10 @@ class AsyncCollection(Collection):
args = self.model_args + [params]
task = create(*args)
cherrypy.response.status = 202
- return kimchi.template.render("Task", task)
+ cherrypy.response.headers['Content-Type'] = \
+ 'application/json;charset=utf-8'
+ return json.dumps(task, indent=2, separators=(',', ':'))
After applying this patch I was able to upload a file into a storage
pool, but the dialog is not closed.
I need to manually close the dialog and check the storage volumes inside
the pool. (And any more errors on firebug =])
Let me know if you have any other idea to solve this content type issue.
Otherwise, I will send the above patch for review.
> + /**
> + * Add a volume to a given storage pool by URL.
> + */
> + downloadVolumeToSP: function(settings, suc, err) {
> + var url = settings['url'];
> + var name = settings['name'];
> + var sp = encodeURIComponent(settings['sp']);
> + kimchi.requestJSON({
> + url : kimchi.url + 'storagepools/' + sp + '/storagevolumes',
> + type : 'POST',
> + data : JSON.stringify({
> + name: name,
> + url: url
> + }),
> + contentType : 'application/json',
> + dataType : 'json',
> + success : suc,
> + error : err
> + });
> }
> };
> diff --git a/ui/js/src/kimchi.sp_add_volume_main.js b/ui/js/src/kimchi.sp_add_volume_main.js
> new file mode 100644
> index 0000000..e4f25fc
> --- /dev/null
> +++ b/ui/js/src/kimchi.sp_add_volume_main.js
Name the fie as storagepool_add_volume_main.js so we use the same
pattern already in use.
> @@ -0,0 +1,152 @@
> +/*
> + * Project Kimchi
> + *
> + * Copyright IBM, Corp. 2013-2014
> + *
> + * 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.sp_add_volume_main = function() {
> + // download from remote server or upload from local file
> + var type = 'download';
> +
> + var addButton = $('#sp-add-volume-button');
> +
> + $('input.volume-type').change(function(e) {
> + $('.volume-input').prop('disabled', true);
> + $('.volume-input.' + this.value).prop('disabled', false);
> + type = this.value;
> + });
> +
> + var volumeName;
> + var taskID = -1;
> +
> + var onAccepted = function() {
> + if(inited[taskID]) {
> + return;
> + }
> + inited[taskID] = true;
> + kimchi.window.close();
> +
> + kimchi.topic('kimchi/volumeDownloadStarted').publish({
> + sp: kimchi.selectedSP
> + });
> + };
> + var inited = {};
> +
> + var onError = function(result) {
> + if (result['message']) {
> + var errText = result['message'];
> + }
> + else {
> + var errText = result['responseJSON']['reason'];
> + }
> + result && kimchi.message.error(errText);
> + var volumeName = result['target_uri'].split('/').pop();
> + var volumeBox = $('#volume' + kimchi.selectedSP + ' [data-volume-name="' + volumeName + '"]');
> + $('.progress-status', volumeBox).text(i18n['KCHPOOL6016M']);
> + };
> +
> + var fetchRemoteFile = function() {
> + var volumeURL = $('#volume-remote-url').val();
> + volumeName = volumeURL.split(/(\\|\/)/g).pop();
> + kimchi.downloadVolumeToSP({
> + sp: kimchi.selectedSP,
> + name: volumeName,
> + url: volumeURL
> + }, function(resp) {
> + taskID = resp['id'];
> + onAccepted();
> + }, onError);
> + };
> +
> + var onProgress = function(resp) {
> + var sizeArray = resp['message'].split('/');
> + var downloaded = sizeArray[0];
> + var percent = 0;
> + if(isNaN(downloaded)) {
> + downloaded = 0;
> + }
> + else {
> + var total = sizeArray[1];
> + percent = downloaded / total * 100;
> + }
> + var formatted = kimchi.formatMeasurement(downloaded);
> + var size = formatted['v'].toFixed(1) + formatted['s'];
> + var volumeBox = $('#volume' + kimchi.selectedSP + ' [data-volume-name="' + volumeName + '"]');
> + $('.volume-progress', volumeBox).removeClass('hidden');
> + $('.progress-bar-inner', volumeBox).css({
> + width: percent + '%'
> + });
> + $('.progress-status', volumeBox).text(i18n['KCHPOOL6014M']);
> + $('.progress-downloaded', volumeBox).text(size);
> + };
> +
> + var onTaskResponse = function(result) {
> + var taskStatus = result['status'];
> + switch(taskStatus) {
> + case 'running':
> + onProgress(result);
> + setTimeout(function() {
> + trackTask(result['id']);
> + }, 1500);
> + break;
> + case 'finished':
> + taskID = -1;
> + var volumeName = result['target_url'].split('/').pop();
It should be target_uri
> + var volumeBox = $('#volume' + kimchi.selectedSP + ' [data-volume-name="' + volumeName + '"]');
> + $('.progress-status', volumeBox).text(i18n['KCHPOOL6015M']);
> + break;
> + case 'failed':
> + taskID = -1;
> + onError(result);
> + break;
> + default:
> + break;
> + }
> + };
> +
> + var trackTask = function(task) {
> + kimchi.getTask(task, onTaskResponse, function(resp) {});
> + };
> +
> + var uploadFile = function() {
> + var blobFile = $('#volume-input-file')[0].files[0];
> + var fileName = blobFile.name;
> + var fd = new FormData();
> + fd.append('name', fileName);
> + fd.append('file', blobFile);
> + kimchi.uploadVolumeToSP({
> + sp: kimchi.selectedSP,
> + formData: fd
> + }, function(resp) {
> + });
> + };
> +
> + $(addButton).on('click', function(event) {
> + $(this).prop('disabled', true);
> + if(type === 'download') {
> + fetchRemoteFile();
> + }
> + else {
> + uploadFile();
> + }
> + event.preventDefault();
> + });
> +
> + kimchi.topic('kimchi/volumesListed').subscribe(function() {
> + var volumeBox = $('#volume' + kimchi.selectedSP + ' [data-volume-name="' + volumeName + '"]');
> + $(volumeBox).removeClass('hidden');
> + $('.progress-status', volumeBox).text(i18n['KCHPOOL6014M']);
> + taskID > -1 && trackTask(taskID);
> + });
> +};
> diff --git a/ui/js/src/kimchi.storage_main.js b/ui/js/src/kimchi.storage_main.js
> index ae3f963..56252a8 100644
> --- a/ui/js/src/kimchi.storage_main.js
> +++ b/ui/js/src/kimchi.storage_main.js
> @@ -135,6 +135,12 @@ kimchi.storageBindClick = function() {
> }
> });
>
> + $('.pool-add-volume').on('click', function(event) {
> + var poolName = $(this).data('name');
> + kimchi.selectedSP = poolName;
> + kimchi.window.open('sp-add-volume.html');
> + });
> +
> $('.storage-action').on('click', function() {
> var storage_action = $(this);
> var deleteButton = storage_action.find('.pool-delete');
> @@ -149,10 +155,6 @@ kimchi.storageBindClick = function() {
> $("#logicalPoolExtend").dialog("option", "poolName", $(this).data('name'));
> $("#logicalPoolExtend").dialog("open");
> });
> -
> - $('#volume-doAdd').on('click', function() {
> - kimchi.window.open('storagevolume-add.html');
> - });
> }
>
> $('.storage-li').on('click', function(event) {
> @@ -195,6 +197,9 @@ kimchi.doListVolumes = function(poolObj) {
> poolObj.removeClass('in');
> kimchi.changeArrow(handleArrow);
> slide.slideDown('slow');
> + setTimeout(function() {
> + kimchi.topic('kimchi/volumesListed').publish();
> + }, 1500);
> }
> }, function(err) {
> kimchi.message.error(err.responseJSON.reason);
> @@ -255,6 +260,12 @@ kimchi.storage_main = function() {
> }
> kimchi.doListStoragePools();
> kimchi.initLogicalPoolExtend();
> +
> + kimchi.topic('kimchi/volumeDownloadStarted').subscribe(function(data) {
> + var sp = data['sp'];
> + var poolNode = $('.storage-li[data-name="' + sp + '"]');
> + kimchi.doListVolumes(poolNode);
> + });
> }
>
> kimchi.changeArrow = function(obj) {
> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
> index d920ae2..b3bca53 100644
> --- a/ui/pages/i18n.json.tmpl
> +++ b/ui/pages/i18n.json.tmpl
> @@ -169,6 +169,9 @@
> "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?")",
> "KCHPOOL6013M": "$_("Unable to retrieve partitions information.")",
> + "KCHPOOL6014M": "$_("Downloading...")",
> + "KCHPOOL6015M": "$_("Done!")",
> + "KCHPOOL6016M": "$_("Failed!")",
>
> "KCHVMSTOR0001E": "$_("CDROM path need to be a valid local path and cannot be blank.")",
> "KCHVMSTOR0002E": "$_("Disk pool or volume cannot be blank.")"
> diff --git a/ui/pages/sp-add-volume.html.tmpl b/ui/pages/sp-add-volume.html.tmpl
> new file mode 100644
> index 0000000..148a17b
> --- /dev/null
> +++ b/ui/pages/sp-add-volume.html.tmpl
> @@ -0,0 +1,80 @@
> +#*
> + * Project Kimchi
> + *
> + * Copyright IBM, Corp. 2014
> + *
> + * Authors:
> + * Hongliang Wang <hlwang at 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="sp-add-volume-window" class="window">
> + <form id="form-sp-add-volume">
> + <header class="window-header">
> + <h1 class="title">$_("Add a Volume to Storage Pool")</h1>
> + <div class="close">X</div>
> + </header>
> + <section>
> + <div class="content">
> + <div class="form-section">
> + <h2>
> + <input type="radio" id="volume-type-download" class="volume-type" name="volumeType" value="download" checked="checked" />
> + <label for="volume-type-download">
> + $_("Fetch from remote URL")
> + </label>
> + </h2>
> + <div class="field">
> + <p class="text-help">
> + $_("Enter the remote URL here.")
> + </p>
> + <div class="textbox-wrapper">
> + <input type="text" id="volume-remote-url" class="text volume-input download" name="volumeRemoteURL" />
> + </div>
> + </div>
> + </div>
> + <div class="form-section">
> + <h2>
> + <input type="radio" id="volume-type-upload" class="volume-type" name="volumeType" value="upload"/>
> + <label for="volume-type-upload">
> + $_("Upload an file")
> + </label>
> + </h2>
> + <div class="field">
> + <p class="text-help">
> + $_("Choose the file you want to upload.")
> + </p>
> + <div class="textbox-wrapper">
> + <input type="file" class="volume-input upload" id="volume-input-file" name="volumeLocalFile" disabled="disabled" />
> + </div>
> + </div>
> + </div>
> + </div>
> + </section>
> + <footer>
> + <div class="btn-group">
> + <button type="submit" id="sp-add-volume-button" class="btn-normal">
> + <span class="text">$_("OK")</span>
> + </button>
> + </div>
> + </footer>
> + </form>
> +</div>
> +<script type="text/javascript">
> + kimchi.sp_add_volume_main();
> +</script>
> diff --git a/ui/pages/tabs/storage.html.tmpl b/ui/pages/tabs/storage.html.tmpl
> index 87205bd..d5aceef 100644
> --- a/ui/pages/tabs/storage.html.tmpl
> +++ b/ui/pages/tabs/storage.html.tmpl
> @@ -82,6 +82,7 @@
> <div class="popover actionsheet right-side" style="width: 250px">
> <button class="button-big pool-deactivate" data-stat="{state}" data-name="{name}" data-persistent="{persistent}"><span class="text">$_("Deactivate")</span></button>
> <button class="button-big pool-activate" data-stat="{state}" data-name="{name}"><span class="text">$_("Activate")</span></button>
> + <button class="button-big pool-add-volume" data-stat="{state}" data-name="{name}"><span class="text">$_("Add Volume")</span></button>
> <button class="button-big pool-extend {enableExt}" data-stat="{state}" data-name="{name}"><span class="text">$_("Extend")</span></button>
> <button class="button-big red pool-delete" data-stat="{state}" data-name="{name}"><span class="text">$_("Undefine")</span></button>
> </div>
> @@ -98,11 +99,20 @@
> </li>
> </script>
> <script id="volumeTmpl" type="html/text">
> - <div class="volume-box white-box">
> + <div class="volume-box white-box" data-volume-name="{name}">
> <div class="storage-icon volume-default icon-{format} ">
> </div>
> <div class="volume-title">
> <div class="volume-name" title="{name}">{name}</div>
> + <div class="volume-progress hidden">
> + <div class="progress-bar-outer">
> + <div class="progress-bar-inner"></div>
> + </div>
> + <div class="progress-label">
> + <span class="progress-status"></span>
> + <span class="progress-downloaded"></span>
> + </div>
> + </div>
> </div>
> <div class="volume-setting">
> </div>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ovirt.org/pipermail/kimchi-devel/attachments/20140908/e7e10b8c/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: gdadcjdd.
Type: image/jpeg
Size: 52253 bytes
Desc: not available
URL: <http://lists.ovirt.org/pipermail/kimchi-devel/attachments/20140908/e7e10b8c/attachment.jpe>
More information about the Kimchi-devel
mailing list