[Kimchi-devel] [PATCH V2] [Kimchi] Create new volume with .img extension and attach to VM

Aline Manera alinefm at linux.vnet.ibm.com
Fri Feb 5 19:25:20 UTC 2016



On 02/05/2016 02:14 PM, Socorro Stoppler wrote:
> 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..9c71486 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"];

Place 'qcow2' as the first option, so we implicitly guide user to use it.

> +        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')) {

I don't know why it is not working, but the kimchi_isos pool is being 
listed to me in the combo box.

> +                            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 + ".img";
> +                //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>




More information about the Kimchi-devel mailing list