
From: samhenri <samuel.guimaraes@eldorado.org.br> Signed-off-by: samhenri <samuel.guimaraes@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 +@import "modules/validation"; // Wok jQuery-UI Accordion @import "modules/jqueryui"; // Wok ISO / VM / Template list -- 1.9.3