[Kimchi-devel] [PATCH] [Kimchi] [RFC] Create new volume and attach to VM

Paulo Ricardo Paz Vital pvital at linux.vnet.ibm.com
Fri Feb 5 12:27:40 UTC 2016


On 02/05/2016 10:03 AM, Aline Manera wrote:
> 
> 
> On 02/05/2016 09:03 AM, Paulo Ricardo Paz Vital wrote:
>> Looks good! I tested the patch and could create a new volume and attach
>> it to the VM I was editing.
>>
>> Just only one suggestion. Add the extension iso in the end of the
>> filename of the volume when creating. The volume I added to my VM
>> 'ubuntu15.10-vm-1' was created with this info:
>>
>> $ sudo qemu-img info /var/lib/libvirt/images/ubuntu15.10-vm-11454669512074
>> image: /var/lib/libvirt/images/ubuntu15.10-vm-11454669512074
>> file format: qcow2
>> virtual size: 10K (10240 bytes)
>> disk size: 196K
>> cluster_size: 65536
>> Format specific information:
>>     compat: 0.10
>>     refcount bits: 16
>>
>> Would be nice, that the file has the name
>> ubuntu15.10-vm-11454669512074.iso
> 
> The extension '.iso' is for ISO files. In this case, we should use the
> extension given by the selected format, ie, in your case
> ubuntu15.10-vm-11454669512074*.qcow2
> 
> *Socorro, following Paulo's suggestion, please, append the ".<format>"
> to the end of the file name.


Ooops, I mean '.img' instead of '.iso', but if add the '.<format>' is
not a big issue, would be better than.

> *
> *
>> On 02/05/2016 12:41 AM, Socorro Stoppler wrote:
>>> This is the initial checkin for creating a new volume and attaching to VM when editing a guest.
>>> Not sure of all the supported cases, but certainly tried default pool and qcow2 format per the
>>> example provided and it was successful.
>>>
>>> Known issue - which currently exists in the code already in master - code is broken when VM 
>>> is running.  This patch does not address that.  It will get addressed separately.
>>>
>>>
>>> Signed-off-by: Socorro Stoppler <socorro at linux.vnet.ibm.com>
>>> ---
>>>  ui/js/src/kimchi.guest_storage_add.main.js | 320 ++++++++++++++++++++++++-----
>>>  ui/pages/guest-storage-add.html.tmpl       |  58 ++++--
>>>  2 files changed, 315 insertions(+), 63 deletions(-)
>>>
>>> diff --git a/ui/js/src/kimchi.guest_storage_add.main.js b/ui/js/src/kimchi.guest_storage_add.main.js
>>> index 6e1926b..9c3f6e4 100644
>>> --- a/ui/js/src/kimchi.guest_storage_add.main.js
>>> +++ b/ui/js/src/kimchi.guest_storage_add.main.js
>>> @@ -15,6 +15,37 @@
>>>   * See the License for the specific language governing permissions and
>>>   * limitations under the License.
>>>   */
>>> +kimchi.switchPage = function(fromPageId, toPageId, direction) {
>>> +    $('.tab-content').css('overflow', 'hidden');
>>> +    direction = direction || 'left';
>>> +    var toLeftBegin;
>>> +    var fromLeftEnd;
>>> +    if ('left' === direction) {
>>> +        toLeftBegin = '100%';
>>> +        fromLeftEnd = '-100%';
>>> +    } else if ('right' === direction) {
>>> +        toLeftBegin = '-100%';
>>> +        fromLeftEnd = '100%';
>>> +    }
>>> +    var formPage = $('#' + fromPageId);
>>> +    var toPage = $('#' + toPageId);
>>> +    toPage.css({
>>> +        left: toLeftBegin
>>> +    });
>>> +    formPage.animate({
>>> +        left: fromLeftEnd,
>>> +        opacity: 0.1
>>> +    }, 400, function() {
>>> +        $('.tab-content').css('overflow', 'visible');
>>> +    });
>>> +    toPage.animate({
>>> +        left: '0',
>>> +        opacity: 1
>>> +    }, 400, function() {
>>> +        $('.tab-content').css('overflow', 'visible');
>>> +    });
>>> +};
>>> +
>>>  kimchi.guest_storage_add_main = function() {
>>>      var types = [{
>>>          label: 'cdrom',
>>> @@ -35,9 +66,27 @@ kimchi.guest_storage_add_main = function() {
>>>      var pathTextbox = $('input[name="path"]', storageAddForm);
>>>      var poolTextbox = $('select#guest-disk-pool', storageAddForm);
>>>      var volTextbox = $('select#guest-disk-vol', storageAddForm);
>>> +    var newPoolTextbox = $('select#guest-disk-pool-new', storageAddForm);
>>> +    var capacityTextbox = $('input[name="capacity"]', storageAddForm);
>>> +    var formatTextbox = $('select#guest-disk-format-new', storageAddForm);
>>>      var selectStorageTypeHTML = '';
>>>      var selectStoragePoolHTML = '';
>>>      var selectStorageVolHTML  = '';
>>> +    var rbExisting = 'false';
>>> +
>>> +    var getFormatList = function() {
>>> +        var format = ["bochs", "cloop", "cow", "dmg", "qcow", "qcow2", "qed", "raw", "vmdk", "vpc"];
>>> +        var selectFormatHTML = '';
>>> +        var i;
>>> +        for (i = 0; i < format.length; i++) {
>>> +            selectFormatHTML += '<option value="'+ format[i] + '">' + format[i] + '</option>';
>>> +        }
>>> +        formatTextbox.empty();
>>> +        formatTextbox.append(selectFormatHTML);
>>> +        $(formatTextbox).change();
>>> +        formatTextbox.selectpicker();
>>> +        $('.selectpicker').selectpicker('refresh');
>>> +    };
>>>
>>>      typeTextbox.change(function() {
>>>          var pathObject = {'cdrom': ".path-section", 'disk': '.volume-section'};
>>> @@ -45,41 +94,82 @@ kimchi.guest_storage_add_main = function() {
>>>          $.each(pathObject, function(type, value) {
>>>              if(selectType === type){
>>>                  $(value).removeClass('hidden');
>>> +            } else if ((selectType === null) && (type === 'disk')) {
>>> +                $(value).removeClass('hidden');
>>>              } else {
>>>                  $(value).addClass('hidden');
>>>              }
>>>          });
>>> -
>>>          if ($(".path-section").hasClass('hidden')) {
>>> -            $(poolTextbox).val('default');
>>> -            $(poolTextbox).change();
>>>              $(pathTextbox).val("");
>>> -        }
>>> -        else {
>>> +            if ($('#new-disk').checked) {
>>> +                $('#existing-disk-box').addClass('hidden');
>>> +                $(newPoolTextbox).val('default');
>>> +                $(newPoolTextbox).change();
>>> +            } else if ($('#existing-disk').checked) {
>>> +                $('#new-disk-box').addClass('hidden');
>>> +                $(poolTextbox).val('default');
>>> +                $(poolTextbox).change();
>>> +            } else {
>>> +                if (rbExisting === 'true') {
>>> +                    $('#new-disk-box').addClass('hidden');
>>> +                } else {
>>> +                    $('#existing-disk-box').addClass('hidden');
>>> +                }
>>> +            }
>>> +        } else {
>>>              $(poolTextbox).val("");
>>>              $(volTextbox).val("");
>>> +            $(newPoolTextbox).val("");
>>> +            $(capacityTextbox).val("");
>>> +            $(formatTextbox).val("");
>>>          }
>>>          $('.selectpicker').selectpicker('refresh');
>>>      });
>>>
>>> -    kimchi.listStoragePools(function(result) {
>>> -        var options = [];
>>> -        if (result && result.length) {
>>> -            $.each(result, function(index, storagePool) {
>>> -                if ((storagePool.state==="active") && (storagePool.type !== 'kimchi-iso')) {
>>> -                    options.push({
>>> -                        label: storagePool.name,
>>> -                        value: storagePool.name
>>> -                        });
>>> -                    selectStoragePoolHTML += '<option value="'+ storagePool.name + '">' + storagePool.name + '</option>';
>>> +    var getStoragePools = function(radioButton) {
>>> +        kimchi.listStoragePools(function(result) {
>>> +            var options = [];
>>> +            selectStoragePoolHTML = ''; //reset string
>>> +            if (result && result.length) {
>>> +                $.each(result, function(index, storagePool) {
>>> +                    if (radioButton === 'existing') {
>>> +                        if ((storagePool.state==="active") && (storagePool.type !== 'kimchi-iso')) {
>>> +                            options.push({
>>> +                                label: storagePool.name,
>>> +                                value: storagePool.name
>>> +                                });
>>> +                            selectStoragePoolHTML += '<option value="'+ storagePool.name + '">' + storagePool.name + '</option>';
>>> +                        }
>>> +                    } else { //new disk
>>> +                        if ((storagePool.type != 'iscsi') && (storagePool.type != 'scsi')) {
>>> +                            options.push({
>>> +                                label: storagePool.name,
>>> +                                value: storagePool.name
>>> +                                });
>>> +                            selectStoragePoolHTML += '<option value="'+ storagePool.name + '">' + storagePool.name + '</option>';
>>> +                        }
>>>                      }
>>> -
>>>                  });
>>> -            poolTextbox.append(selectStoragePoolHTML);
>>> -            poolTextbox.val(options[0].value);
>>> -            poolTextbox.selectpicker();
>>> -        }
>>> -    });
>>> +                if (radioButton === 'existing') {
>>> +                    poolTextbox.empty();
>>> +                    poolTextbox.append(selectStoragePoolHTML);
>>> +                    $(poolTextbox).change();
>>> +	            poolTextbox.selectpicker();
>>> +                    $('.selectpicker').selectpicker('refresh');
>>> +                } else if (radioButton === 'new') { //new disk
>>> +                    newPoolTextbox.empty();
>>> +                    newPoolTextbox.append(selectStoragePoolHTML);
>>> +                    $(newPoolTextbox).val(options[0].value);
>>> +                    newPoolTextbox.selectpicker();
>>> +                    getFormatList();
>>> +                }
>>> +            }
>>> +        });
>>> +    };
>>> +
>>> +    //First time retrieving list of Storage Pools - defaulting to new disk
>>> +    getStoragePools('new');
>>>
>>>      poolTextbox.change(function() {
>>>          var options = [];
>>> @@ -109,13 +199,15 @@ kimchi.guest_storage_add_main = function() {
>>>                      $(volTextbox).prop('disabled',true);
>>>                      $(submitButton).prop('disabled', true);
>>>                  }
>>> -                volTextbox.selectpicker();
>>> -                $('.selectpicker').selectpicker('refresh');
>>> +            } else {
>>> +                $(volTextbox).prop('disabled',true);
>>> +                $(submitButton).prop('disabled', true);
>>>              }
>>> +            volTextbox.selectpicker();
>>> +            $('.selectpicker').selectpicker('refresh');
>>>          }, null, false);
>>>      });
>>>
>>> -
>>>      typeTextbox.change(function() {
>>>          var pathObject = {'cdrom': ".path-section", 'disk': '.volume-section'}; var selectType = $(this).val(); @@
>>> -128,6 +220,47 @@ kimchi.guest_storage_add_main = function() { });
>>> }); + var currentPage = 'new-disk-box'; +
>>> $('#existing-disk').change(function() { + if (this.checked) { +
>>> rbExisting = 'true'; + if (currentPage === 'new-disk-box') { +
>>> kimchi.switchPage(currentPage, 'existing-disk-box', 'right'); + } +
>>> currentPage = 'existing-disk-box'; +
>>> $('#existing-disk-box').removeClass('hidden'); +
>>> $('#new-disk-box').addClass('hidden'); + $('#guest-storage-add-window
>>> .modal-body .template-pager').animate({ + height: "200px"
>>> +            }, 300);
>>> +            getStoragePools('existing');
>>> +            $(pathTextbox).val("");
>>> +            $(newPoolTextbox).val("");
>>> +            $(capacityTextbox).val("");
>>> +            $(formatTextbox).val("");
>>> +        }
>>> +    });
>>> +
>>> +    $('#new-disk').change(function() {
>>> +        if (this.checked) {
>>> +            rbExisting = 'false';
>>> +            if (currentPage === 'existing-disk-box') {
>>> +                kimchi.switchPage(currentPage, 'new-disk-box', 'right');
>>> +            } else if($(capacityTextbox).is(":visible") === false ) {
>>> +                 kimchi.switchPage(currentPage, 'new-disk-box', 'right');
>>> +            }
>>> +            currentPage = 'new-disk-box';
>>> +            $('#existing-disk-box').addClass('hidden');
>>> +            $('#new-disk-box').removeClass('hidden');
>>> +            $('#guest-storage-add-window .modal-body .template-pager').animate({
>>> +                height: "300px"
>>> +            }, 400);
>>> +            $(pathTextbox).val("");
>>> +            $(poolTextbox).val("");
>>> +            $(volTextbox).val("");
>>> +        }
>>> +    });
>>> +
>>>      if (kimchi.thisVMState === 'running') {
>>>          types =typesRunning;
>>>          $(typeTextbox).val('disk');
>>> @@ -155,15 +288,104 @@ kimchi.guest_storage_add_main = function() {
>>>          }
>>>      };
>>>
>>> +    var onError = function(result) {
>>> +        if(!result) {
>>> +            return;
>>> +        }
>>> +        var msg = result['message'] || (
>>> +            result['responseJSON'] && result['responseJSON']['reason']
>>> +        );
>>> +        wok.message.error(msg);
>>> +    };
>>> +
>>> +    var addStorage = function(settings) {
>>> +        kimchi.addVMStorage(settings, function(result) {
>>> +            wok.window.close();
>>> +            wok.topic('kimchi/vmCDROMAttached').publish({
>>> +            result: result
>>> +            });
>>> +        }, function(result) {
>>> +            var errText = result['reason'] ||
>>> +            result['responseJSON']['reason'];
>>> +            wok.message.error(errText, '#alert-modal-container2');
>>> +            $.each([submitButton, pathTextbox, poolTextbox, volTextbox, newPoolTextbox, capacityTextbox, formatTextbox], function(i, c) {
>>> +                $(c).prop('disabled', false);
>>> +            });
>>> +        });
>>> +    }
>>> +
>>> +    var createVol = function(settings, addVolSettings) {
>>> +        kimchi.createVolumeWithCapacity('default', {
>>> +            name: settings['vol'],
>>> +            format: settings['format'],
>>> +            capacity: settings['capacity']
>>> +        }, function(result) {
>>> +            var taskId = result.id;
>>> +            function monitorTask() {
>>> +                kimchi.getTask(taskId, function(result) {
>>> +                    var status = result.status;
>>> +                    if (status === "finished") {
>>> +                        //Now add newly created volume to VM
>>> +                        addStorage(addVolSettings);
>>> +                    } else if (status === "running") {
>>> +                        setTimeout(monitorTask, 2000);
>>> +                        $(submitButton).prop('disabled', true);
>>> +                    } else if (status === "failed") {
>>> +                        var errText = result['reason'] ||
>>> +                        result['responseJSON']['reason'];
>>> +                        $(submitButton).prop('disabled', true);
>>> +                        wok.message.error(errText, '#alert-modal-container2');
>>> +                    }
>>> +                });
>>> +            }
>>> +            setTimeout(monitorTask, 2000);
>>> +        }, onError);
>>> +    };
>>> +
>>> +    var bNewDisk = 'false';
>>> +
>>>      var validateDisk = function(settings) {
>>> -        if (settings['pool'] && settings['vol']){
>>> -           // Delete path property since it's not needed for disk
>>> -           delete settings['path'];
>>> -           return true;
>>> +        // Determine whether it's existing disk or new disk
>>> +        if($(capacityTextbox).is(":visible") === true ) {
>>> +            bNewDisk = 'true';
>>>          }
>>> -        else {
>>> -            wok.message.error(i18n['KCHVMSTOR0002E'],'#alert-modal-container2');
>>> -            return false;
>>> +        if (bNewDisk === 'true') {
>>> +            if (settings['newpool'] && settings['capacity'] && settings['format']){
>>> +                //Change settings['newpool'] to settings['pool']
>>> +                settings['pool']=settings['newpool'];
>>> +                var vmname = settings['vm'];
>>> +                vmname = vmname + new Date().getTime();
>>> +                //Unique vol name to be created
>>> +                settings['vol']=vmname;
>>> +                //This is all that is needed for attaching newly created volume to VM
>>> +                var addVolSettings = {
>>> +                    vm: settings['vm'],
>>> +                    type: settings['type'],
>>> +                    vol:  settings['vol'],
>>> +                    pool: settings['pool']
>>> +                };
>>> +                var sizeInMB = parseInt(settings['capacity']) * 1024;
>>> +                settings['capacity'] = sizeInMB;
>>> +                //These need to be deleted so they don't get passed to backend
>>> +                delete settings['path'];
>>> +                delete settings['newpool'];
>>> +                //Create an empty storage volume and attach to VM if successful
>>> +                createVol(settings, addVolSettings);
>>> +                return true;
>>> +            } else {
>>> +                wok.message.error(i18n['KCHVMSTOR0002E'],'#alert-modal-container2');
>>> +                return false;
>>> +            }
>>> +        } else {
>>> +            if (settings['pool'] && settings['vol']){
>>> +                // Delete path property since it's not needed for disk
>>> +                delete settings['path'];
>>> +                return true;
>>> +            }
>>> +            else {
>>> +                wok.message.error(i18n['KCHVMSTOR0002E'],'#alert-modal-container2');
>>> +                return false;
>>> +            }
>>>          }
>>>      };
>>>
>>> @@ -172,6 +394,11 @@ kimchi.guest_storage_add_main = function() {
>>>          if (submitButton.prop('disabled')) {
>>>              return false;
>>>          }
>>> +        var bNewDisk = 'false';
>>> +        // Determine whether it's existing disk or new disk
>>> +        if($(capacityTextbox).is(":visible") === true ) {
>>> +            bNewDisk = 'true';
>>> +        }
>>>
>>>          var formData = storageAddForm.serializeObject();
>>>          var settings = {
>>> @@ -179,40 +406,30 @@ kimchi.guest_storage_add_main = function() {
>>>              type: typeTextbox.val(),
>>>              path: pathTextbox.val(),
>>>              pool: poolTextbox.val(),
>>> -            vol: volTextbox.val()
>>> +            vol: volTextbox.val(),
>>> +            newpool: newPoolTextbox.val(),
>>> +            format: formatTextbox.val(),
>>> +            capacity: capacityTextbox.val()
>>>          };
>>>
>>>          $(submitButton).prop('disabled', true);
>>> -        $.each([pathTextbox, poolTextbox, volTextbox], function(i, c) {
>>> +        $.each([pathTextbox, poolTextbox, volTextbox, newPoolTextbox, capacityTextbox, formatTextbox], function(i, c) {
>>>              $(c).prop('disabled', true);
>>>          });
>>>          // Validate form for cdrom and disk
>>>          validateSpecifiedForm = validator[settings['type']];
>>>          if (!validateSpecifiedForm(settings)) {
>>>              $(submitButton).prop('disabled', false);
>>> -            $.each([submitButton, pathTextbox, poolTextbox, volTextbox], function(i, c) {
>>> +            $.each([submitButton, pathTextbox, poolTextbox, volTextbox, newPoolTextbox, capacityTextbox, formatTextbox], function(i, c) {
>>>                  $(c).prop('disabled', false);
>>>              });
>>>              return false;
>>>          }
>>>          $(submitButton).addClass('loading').text(i18n['KCHVMCD6003M']);
>>>
>>> -        kimchi.addVMStorage(settings, function(result) {
>>> -            wok.window.close();
>>> -            wok.topic('kimchi/vmCDROMAttached').publish({
>>> -                result: result
>>> -            });
>>> -        }, function(result) {
>>> -            var errText = result['reason'] ||
>>> -                result['responseJSON']['reason'];
>>> -            wok.message.error(errText, '#alert-modal-container2');
>>> -
>>> -            $.each([submitButton, pathTextbox, poolTextbox, volTextbox], function(i, c) {
>>> -                $(c).prop('disabled', false);
>>> -            });
>>> -            $(submitButton).removeClass('loading').text(i18n['KCHVMCD6002M']);
>>> -        });
>>> -
>>> +        if(bNewDisk === 'false'){
>>> +            addStorage(settings);
>>> +        }
>>>          event.preventDefault();
>>>      };
>>>
>>> @@ -224,5 +441,8 @@ kimchi.guest_storage_add_main = function() {
>>>      volTextbox.on('change propertychange', function (event) {
>>>          $(submitButton).prop('disabled', $(this).val() === '');
>>>      });
>>> +    capacityTextbox.on('change input propertychange', function(event) {
>>> +        $(submitButton).prop('disabled', $(this).val() === '');
>>> +    });
>>>
>>>  };
>>> diff --git a/ui/pages/guest-storage-add.html.tmpl b/ui/pages/guest-storage-add.html.tmpl
>>> index bde0eee..660c274 100644
>>> --- a/ui/pages/guest-storage-add.html.tmpl
>>> +++ b/ui/pages/guest-storage-add.html.tmpl
>>> @@ -1,7 +1,7 @@
>>>  #*
>>>   * Project Kimchi
>>>   *
>>> - * Copyright IBM, Corp. 2014
>>> + * Copyright IBM, Corp. 2014-2016
>>>   *
>>>   * Licensed under the Apache License, Version 2.0 (the "License");
>>>   * you may not use this file except in compliance with the License.
>>> @@ -41,18 +41,50 @@
>>>                      </select>
>>>                      <p class="help-block"><i class="fa fa-info-circle"></i> $_("The device type. Currently, \"cdrom\" and \"disk\" are supported.")</p>
>>>                  </div>
>>> -                <div class="volume-section hidden">
>>> -                    <div class="form-group">
>>> -                        <label>$_("Storage Pool")</label>
>>> -                        <select id="guest-disk-pool" class="selectpicker col-md-12 col-lg-12">
>>> -                        </select>
>>> -                        <p class="help-block"><i class="fa fa-info-circle"></i> $_("Storage pool which volume located in")</p>
>>> +                <div class="volume-section hidden form-group">
>>> +                    <div class="template-modal-container">
>>> +                        <div>
>>> +                            <span id="alert-modal-container"></span>
>>> +                            <input type="radio" checked="checked" name="disk-btn" id="new-disk" value="new-disk" class="wok-radio">
>>> +                            <label for="new-disk">$_("Create a new disk")</label>
>>> +                            <input type="radio" name="disk-btn" id="existing-disk" value="existing-disk" class="wok-radio">
>>> +                            <label for="existing-disk">$_("Select an existing disk")</label>
>>> +                        </div>
>>>                      </div>
>>> -                    <div class="form-group">
>>> -                        <label>$_("Storage Volume")</label>
>>> -                        <select id="guest-disk-vol" class="selectpicker col-md-12 col-lg-12">
>>> -                        </select>
>>> -                        <p class="help-block"><i class="fa fa-info-circle"></i> $_("Storage volume to be attached")</p>
>>> +                    <div class="template-pager">
>>> +                        <div class="page" id="new-disk-box">
>>> +                            <div class="form-group">
>>> +                                <label>$_("Storage Pool")</label>
>>> +                                <select id="guest-disk-pool-new" class="selectpicker col-md-12 col-lg-12">
>>> +                                </select>
>>> +                                <p class="help-block"><i class="fa fa-info-circle"></i> $_("Storage pool to create the volume in")</p>
>>> +                            </div>
>>> +                            <div class="form-group">
>>> +                                <label>$_("Disk Size (GB)")</label>
>>> +                                <input type="number" class="form-control" name="capacity" min="1" id="capacity" />
>>> +                                <p class="help-block"><i class="fa fa-info-circle"></i> $_("New disk size to be created")</p>
>>> +                            </div>
>>> +                            <div class="form-group">
>>> +                                <label>$_("Format")</label>
>>> +                                <select id="guest-disk-format-new" class="selectpicker col-md-12 col-lg-12">
>>> +                                </select>
>>> +                                <p class="help-block"><i class="fa fa-info-circle"></i> $_("Format of the new disk to be created")</p>
>>> +                            </div>
>>> +                        </div>
>>> +                        <div class="page" id="existing-disk-box">
>>> +                            <div class="form-group">
>>> +                                <label>$_("Storage Pool")</label>
>>> +                                <select id="guest-disk-pool" class="selectpicker col-md-12 col-lg-12">
>>> +                                </select>
>>> +                                <p class="help-block"><i class="fa fa-info-circle"></i> $_("Storage pool in which the volume is located in")</p>
>>> +                            </div>
>>> +                            <div class="form-group">
>>> +                                <label>$_("Storage Volume")</label>
>>> +                                <select id="guest-disk-vol" class="selectpicker col-md-12 col-lg-12">
>>> +                                </select>
>>> +                                <p class="help-block"><i class="fa fa-info-circle"></i> $_("Storage volume to be attached")</p>
>>> +                            </div>
>>> +                        </div>
>>>                      </div>
>>>                  </div>
>>>                  <div class="path-section form-group">
>>> @@ -72,4 +104,4 @@
>>>      kimchi.guest_storage_add_main();
>>>  </script>
>>>  </body>
>>> -</html>
>>> \ No newline at end of file
>>> +</html>
>>>
>> _______________________________________________
>> Kimchi-devel mailing list
>> Kimchi-devel at ovirt.org
>> http://lists.ovirt.org/mailman/listinfo/kimchi-devel
>>
> 




More information about the Kimchi-devel mailing list