<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <br>
    <div class="moz-cite-prefix">On 09/05/2014 02:48 PM, Hongliang Wang
      wrote:<br>
    </div>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">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 <a class="moz-txt-link-rfc2396E" href="mailto:hlwang@linux.vnet.ibm.com">&lt;hlwang@linux.vnet.ibm.com&gt;</a>
---
 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,</pre>
    </blockquote>
    <br>
    I think you used it only for your tests, right? =)<br>
    <br>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">
              '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
+ *</pre>
    </blockquote>
    <br>
    The copyright refers to the year the file was created followed by
    the modifications.<br>
    So if a file was created in 2014 and never modified the copyright
    will be only 2014.<br>
    if the files was created in 2013 and modified in 2014 the copyright
    will be 2013-2014<br>
    So in this case you need to keep only 2014<br>
    <br>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">
+ * 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
+ *
+ *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
+ *
+ * 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
+        });
+    },
+</pre>
    </blockquote>
    <br>
    I am getting the following error while trying to upload a file into
    a storage pool:<br>
    <br>
    <br>
    <img src="cid:part1.03090309.08020406@linux.vnet.ibm.com" alt=""><br>
    <br>
    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.<br>
    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.<br>
    <br>
    Check src/kimchi/template.py<br>
    <br>
    def
    can_accept_html():&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    <br>
    &nbsp;&nbsp;&nbsp; return can_accept('text/html') or
    \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; can_accept('application/xaml+xml') or
    \&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; can_accept('*/*')&nbsp; <br>
    <br>
    To solve this issue, we can change AsyncCollection
    (src/kimchi/control/base.py) to always return a JSON (see patch
    below)<br>
    <br>
    <br>
    diff --git a/src/kimchi/control/base.py b/src/kimchi/control/base.py<br>
    index 6391a1a..9ab6696 100644<br>
    --- a/src/kimchi/control/base.py<br>
    +++ b/src/kimchi/control/base.py<br>
    @@ -344,7 +344,10 @@ class AsyncCollection(Collection):<br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; args = self.model_args + [params]<br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; task = create(*args)<br>
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cherrypy.response.status = 202<br>
    -&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return kimchi.template.render("Task", task)<br>
    +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cherrypy.response.headers['Content-Type'] = \<br>
    +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'application/json;charset=utf-8'<br>
    +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return json.dumps(task, indent=2, separators=(',', ':'))<br>
    <br>
    <br>
    After applying this patch I was able to upload a file into a storage
    pool, but the dialog is not closed.<br>
    I need to manually close the dialog and check the storage volumes
    inside the pool. (And any more errors on firebug =])<br>
    <br>
    Let me know if you have any other idea to solve this content type
    issue. Otherwise, I will send the above patch for review.<br>
    <br>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">
+    /**
+     * 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</pre>
    </blockquote>
    <br>
    Name the fie as storagepool_add_volume_main.js so we use the same
    pattern already in use.<br>
    <br>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">
@@ -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
+ *
+ *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
+ *
+ * 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 &amp;&amp; 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();</pre>
    </blockquote>
    <br>
    It should be target_uri<br>
    <br>
    <blockquote
      cite="mid:1409939286-21989-1-git-send-email-hlwang@linux.vnet.ibm.com"
      type="cite">
      <pre wrap="">
+            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 &gt; -1 &amp;&amp; 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 <a class="moz-txt-link-rfc2396E" href="mailto:hlwang@linux.vnet.ibm.com">&lt;hlwang@linux.vnet.ibm.com&gt;</a>
+ *
+ * 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
+ *
+ *     <a class="moz-txt-link-freetext" href="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</a>
+ *
+ * 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
+&lt;div id="sp-add-volume-window" class="window"&gt;
+    &lt;form id="form-sp-add-volume"&gt;
+        &lt;header class="window-header"&gt;
+            &lt;h1 class="title"&gt;$_("Add a Volume to Storage Pool")&lt;/h1&gt;
+            &lt;div class="close"&gt;X&lt;/div&gt;
+        &lt;/header&gt;
+        &lt;section&gt;
+            &lt;div class="content"&gt;
+                &lt;div class="form-section"&gt;
+                    &lt;h2&gt;
+                        &lt;input type="radio" id="volume-type-download" class="volume-type" name="volumeType" value="download" checked="checked" /&gt;
+                        &lt;label for="volume-type-download"&gt;
+                            $_("Fetch from remote URL")
+                        &lt;/label&gt;
+                    &lt;/h2&gt;
+                    &lt;div class="field"&gt;
+                        &lt;p class="text-help"&gt;
+                            $_("Enter the remote URL here.")
+                        &lt;/p&gt;
+                        &lt;div class="textbox-wrapper"&gt;
+                            &lt;input type="text" id="volume-remote-url" class="text volume-input download" name="volumeRemoteURL" /&gt;
+                        &lt;/div&gt;
+                    &lt;/div&gt;
+                &lt;/div&gt;
+                &lt;div class="form-section"&gt;
+                    &lt;h2&gt;
+                        &lt;input type="radio" id="volume-type-upload" class="volume-type" name="volumeType" value="upload"/&gt;
+                        &lt;label for="volume-type-upload"&gt;
+                        $_("Upload an file")
+                        &lt;/label&gt;
+                    &lt;/h2&gt;
+                    &lt;div class="field"&gt;
+                        &lt;p class="text-help"&gt;
+                            $_("Choose the file you want to upload.")
+                        &lt;/p&gt;
+                        &lt;div class="textbox-wrapper"&gt;
+                            &lt;input type="file" class="volume-input upload" id="volume-input-file" name="volumeLocalFile" disabled="disabled" /&gt;
+                        &lt;/div&gt;
+                    &lt;/div&gt;
+                &lt;/div&gt;
+            &lt;/div&gt;
+        &lt;/section&gt;
+        &lt;footer&gt;
+            &lt;div class="btn-group"&gt;
+                &lt;button type="submit" id="sp-add-volume-button" class="btn-normal"&gt;
+                    &lt;span class="text"&gt;$_("OK")&lt;/span&gt;
+                &lt;/button&gt;
+            &lt;/div&gt;
+        &lt;/footer&gt;
+    &lt;/form&gt;
+&lt;/div&gt;
+&lt;script type="text/javascript"&gt;
+    kimchi.sp_add_volume_main();
+&lt;/script&gt;
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 @@
                     &lt;div class="popover actionsheet right-side" style="width: 250px"&gt;
                         &lt;button class="button-big pool-deactivate" data-stat="{state}" data-name="{name}" data-persistent="{persistent}"&gt;&lt;span class="text"&gt;$_("Deactivate")&lt;/span&gt;&lt;/button&gt;
                         &lt;button class="button-big pool-activate" data-stat="{state}" data-name="{name}"&gt;&lt;span class="text"&gt;$_("Activate")&lt;/span&gt;&lt;/button&gt;
+                        &lt;button class="button-big pool-add-volume" data-stat="{state}" data-name="{name}"&gt;&lt;span class="text"&gt;$_("Add Volume")&lt;/span&gt;&lt;/button&gt;
                         &lt;button class="button-big pool-extend {enableExt}" data-stat="{state}" data-name="{name}"&gt;&lt;span class="text"&gt;$_("Extend")&lt;/span&gt;&lt;/button&gt;
                         &lt;button class="button-big red pool-delete" data-stat="{state}" data-name="{name}"&gt;&lt;span class="text"&gt;$_("Undefine")&lt;/span&gt;&lt;/button&gt;
                     &lt;/div&gt;
@@ -98,11 +99,20 @@
     &lt;/li&gt;
 &lt;/script&gt;
 &lt;script id="volumeTmpl" type="html/text"&gt;
-        &lt;div class="volume-box white-box"&gt;
+        &lt;div class="volume-box white-box" data-volume-name="{name}"&gt;
             &lt;div class="storage-icon volume-default icon-{format} "&gt;
             &lt;/div&gt;
             &lt;div class="volume-title"&gt;
                 &lt;div class="volume-name" title="{name}"&gt;{name}&lt;/div&gt;
+                &lt;div class="volume-progress hidden"&gt;
+                    &lt;div class="progress-bar-outer"&gt;
+                        &lt;div class="progress-bar-inner"&gt;&lt;/div&gt;
+                    &lt;/div&gt;
+                    &lt;div class="progress-label"&gt;
+                        &lt;span class="progress-status"&gt;&lt;/span&gt;
+                        &lt;span class="progress-downloaded"&gt;&lt;/span&gt;
+                    &lt;/div&gt;
+                &lt;/div&gt;
             &lt;/div&gt;
             &lt;div class="volume-setting"&gt;
             &lt;/div&gt;
</pre>
    </blockquote>
    <br>
  </body>
</html>