From: samhenri <samuel.guimaraes(a)eldorado.org.br>
Signed-off-by: samhenri <samuel.guimaraes(a)eldorado.org.br>
---
model/vms.py | 2 +-
ui/css/kimchi.css | 21 +++--
ui/css/src/modules/_guests.scss | 6 +-
ui/js/src/kimchi.guest_livemigration.js | 136 ++++++++++++++++++++++++++++++++
ui/js/src/kimchi.guest_main.js | 94 +++++++++++-----------
ui/pages/guest-migration.html.tmpl | 78 +++++++++---------
ui/pages/guest.html.tmpl | 9 ++-
ui/pages/i18n.json.tmpl | 2 +
ui/pages/tabs/guests.html.tmpl | 4 +-
9 files changed, 255 insertions(+), 97 deletions(-)
create mode 100644 ui/js/src/kimchi.guest_livemigration.js
diff --git a/model/vms.py b/model/vms.py
index 3faee5a..5775ff3 100644
--- a/model/vms.py
+++ b/model/vms.py
@@ -1729,7 +1729,7 @@ class VMModel(object):
'non_shared': non_shared,
'remote_host': remote_host,
'user': user}
- task_id = add_task('/vms/%s/migrate' % name, self._migrate_task,
+ task_id = add_task('/plugins/kimchi/vms/%s/migrate' % name,
self._migrate_task,
self.objstore, params)
return self.task.lookup(task_id)
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index ec57c93..d74d5cc 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -78,7 +78,7 @@
#guest-add-window.modal-content label.box-iso-outer span.box-iso-border {
display: block;
border: 3px solid transparent;
- transition: all .1s ease-in-out;
+ transition: all 0.1s ease-in-out;
}
#template-add-window.modal-content label.box-iso-outer .iso-radio-hidden:checked +
span.box-iso-border,
@@ -100,7 +100,7 @@
display: block;
border: 1px solid transparent;
background: #fff;
- transition: all .1s ease-in-out;
+ transition: all 0.1s ease-in-out;
}
#template-add-window.modal-content ul.list-template,
@@ -606,6 +606,12 @@
display: none !important;
}
+#guest-content-container .wok-guest-list .wok-guest-list-header .btn .guest-pending,
+#guest-content-container .wok-guest-list .wok-guest-list-body .btn .guest-pending {
+ margin-left: -38px;
+ margin-right: -45px;
+}
+
#guest-content-container .wok-guest-list .distro-icon {
background-color: transparent;
background-size: 27px 27px;
@@ -997,7 +1003,7 @@
border: 1px solid #eee !important;
margin-bottom: 3px;
display: block;
- padding: .2em .6em .3em;
+ padding: 0.2em 0.6em 0.3em;
font-weight: 700;
line-height: 1;
text-align: left;
@@ -1240,7 +1246,7 @@
height: 664px;
width: 2164px;
left: 0;
- transition: left .2s ease-in-out;
+ transition: left 0.2s ease-in-out;
}
#template-add-window.modal-content p {
@@ -1455,7 +1461,9 @@
padding-right: 35px !important;
}
-#templates-root-container .wok-vm-gallery .item-hidden.column-type,
#templates-root-container .wok-vm-gallery .item-hidden.column-version,
#templates-root-container .wok-vm-gallery .item-hidden.column-processors {
+#templates-root-container .wok-vm-gallery .item-hidden.column-type,
+#templates-root-container .wok-vm-gallery .item-hidden.column-version,
+#templates-root-container .wok-vm-gallery .item-hidden.column-processors {
padding-bottom: 11px;
}
@@ -1996,7 +2004,8 @@
white-space: nowrap;
}
-.storage-modal .filter-select.popable .popover ul li:hover, .storage-modal
.filter-select.popable .popover ul li:focus,
+.storage-modal .filter-select.popable .popover ul li:hover,
+.storage-modal .filter-select.popable .popover ul li:focus,
.storage-modal .storage-target-input .popover ul li:hover,
.storage-modal .storage-target-input .popover ul li:focus,
.storage-modal .storage-add-input .popover ul li:hover,
diff --git a/ui/css/src/modules/_guests.scss b/ui/css/src/modules/_guests.scss
index d302fdc..9250bcb 100644
--- a/ui/css/src/modules/_guests.scss
+++ b/ui/css/src/modules/_guests.scss
@@ -312,6 +312,10 @@
span.item-hidden {
display: none !important;
}
+ .btn .guest-pending {
+ margin-left: -38px;
+ margin-right: -45px;
+ }
}
.wok-guest-list .distro-icon {
background-color: transparent;
@@ -348,4 +352,4 @@
.wok-guest-list .wok-guest-list-body.inactive {
color: $disabled-color !important;
}
-}
+}
\ No newline at end of file
diff --git a/ui/js/src/kimchi.guest_livemigration.js
b/ui/js/src/kimchi.guest_livemigration.js
new file mode 100644
index 0000000..94c793b
--- /dev/null
+++ b/ui/js/src/kimchi.guest_livemigration.js
@@ -0,0 +1,136 @@
+/*
+ * Project Kimchi
+ *
+ * Copyright IBM, Corp. 2013-2016
+ *
+ * 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.getOngoingMigration = function(guestName, toDelete) {
+ var guests = [];
+ kimchi.getTasksByFilter('status=running&target_uri=' +
encodeURIComponent('^/plugins/kimchi/vms/' + guestName + '/migrate'),
function(tasks) {
+ for (var i = 0; i < tasks.length; i++) {
+ var guestUri = tasks[i].target_uri;
+ var guestName = guestUri.split('/')[4];
+ guests[guestName] = tasks[i];
+
+ if (kimchi.trackingTasks.indexOf(tasks[i].id) >= 0) {
+ continue;
+ }
+
+ kimchi.trackTask(tasks[i].id, function(guests) {
+ if (toDelete) {
+ kimchi.deleteVM(guestName, function(result) {
+ return;
+ });
+ }
+ kimchi.listVmsAuto();
+ }, function(guests) {
+ return;
+ }, function(guests) {
+ return;
+ });
+ }
+ }, null, true);
+ return guests;
+};
+
+kimchi.initLiveMigrationDialog = function(okCallback) {
+ $("#migrateFormOk").on("click", function() {
+ $("#migrateFormOk").prop("disabled", true);
+ $("#remote_host").prop("readonly", "readonly");
+ $("#user").prop("readonly", "readonly");
+ $("#password").prop("readonly", "readonly");
+ $("#deleteVM").prop("readonly", "readonly");
+ wok.window.close();
+ kimchi.initLiveMigrationProccess();
+ });
+};
+
+kimchi.initLiveMigrationProccess = function() {
+ var obj = kimchi.getLiveMigrationInputValues();
+ var toDelete = obj[kimchi.selectedGuest].toDelete;
+ kimchi.migrateGuest(kimchi.selectedGuest, obj[kimchi.selectedGuest].values,
function() {
+ kimchi.listVmsAuto();
+ kimchi.getOngoingMigration(kimchi.selectedGuest, toDelete);
+ }, function(err) {
+ wok.message.error(err.responseJSON.reason);
+ });
+}
+
+kimchi.getLiveMigrationInputValues = function() {
+ var host = $("#remote_host").val();
+ var username = $("#user").val();
+ var password = $("#password").val();
+ var toDelete = $("#deleteVM").prop('checked');
+ var data = {};
+ data[kimchi.selectedGuest] = {
+ values: {
+ remote_host: host
+ },
+ toDelete: toDelete
+
+ };
+ if (username && password) {
+ data[kimchi.selectedGuest].values.user = username;
+ data[kimchi.selectedGuest].values.password = password;
+ }
+ return data;
+};
+
+kimchi.setupLiveMigrationFormEvent = function() {
+ $("#migrateFormOk").prop("disabled", true);
+ $("#remote_host").on("change keyup", function(event) {
+ if (!this.value) {
+ $(this).parent().addClass('has-error');
+ } else {
+ $(this).parent().removeClass('has-error');
+ }
+ kimchi.updateLiveMigrationButton();
+ });
+ $("#user").on("change keyup", function(event) {
+ if (this.value && !$("#password").val()) {
+ $("#user").parent().removeClass('has-warning');
+ $("#password").parent().addClass('has-warning');
+ } else {
+ $("#user").parent().removeClass('has-warning');
+ $("#password").parent().removeClass('has-warning');
+ }
+ kimchi.updateLiveMigrationButton();
+ });
+ $("#password").on("change keyup", function(event) {
+ if (this.value && !$("#user").val()) {
+ $("#user").parent().addClass('has-warning');
+ } else {
+ $("#user").parent().removeClass('has-warning');
+ $("#password").parent().removeClass('has-warning');
+ kimchi.updateLiveMigrationButton();
+ }
+ });
+};
+
+kimchi.updateLiveMigrationButton = function() {
+ if ($("#remote_host").val()) {
+ if
($("input[type='text']").parent().hasClass("has-error") ||
$("input[type='text']").parent().hasClass("has-warning")) {
+ $("#migrateFormOk").prop("disabled", true);
+ } else {
+ $("#migrateFormOk").prop("disabled", false);
+ }
+ };
+};
\ No newline at end of file
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js
index 2d3ccf3..cabb84c 100644
--- a/ui/js/src/kimchi.guest_main.js
+++ b/ui/js/src/kimchi.guest_main.js
@@ -233,6 +233,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]');
@@ -275,7 +283,7 @@ kimchi.initGuestFilter = function() {
};
kimchi.resetGuestFilter = function() {
- if(guestFilterList){
+ if (guestFilterList) {
$('#search_input').val();
listFiltered = false;
}
@@ -326,23 +334,34 @@ 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);
- }
- }, null, true);
- return guests;
- };
+
+ var getMigratingGuests = function() {
+ var guests = [];
+ kimchi.getTasksByFilter('status=running&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($.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;
+ };
+
kimchi.listVMs(function(result, textStatus, jqXHR) {
if (result && textStatus == "success") {
+ var migrated = getMigratingGuests();
+ for (i = migrated.length - 1; i >= 0; i--) {
+ for (j = result.length - 1; j >= 0; j--) {
+ if (result[j].name == migrated[i].name) result.splice(j, 1);
+ }
+ }
result = getMigratingGuests().concat(result);
result = getCloningGuests().concat(result);
result = getCreatingGuests().concat(result);
@@ -400,27 +419,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");
@@ -440,7 +438,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');
@@ -475,7 +472,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'];
@@ -493,7 +490,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu)
{
imgLoad.attr('src', load_src);
//Link the stopped tile to the start action, the running tile to open the console,
and the paused tile to resume
- if (!(vmObject.isCloning || vmObject.isCreating)) {
+ if (!(vmObject.isCloning || vmObject.isCreating || vmObject.isMigrating)) {
if (vmPoweredOffBool) {
liveTile.off("click", function(event) {
event.preventDefault();
@@ -666,7 +663,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();
@@ -725,18 +722,19 @@ 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) {
+ event.preventDefault();
+ kimchi.vmmigrate(event);
});
} else {
guestActions.find('.btn').attr('disabled', true);
- result.find('.guest-pending').removeClass('hide-content');
+ result.find('.guest-done').addClass('hidden');
+ result.find('.guest-pending').removeClass('hidden');
pendingText = result.find('.guest-pending .text')
if (vmObject.isCloning)
pendingText.text(i18n['KCHAPI6009M']);
- else if(vmObject.isMigrating)
- pendingText.text("Migrating");
+ else if (vmObject.isMigrating)
+ pendingText.text(i18n['KCHAPI6012M']);
else
pendingText.text(i18n['KCHAPI6008M']);
}
@@ -770,4 +768,4 @@ kimchi.editTemplate = function(guestTemplate, oldPopStat) {
return guestTemplate.replace("vm-action", "vm-action open");
}
return guestTemplate;
-};
+};
\ No newline at end of file
diff --git a/ui/pages/guest-migration.html.tmpl b/ui/pages/guest-migration.html.tmpl
index 278d22c..7065605 100644
--- a/ui/pages/guest-migration.html.tmpl
+++ b/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="remote_host">$_("Remote
Server")</label>
+ <input type="text" class="form-control"
id="remote_host" />
+ <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="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>
+ <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/ui/pages/guest.html.tmpl b/ui/pages/guest.html.tmpl
index 080d918..6d189ef 100644
--- a/ui/pages/guest.html.tmpl
+++ b/ui/pages/guest.html.tmpl
@@ -34,7 +34,14 @@
--><span class='column-action pull-right'>
<span class="pull-right">
<div class="dropdown menu-flat guest-actions"
name="guest-actions" style="margin-top: 6px">
- <button class="btn btn-primary dropdown-toggle"
type="button" data-toggle="dropdown"
aria-expanded="false"><span
class="edit-alt"></span>$_("Actions")<span
class="caret"></span>
+ <button class="btn btn-primary dropdown-toggle"
type="button" data-toggle="dropdown"
aria-expanded="false">
+ <span class="guest-done">
+ <span
class="edit-alt"></span>$_("Actions")<span
class="caret"></span>
+ </span>
+ <span class="guest-pending hidden">
+ <span
class="wok-loading-icon"></span>
+ <span class="text"></span>
+ </span>
</button>
<ul class="dropdown-menu" role="menu">
<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>
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index c1979c6..f940ee5 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -44,6 +44,8 @@
"KCHAPI6008M": "$_("Creating...")",
"KCHAPI6009M": "$_("Cloning...")",
"KCHAPI6010M": "$_("Saving...")",
+ "KCHAPI6011M": "$_("Start")",
+ "KCHAPI6012M": "$_("Migrating...")",
"KCHTMPL6001W": "$_("No ISO found")",
diff --git a/ui/pages/tabs/guests.html.tmpl b/ui/pages/tabs/guests.html.tmpl
index 21d1f90..5409dfb 100644
--- a/ui/pages/tabs/guests.html.tmpl
+++ b/ui/pages/tabs/guests.html.tmpl
@@ -44,7 +44,7 @@
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
- </div>
+ </div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse"
id="toolbar">
<ul class="nav navbar-nav navbar-right tools"
display="none">
@@ -60,7 +60,7 @@
<label for="search_input"
class="sr-only">$_("Filter"):</label>
<input type="text" class="filter form-control
search" id="search_input"
placeholder="$_("Filter")">
</div>
- </div>
+ </div>
<div id="alert-container"></div>
<div id="guestListField" style="display: none">
<ul class="wok-guest-list">
--
1.8.3.1