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

Socorro Stoppler socorro at linux.vnet.ibm.com
Mon Feb 8 16:38:05 UTC 2016


Thanks for the comments.  Will be sending v3 shortly.

On 02/05/2016 11:25 AM, Aline Manera wrote:
>
>
> 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.
Fixed in v3.
>
>> +        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.
Fixed in v3.  For this portion of the code, this is when existing disk.  
Added this change below for when it's new disk and 'kimchi_isos' no 
longer shows up.
>
>> + 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