[Kimchi-devel] [PATCH 2/2] Fixing Live Migration for the new-ui Guests tab

sguimaraes943 at gmail.com sguimaraes943 at gmail.com
Tue Dec 1 21:21:22 UTC 2015


From: samhenri <samuel.guimaraes at eldorado.org.br>

Signed-off-by: samhenri <samuel.guimaraes at eldorado.org.br>
---
 .../kimchi/ui/js/src/kimchi.guest_livemigration.js | 115 ++++++++++++++++++++
 .../plugins/kimchi/ui/js/src/kimchi.guest_main.js  | 120 +++++++++++++--------
 .../kimchi/ui/pages/guest-migration.html.tmpl      |  78 +++++++-------
 src/wok/plugins/kimchi/ui/pages/guest.html.tmpl    |   4 +-
 src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl     |   2 +
 src/wok/plugins/kimchi/ui/pages/network.html.tmpl  |   1 -
 ui/css/src/modules/_validation.scss                |  11 ++
 ui/css/src/wok.scss                                |   2 +
 8 files changed, 247 insertions(+), 86 deletions(-)
 create mode 100644 src/wok/plugins/kimchi/ui/js/src/kimchi.guest_livemigration.js
 create mode 100644 ui/css/src/modules/_validation.scss

diff --git a/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_livemigration.js b/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_livemigration.js
new file mode 100644
index 0000000..f8c6f0d
--- /dev/null
+++ b/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_livemigration.js
@@ -0,0 +1,115 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013-2015
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+kimchi.guest_livemigration_main = function() {
+    kimchi.setupLiveMigrationFormEvent();
+    kimchi.initLiveMigrationDialog();
+};
+
+kimchi.startLiveMigration = function() {
+    var errorCallback = function() {
+        $("#migrateFormOk").prop("disabled", false);
+        $("#remoteHostName").removeAttr("readonly");
+        $("#username").removeAttr("readonly");
+        $("#password").removeAttr("readonly");
+        $("#deleteVM").removeAttr("readonly");
+        $("#deleteVM").prop("checked", false);
+        $("#migrateFormOk").text(i18n.KCHAPI6011M);
+    };
+    var values = kimchi.getLiveMigrationDialogValues();
+    var data = {
+        remote_host: values.remote_host,
+        user: values.user,
+        password: values.password
+    };
+    kimchi.migrateGuest(kimchi.selectedGuest, data, function() {
+            kimchi.listVmsAuto();
+            wok.window.close();
+        }, function(suc) {
+            if ($("#deleteVM").prop("checked")) {
+                if (deleteQueue.indexOf(kimchi.selectedGuest) === -1) {
+                    deleteQueue.push(kimchi.selectedGuest);
+                }
+            }
+        },
+        function(err) {
+            wok.message.error(err.responseJSON.reason, "#alert-modal-container");
+            errorCallback();
+        });
+};
+
+kimchi.initLiveMigrationDialog = function(okCallback) {
+    $("#migrateFormOk").on("click", function() {
+        $("#migrateFormOk").prop("disabled", true);
+        $("#remoteHostName").prop("readonly", "readonly");
+        $("#username").prop("readonly", "readonly");
+        $("#password").prop("readonly", "readonly");
+        $("#deleteVM").prop("readonly", "readonly");
+        $("#migrateFormOk").text(i18n.KCHAPI6010M);
+        kimchi.startLiveMigration();
+    });
+};
+
+
+kimchi.getLiveMigrationDialogValues = function() {
+    var data = {
+        remote_host: $("#remoteHostName").val(),
+        user: $("#username").val(),
+        password: $("#password").val()
+    };
+    return data;
+};
+
+kimchi.setupLiveMigrationFormEvent = function() {
+    $("#migrateFormOk").prop("disabled", true);
+    $("#remoteHostName").on("change keyup", function(event) {
+        if (!this.value) {
+            $(this).parent().addClass('has-error');
+        } else {
+            $(this).parent().removeClass('has-error');
+        }
+        kimchi.updateLiveMigrationButton();
+    });
+    $("#username").on("change keyup", function(event) {
+        if (this.value && !$("#password").val()) {
+            $("#username").parent().removeClass('has-warning');
+            $("#password").parent().addClass('has-warning');
+        } else {
+            $("#username").parent().removeClass('has-warning');
+            $("#password").parent().removeClass('has-warning');
+        }
+        kimchi.updateLiveMigrationButton();
+    });
+    $("#password").on("change keyup", function(event) {
+        if (this.value && !$("#username").val()) {
+            $("#username").parent().addClass('has-warning');
+        } else {
+            $("#username").parent().removeClass('has-warning');
+            $("#password").parent().removeClass('has-warning');
+            kimchi.updateLiveMigrationButton();
+        }
+    });
+};
+
+kimchi.updateLiveMigrationButton = function() {
+    if ($("input[type='text']").parent().hasClass("has-error") || $("input[type='text']").parent().hasClass("has-warning")) {
+        $("#migrateFormOk").prop("disabled", true);
+    } else {
+        $("#migrateFormOk").prop("disabled", false);
+    }
+};
diff --git a/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_main.js b/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_main.js
index de832ea..5b8993a 100644
--- a/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_main.js
+++ b/src/wok/plugins/kimchi/ui/js/src/kimchi.guest_main.js
@@ -45,6 +45,8 @@ kimchi.sampleGuestObject = {
     "access": "full"
 };
 
+var deleteQueue = [];
+
 kimchi.vmstart = function(event) {
     var button = event.target;
     if (!$(button).hasClass('loading')) {
@@ -229,6 +231,14 @@ kimchi.vmedit = function(event) {
     });
 };
 
+kimchi.vmmigrate = function(event) {
+    var button = event.target;
+    var vm = $(button).closest('li[name=guest]');
+    var vm_id = $(vm).attr("id");
+    kimchi.selectedGuest = vm_id;
+    wok.window.open('plugins/kimchi/guest-migration.html');
+};
+
 kimchi.openVmConsole = function(event) {
     var button = event.target;
     var vm = $(button).closest('li[name=guest]');
@@ -303,24 +313,66 @@ kimchi.listVmsAuto = function() {
             }, null, true);
             return guests;
         };
-    var getMigratingGuests = function(){
-        var guests = [];
-        kimchi.getTasksByFilter('status=running&target_uri='+encodeURIComponent('^/vms/.+/migrate'), function(tasks) {
-            for(var i=0;i<tasks.length;i++){
-                var guestUri = tasks[i].target_uri;
-                var guestName = guestUri.split('/')[4]
-                guests.push($.extend({}, kimchi.sampleGuestObject, {name: guestName, isMigrating: true}));
-                if(kimchi.trackingTasks.indexOf(tasks[i].id)==-1)
-                    kimchi.trackTask(tasks[i].id, null, function(err){
-                        wok.message.error(err.message);
-                    }, null);
+        var getMigratingGuests = function() {
+            var guests = [];
+            kimchi.getTasksByFilter('status=running&target_uri=' + encodeURIComponent('^/vms/.+/migrate'), function(tasks) {
+                for (var i = 0; i < tasks.length; i++) {
+                    var guestUri = tasks[i].target_uri;
+                    var guestName = guestUri.split('/')[4]
+                    guests.push($.extend({}, kimchi.sampleGuestObject, {
+                        name: guestName,
+                        isMigrating: true
+                    }));
+                    if (kimchi.trackingTasks.indexOf(tasks[i].id) == -1)
+                        kimchi.trackTask(tasks[i].id, null, function(err) {
+                            wok.message.error(err.message);
+                        }, null);
+                }
+            }, null, true);
+            return guests;
+        };
+        var getMigratedGuests = function() {
+            var guests = [];
+            kimchi.getTasksByFilter('status=finished&target_uri=' + encodeURIComponent('^/plugins/kimchi/vms/.+/migrate'), function(tasks) {
+                for (var i = 0; i < tasks.length; i++) {
+                    var guestUri = tasks[i].target_uri;
+                    var guestName = guestUri.split('/')[4]
+                    guests.push(guestName);
+                    if (kimchi.trackingTasks.indexOf(tasks[i].id) == -1)
+                        kimchi.trackTask(tasks[i].id, null, function(err) {
+                            wok.message.error(err.message);
+                        }, null);
+                }
+            }, null, true);
+            return guests;
+        };
+        var deleteMigratedGuests = function() {
+            deleteQueue.reverse();
+            migratedGuests = getMigratedGuests();
+            migratedGuests.sort();
+            var i = deleteQueue.length;
+            while (i-- && i > 0) {
+                if (migratedGuests.indexOf(deleteQueue[i]) > -1) {
+                    kimchi.deleteVM(deleteQueue[i], function(result) {
+                        clearFromQueue(deleteQueue[i]);
+                    }, function(err) {
+                        wok.message.error(err.responseJSON.reason);
+                    });
+                }
             }
-        }, null, true);
-        return guests;
-    };
+        };
+
+        var clearFromQueue = function(item) {
+            var toDequeue = deleteQueue.indexOf(item);
+            if (toDequeue != -1) {
+                deleteQueue.splice(toDequeue, 1);
+            };
+        };
+
         kimchi.listVMs(function(result, textStatus, jqXHR) {
+                deleteMigratedGuests();
                 if (result && textStatus == "success") {
-            result = getMigratingGuests().concat(result);            
+                    result = getMigratingGuests().concat(result);
                     result = getCloningGuests().concat(result);
                     result = getCreatingGuests().concat(result);
                     if (result.length) {
@@ -359,27 +411,6 @@ kimchi.listVmsAuto = function() {
 
 kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
     var result = kimchi.guestElem.clone();
-
-    var initializeMigratePanel = function() {
-        $("#migrateFormOk").on("click", function() {
-            //TODO:  Get values from UI for remote_host, user, password
-            var data = {
-                    "remote_host" : "ltc-hab1.aus.stglabs.ibm.com",
-                    "user" : "root",
-                    "password" : "passw0rd"
-            };
-            //TODO:  Need to get guest to be passed in here               
-            kimchi.migrateGuest(guest, data, function(){
-                kimchi.listVmsAuto();
-                wok.window.close();
-                }, function(err) {
-                    wok.message.error(err.responseJSON.reason);               
-            });    
-        });
-    }
-    
-    initializeMigratePanel();
-
     //Setup the VM list entry
     var currentState = result.find('.guest-state');
     var vmRunningBool = (vmObject.state == "running");
@@ -399,7 +430,6 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
 
     //Add the OS Type and Icon
     var osType = result.find('.distro-icon');
-    console.log(vmObject);
     if (vmObject.icon == 'plugins/kimchi/images/icon-fedora.png') {
         osType.addClass('icon-fedora');
         osType.attr('val', 'Fedora');
@@ -434,7 +464,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
     //Setup the VM console thumbnail display
     var curImg = vmObject.icon;
     if (vmObject.screenshot) {
-        curImg = vmObject.screenshot.replace(/^\//,'');
+        curImg = vmObject.screenshot.replace(/^\//, '');
     }
     var load_src = curImg || 'plugins/kimchi/images/icon-vm.png';
     var tile_src = prevScreenImage || vmObject['load-src'];
@@ -590,7 +620,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
         result.addClass('inactive');
         result.find('.distro-icon').addClass('inactive');
         result.find('.vnc-link').css("display", "none");
-        result.find('.column-vnc').html('--');        
+        result.find('.column-vnc').html('--');
         //Hide PowerOff
         guestActions.find(".shutoff-hidden").hide();
         //Hide Pause
@@ -625,7 +655,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
     }
 
     //Setup action event handlers
-    if(!(vmObject.isCloning || vmObject.isCreating || vmObject.isMigrating)){
+    if (!(vmObject.isCloning || vmObject.isCreating || vmObject.isMigrating)) {
 
         guestActions.find("[name=vm-start]").on("click", function(event) {
             event.preventDefault();
@@ -684,17 +714,17 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) {
                 });
             }, null);
         });
-        guestActions.find("[name=vm-migrate]").click(function(){
-            var guest = $(this).closest('li[name=guest]').attr("id");
-            wok.window.open('plugins/kimchi/guest-migration.html');
-        });       
+        guestActions.find("[name=vm-migrate]").on('click', function() {
+            event.preventDefault();
+            kimchi.vmmigrate(event);
+        });
     } else {
         guestActions.find('.btn').attr('disabled', true);
         result.find('.guest-pending').removeClass('hide-content');
         pendingText = result.find('.guest-pending .text')
         if (vmObject.isCloning)
             pendingText.text(i18n['KCHAPI6009M']);
-        else if(vmObject.isMigrating)
+        else if (vmObject.isMigrating)
             pendingText.text("Migrating");
         else
             pendingText.text(i18n['KCHAPI6008M']);
diff --git a/src/wok/plugins/kimchi/ui/pages/guest-migration.html.tmpl b/src/wok/plugins/kimchi/ui/pages/guest-migration.html.tmpl
index 278d22c..7cac2c5 100644
--- a/src/wok/plugins/kimchi/ui/pages/guest-migration.html.tmpl
+++ b/src/wok/plugins/kimchi/ui/pages/guest-migration.html.tmpl
@@ -21,45 +21,47 @@
 #silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True)
 #silent _ = t.gettext
 #silent _t = t.gettext
-<div id="migrate-guest-window" class="window modal-content">
-    <div class="modal-header">
-        <h4 class="modal-title" id="migrateModalLabel">$_("Migrate a Guest")</h4>
-    </div>
-    <div id="migrateInfo" class="modal-body">
-         <div class="alert alert-warning" role="alert">Disclaimer: This process cannot be stopped after started,
-            can take a long time to complete and will turn off the VM on this Hypervisor when it is successfully
-            migrated to the remote destination.
-        </div>
-         <div class="form-group">
-            <label for="remoteHostName">$_("Remote Server")</label>
-            <input type="text" class="form-control" id="remoteHostName" />
-            <p class="help-block">
-                <i class="fa fa-info-circle"></i> $_("IP Address or Hostname")</p>
-        </div>
-        <div class="alert alert-info" role="alert">The following fields are optional. Fill them if you want Kimchi to
-            setup a password-less ssh session between the localhost and the remote host. The setup process will only 
-            be successful if the user has 'SUDO ALL' permission in the remote machine.
+<!DOCTYPE html>
+<html>
+<body>
+    <div id="migrate-guest-window" class="window modal-content">
+        <div class="modal-header">
+            <h4 class="modal-title" id="migrateModalLabel">$_("Migrate a Guest")</h4>
         </div>
-        <div class="form-group">
-            <label for="user">$_("User")</label>
-            <input type="text" class="form-control" id="user" />
-            <p class="help-block">
-                <i class="fa fa-info-circle"></i> $_("Username of the remote host")</p>
+        <div id="migrateInfo" class="modal-body">
+            <span id="alert-modal-container"></span>
+            <div class="alert alert-warning" role="alert">$_("Disclaimer: This process cannot be stopped after started, can take a long time to complete and will turn off the VM on this Hypervisor when it is successfully migrated to the remote destination.")</div>
+            <div class="form-group">
+                <label for="remoteHostName">$_("Remote Server")</label>
+                <input type="text" class="form-control" id="remoteHostName" />
+                <p class="help-block">
+                    <i class="fa fa-info-circle"></i> $_("IP Address or Hostname")</p>
+            </div>
+            <div class="alert alert-info" role="alert">$_("The following fields are optional. Fill them if you want Kimchi to setup a password-less ssh session between the localhost and the remote host. The setup process will only be successful if the user has 'SUDO ALL' permission in the remote machine.")</div>
+            <div class="form-group">
+                <label for="username">$_("User")</label>
+                <input type="text" class="form-control" id="username" />
+                <p class="help-block">
+                    <i class="fa fa-info-circle"></i> $_("Username of the remote host")</p>
+            </div>
+            <div class="form-group">
+                <label for="password">$_("Password")</label>
+                <input type="password" class="form-control" id="password" />
+                <p class="help-block">
+                    <i class="fa fa-info-circle"></i> $_("Password of the user in the remote host")</p>
+            </div>
+            <div class="form-group">
+                <input id="deleteVM" class="wok-checkbox" type="checkbox" value="" />
+                <label for="deleteVM" id="labelDeleteVM">$_("Delete this VM when the migration is completed") </label>
+            </div>
         </div>
-        <div class="form-group">
-            <label for="password">$_("Password")</label>
-            <input type="password" class="form-control" id="password" />
-            <p class="help-block">
-                <i class="fa fa-info-circle"></i> $_("Password of the user in the remote host")</p>
+        <div class="modal-footer">
+            <button type="submit" id="migrateFormOk" class="btn btn-default">$_("Start")</button>
+            <button type="button" id="migrateFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button>
         </div>
-        <div class="form-group">
-            <input id="deleteVM" class="wok-checkbox" type="checkbox" value="" />
-            <label for="deleteVM" id="labelDeleteVM">$_("Delete this VM when the migration is completed") </label>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button type="submit" id="migrateFormOk" class="btn btn-default">$_("Start")</button>
-        <button type="button" id="migrateFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button>
     </div>
-</div>
-
+    <script>
+    kimchi.guest_livemigration_main();
+    </script>
+</body>
+</html>
diff --git a/src/wok/plugins/kimchi/ui/pages/guest.html.tmpl b/src/wok/plugins/kimchi/ui/pages/guest.html.tmpl
index f5d28f1..11feb5c 100644
--- a/src/wok/plugins/kimchi/ui/pages/guest.html.tmpl
+++ b/src/wok/plugins/kimchi/ui/pages/guest.html.tmpl
@@ -40,7 +40,8 @@
                             <li role="presentation"><a nwAct="connect-vnc" class='shutoff-disabled' name="vm-console" href="#"><i class="fa fa-list-alt"></i>$_("Connect VNC")</a></li>
                         <!--     <li role="presentation"><a nwAct="view-vnc" class='shutoff-disabled' name="vm-view-vnc" href="#"><i class="fa fa-eye"></i>$_("View VNC Console")</a></li> -->
                             <li role="presentation"><a nwAct="edit" name="vm-edit" href="#"><i class="fa fa-pencil"></i>$_("Edit")</a></li>
-                            <li role="presentation"><a href="#" nwAct="clone" class='running-disabled' name="vm-clone"><i class="fa fa-copy"></i>$_("Clone")</a></li>
+                            <li role="presentation"><a nwAct="clone" class='running-disabled' name="vm-clone" href="#"><i class="fa fa-copy"></i>$_("Clone")</a></li>
+                            <li role="presentation"><a nwAct="migrate" name="vm-migrate" href="#"><i class="fa fa-exchange"></i>$_("Migrate")</a></li>
                             <li role="presentation"><a nwAct="reset" class='shutoff-hidden non-persistent-disabled' name="vm-reset" href="#"><i class="fa fa-refresh"></i>$_("Reset")</a></li>
                             <li role="presentation"><a nwAct="pause" class='pause-hidden non-persistent-disabled' name="vm-pause" href="#"><i class="fa fa-pause"></i>$_("Pause")</a></li>
                             <li role="presentation"><a nwAct="resume" class='resume-hidden' name="vm-resume" href="#"><i class="fa fa-play-circle"></i>$_("Resume")</a></li>
@@ -62,7 +63,6 @@
                     <div class="progress-bar medium-grey cpu"></div>
                     <div class="progress-bar light-grey cpu"></div>
                     <div class="progress-bar cpu-progress-bar"></div>
-                            <button class="button-big" name="vm-migrate"><span class="text">$_("Migrate")</span></button>
                 </div>
             </span><!--
             --><span class="item-hidden">$_("Processors Used")</span><!--
diff --git a/src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl b/src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl
index d356437..bee3ad1 100644
--- a/src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl
+++ b/src/wok/plugins/kimchi/ui/pages/i18n.json.tmpl
@@ -43,6 +43,8 @@
     "KCHAPI6007M": "$_("Save")",
     "KCHAPI6008M": "$_("Creating...")",
     "KCHAPI6009M": "$_("Cloning...")",
+    "KCHAPI6010M": "$_("Migrating...")",
+    "KCHAPI6011M": "$_("Start")",
 
     "KCHTMPL6001W": "$_("No ISO found")",
 
diff --git a/src/wok/plugins/kimchi/ui/pages/network.html.tmpl b/src/wok/plugins/kimchi/ui/pages/network.html.tmpl
index 722fc11..d607c39 100644
--- a/src/wok/plugins/kimchi/ui/pages/network.html.tmpl
+++ b/src/wok/plugins/kimchi/ui/pages/network.html.tmpl
@@ -25,7 +25,6 @@
 <!DOCTYPE html>
 <html>
 <head>
-<link rel="stylesheet" href="plugins/kimchi/css/theme-default.min.css">
 <script src="plugins/kimchi/js/kimchi.min.js"></script>
 </head>
 <body>
diff --git a/ui/css/src/modules/_validation.scss b/ui/css/src/modules/_validation.scss
new file mode 100644
index 0000000..914e423
--- /dev/null
+++ b/ui/css/src/modules/_validation.scss
@@ -0,0 +1,11 @@
+
+// Form validation
+.has-success {
+  @include form-control-validation(darken($state-success-border,10%), darken($state-success-border,10%), $state-success-bg);
+}
+.has-warning {
+  @include form-control-validation(darken($state-warning-border,10%), darken($state-warning-border,10%), $state-warning-bg);
+}
+.has-error {
+  @include form-control-validation(darken($state-danger-border,10%), darken($state-danger-border,10%), $state-danger-bg);
+}
\ No newline at end of file
diff --git a/ui/css/src/wok.scss b/ui/css/src/wok.scss
index 34f5f2f..3d182be 100755
--- a/ui/css/src/wok.scss
+++ b/ui/css/src/wok.scss
@@ -99,6 +99,8 @@
 @import "modules/datagrid";
 // Wok Confirm Dialog
 @import "modules/wok-confirm";
+// Form validation classes
+ at import "modules/validation";
 // Wok jQuery-UI Accordion
 @import "modules/jqueryui";
 // Wok ISO / VM / Template list
-- 
1.9.3




More information about the Kimchi-devel mailing list