[PATCH v2][Kimchi 0/3] Add new function to Kimchi and fix multiple clones

This is the v2 of patchset to fix/provide support to clone a guest multiple times. This patch: - Creates a new function, moved from Wok to name clones; - Use timestamp parameter of the function to avoid clone problems; - Fixes backend imports; - Adds UI. Rodrigo Trujillo (3): Add function 'get_next_clone_name' Fix issue when clone a vm multiple times Add UI support to clone a guest multiple times mockmodel.py | 4 +-- model/storagevolumes.py | 4 +-- model/vms.py | 5 ++-- ui/js/src/kimchi.guest_main.js | 58 ++++++++++++++++++++++++++++++++---------- ui/pages/guest-clone.html.tmpl | 47 ++++++++++++++++++++++++++++++++++ ui/pages/i18n.json.tmpl | 3 ++- utils.py | 50 ++++++++++++++++++++++++++++++++++++ 7 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 ui/pages/guest-clone.html.tmpl -- 2.1.0

This patch moves the function 'get_next_clone_name' from Wok to Kimchi. The function adds, originally, an incremental number in each clone name, however this approach causes a race condiction if Kimchi tries to clone guest multiple times, sending multiple request at once. If clone requests are done so fast, it is possible that the plugin does not send all resource names in all_names parameter, then the function is going to select the same name for 2 or more clones, which is going to cause errors. To avoid this problem, it is better to use timestamps in the clones names instead of incremental numbers. Then it guarantees names will be unique. Supporting timestamps in microsecond precision, avoids this type of error. New clone name will be "basename-clone-timestamp-name-suffix". Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- utils.py | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/utils.py b/utils.py index 7524d4e..87d7d9b 100644 --- a/utils.py +++ b/utils.py @@ -23,6 +23,7 @@ import json import os import re import sqlite3 +import time import urllib2 from httplib import HTTPConnection, HTTPException from urlparse import urlparse @@ -224,3 +225,52 @@ def upgrade_objectstore_memory(): if total > 0: wok_log.info( "%d 'template' memory entries upgraded in objectstore.", total) + + +def get_next_clone_name(all_names, basename, name_suffix='', ts=False): + """Find the next available name for a cloned resource. + + If any resource named "<basename>-clone-<number><name_suffix>" is found + in "all_names", use the maximum "number" + 1; else, use 1. + + Arguments: + all_names -- All existing names for the resource type. This list will + be used to make sure the new name won't conflict with + existing names. + basename -- The name of the original resource. + name_suffix -- The resource name suffix (optional). This parameter + exist so that a resource named "foo.img" gets the name + "foo-clone-1.img" instead of "foo.img-clone-1". If this parameter + is used, the suffix should not be present in "basename". + ts -- use timestamp, instead of incremental numbers + + Return: + A UTF-8 string in the format "<basename>-clone-<number><name_suffix>". + """ + re_group_num = 'num' + + # Use timestamps or increase clone number + if ts: + # Microsecond precision + ts_suffix = int(time.time() * 1000000) + new_name = u'%s-clone-%d' % (basename, ts_suffix) + else: + re_expr = u'%s-clone-(?P<%s>\d+)' % (basename, re_group_num) + if name_suffix != '': + re_expr = u'%s%s' % (re_expr, name_suffix) + + max_num = 0 + re_compiled = re.compile(re_expr) + + for n in all_names: + match = re_compiled.match(n) + if match is not None: + max_num = max(max_num, int(match.group(re_group_num))) + + # increments the maximum "clone number" found + new_name = u'%s-clone-%d' % (basename, max_num + 1) + + if name_suffix != '': + new_name = new_name + name_suffix + + return new_name -- 2.1.0

Use timestamp in "get_next_clone_name" function to avoid problemas with the incremental number in the new clone names. This patch also fixes imports of the function from Wok. Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- mockmodel.py | 4 ++-- model/storagevolumes.py | 4 ++-- model/vms.py | 5 +++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/mockmodel.py b/mockmodel.py index cbeeb5d..c3c6930 100644 --- a/mockmodel.py +++ b/mockmodel.py @@ -28,7 +28,7 @@ from lxml.builder import E from wok.exception import NotFoundError, OperationFailed from wok.objectstore import ObjectStore -from wok.utils import add_task, get_next_clone_name +from wok.utils import add_task from wok.xmlutils.utils import xml_item_update from wok.plugins.kimchi import imageinfo @@ -49,7 +49,7 @@ from wok.plugins.kimchi.model import storagevolumes from wok.plugins.kimchi.model.templates import LibvirtVMTemplate from wok.plugins.kimchi.model.users import PAMUsersModel from wok.plugins.kimchi.model.vmhostdevs import VMHostDevsModel -from wok.plugins.kimchi.utils import pool_name_from_uri +from wok.plugins.kimchi.utils import get_next_clone_name, pool_name_from_uri from wok.plugins.kimchi.vmtemplate import VMTemplate diff --git a/model/storagevolumes.py b/model/storagevolumes.py index f87738f..83f38e2 100644 --- a/model/storagevolumes.py +++ b/model/storagevolumes.py @@ -30,8 +30,7 @@ from lxml.builder import E from wok.exception import InvalidOperation, InvalidParameter, IsoFormatError from wok.exception import MissingParameter, NotFoundError, OperationFailed -from wok.utils import add_task, get_next_clone_name, get_unique_file_name -from wok.utils import wok_log +from wok.utils import add_task, get_unique_file_name, wok_log from wok.xmlutils.utils import xpath_get_text from wok.model.tasks import TaskModel @@ -40,6 +39,7 @@ from wok.plugins.kimchi.isoinfo import IsoImage from wok.plugins.kimchi.model.diskutils import get_disk_used_by from wok.plugins.kimchi.model.diskutils import set_disk_used_by from wok.plugins.kimchi.model.storagepools import StoragePoolModel +from wok.plugins.kimchi.utils import get_next_clone_name VOLUME_TYPE_MAP = {0: 'file', diff --git a/model/vms.py b/model/vms.py index 195c879..9f464f4 100644 --- a/model/vms.py +++ b/model/vms.py @@ -39,7 +39,7 @@ from wok.exception import InvalidOperation, InvalidParameter from wok.exception import NotFoundError, OperationFailed from wok.model.tasks import TaskModel from wok.rollbackcontext import RollbackContext -from wok.utils import add_task, convert_data_size, get_next_clone_name +from wok.utils import add_task, convert_data_size from wok.utils import import_class, run_setfacl_set_attr, run_command, wok_log from wok.xmlutils.utils import dictize, xpath_get_text, xml_item_insert from wok.xmlutils.utils import xml_item_remove, xml_item_update @@ -59,6 +59,7 @@ from wok.plugins.kimchi.model.utils import get_metadata_node from wok.plugins.kimchi.model.utils import remove_metadata_node from wok.plugins.kimchi.model.utils import set_metadata_node from wok.plugins.kimchi.screenshot import VMScreenshot +from wok.plugins.kimchi.utils import get_next_clone_name from wok.plugins.kimchi.utils import template_name_from_uri from wok.plugins.kimchi.xmlutils.cpu import get_cpu_xml, get_numa_xml from wok.plugins.kimchi.xmlutils.cpu import get_topology_xml @@ -318,7 +319,7 @@ class VMModel(object): vms_being_created.append(uri_name) current_vm_names = self.vms.get_list() + vms_being_created - new_name = get_next_clone_name(current_vm_names, name) + new_name = get_next_clone_name(current_vm_names, name, ts=True) # create a task with the actual clone function taskid = add_task(u'/plugins/kimchi/vms/%s/clone' % new_name, -- 2.1.0

This patch provides the support to clone a guest multiple times. User will be asked to provide the number of clone he/she wants, then UI is going to send that many requests to backend. Signed-off-by: Socorro Stoppler <socorro@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- ui/js/src/kimchi.guest_main.js | 58 ++++++++++++++++++++++++++++++++---------- ui/pages/guest-clone.html.tmpl | 47 ++++++++++++++++++++++++++++++++++ ui/pages/i18n.json.tmpl | 3 ++- 3 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 ui/pages/guest-clone.html.tmpl diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js index 2542a63..e45f439 100644 --- a/ui/js/src/kimchi.guest_main.js +++ b/ui/js/src/kimchi.guest_main.js @@ -240,6 +240,14 @@ kimchi.vmmigrate = function(event) { wok.window.open('plugins/kimchi/guest-migration.html'); }; +kimchi.vmclone = 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-clone.html'); +}; + kimchi.openVmSerialConsole = function(event) { var button = event.target; var vm = $(button).closest('li[name=guest]'); @@ -294,6 +302,18 @@ kimchi.resetGuestFilter = function() { } }; + +kimchi.initClone = function() { + var numTimesToClone = $('#numberClone').val(); + for (var i = 0; i < numTimesToClone; i++) { + kimchi.cloneGuest(kimchi.selectedGuest, function(data) { + kimchi.listVmsAuto(); + }); + } + wok.window.close(); +}; + + kimchi.listVmsAuto = function() { //Check if the actions button is opened or not, @@ -333,8 +353,8 @@ kimchi.listVmsAuto = function() { })); if (kimchi.trackingTasks.indexOf(tasks[i].id) == -1) kimchi.trackTask(tasks[i].id, null, function(err) { - wok.message.error(err.message); - }, null); + wok.message.error(err.message); + }, null); } }, null, true); return guests; @@ -752,17 +772,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) { }); guestActions.find("[name=vm-clone]").on("click", function(event) { event.preventDefault(); - var guest = $(this).closest('li[name=guest]').attr("id"); - wok.confirm({ - title: i18n['KCHAPI6006M'], - content: i18n['KCHVM6010M'], - confirm: i18n['KCHAPI6002M'], - cancel: i18n['KCHAPI6003M'] - }, function() { - kimchi.cloneGuest(guest, function(data) { - kimchi.listVmsAuto(); - }); - }, null); + kimchi.vmclone(event); }); guestActions.find("[name=vm-migrate]").on('click', function(event) { event.preventDefault(); @@ -819,6 +829,28 @@ kimchi.guest_main = function() { kimchi.listVmsAuto(); }; + +kimchi.guest_clonevm_main = function() { + kimchi.initCloneDialog(); +}; + +kimchi.initCloneDialog = function(callback) { + $("#cloneFormOk").on("click", function() { + //Check if input is a number + var numClone = parseInt($('#numberClone').val()); + var err = ""; + if (isNaN(numClone)) { + err = i18n['KCHVM0001E']; + wok.message.error(err,'#alert-modal-container'); + } else { + $("#cloneFormOk").prop("disabled", true); + kimchi.initClone(); + } + }); +}; + + + kimchi.editTemplate = function(guestTemplate, oldPopStat) { if (oldPopStat) { return guestTemplate.replace("vm-action", "vm-action open"); diff --git a/ui/pages/guest-clone.html.tmpl b/ui/pages/guest-clone.html.tmpl new file mode 100644 index 0000000..25907fa --- /dev/null +++ b/ui/pages/guest-clone.html.tmpl @@ -0,0 +1,47 @@ +#* + * Project Kimchi + * + * Copyright IBM Corp, 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. + *# +#unicode UTF-8 +#import gettext +#from wok.cachebust import href +#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True) +#silent _ = t.gettext +#silent _t = t.gettext +<!DOCTYPE html> +<html> +<body> + <div id="clone-guest-window" class="window modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="cloneModalLabel">$_("Clone a Guest")</h4> + </div> + <div id="cloneInfo" class="modal-body"> + <span id="alert-modal-container"></span> + <div class="alert alert-warning" role="alert">$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on the default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")</div> + <div class="form-group"> + <label for="numberClone">$_("Number of times to clone")</label> + <input type="number" class="form-control" id="numberClone" min="1" /> + </div> + <div class="modal-footer"> + <button type="submit" id="cloneFormOk" class="btn btn-default">$_("Continue")</button> + <button type="button" id="cloneFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button> + </div> + </div> + <script> + kimchi.guest_clonevm_main(); + </script> +</body> +</html> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl index 34a44ab..5be0a66 100644 --- a/ui/pages/i18n.json.tmpl +++ b/ui/pages/i18n.json.tmpl @@ -64,7 +64,8 @@ "KCHVM6007M": "$_("Note the guest OS may ignore this request. Would you like to continue?")", "KCHVM6008M": "$_("Virtual Machine delete Confirmation")", "KCHVM6009M": "$_("This virtual machine is not persistent. Power Off will delete it. Continue?")", - "KCHVM6010M": "$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")", + + "KCHVM0001E": "$_("Input is not a number")", "KCHVMCD6001M": "$_("This CDROM will be detached permanently and you can re-attach it. Continue to detach it?")", "KCHVMCD6003M": "$_("Attaching...")", -- 2.1.0

Hi all, When the clone dialog opens, there is not default value set to the input box regarding the number to clone the guest. I suggest to set it to 1 as default. Socorro, could you do that and resend the patch? FYI, this patch set will be applied to next branch due the code freeze for 2.1 release. On 03/15/2016 01:16 AM, Rodrigo Trujillo wrote:
This patch provides the support to clone a guest multiple times. User will be asked to provide the number of clone he/she wants, then UI is going to send that many requests to backend.
Signed-off-by: Socorro Stoppler <socorro@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- ui/js/src/kimchi.guest_main.js | 58 ++++++++++++++++++++++++++++++++---------- ui/pages/guest-clone.html.tmpl | 47 ++++++++++++++++++++++++++++++++++ ui/pages/i18n.json.tmpl | 3 ++- 3 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 ui/pages/guest-clone.html.tmpl
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js index 2542a63..e45f439 100644 --- a/ui/js/src/kimchi.guest_main.js +++ b/ui/js/src/kimchi.guest_main.js @@ -240,6 +240,14 @@ kimchi.vmmigrate = function(event) { wok.window.open('plugins/kimchi/guest-migration.html'); };
+kimchi.vmclone = 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-clone.html'); +}; + kimchi.openVmSerialConsole = function(event) { var button = event.target; var vm = $(button).closest('li[name=guest]'); @@ -294,6 +302,18 @@ kimchi.resetGuestFilter = function() { } };
+ +kimchi.initClone = function() { + var numTimesToClone = $('#numberClone').val(); + for (var i = 0; i < numTimesToClone; i++) { + kimchi.cloneGuest(kimchi.selectedGuest, function(data) { + kimchi.listVmsAuto(); + }); + } + wok.window.close(); +}; + + kimchi.listVmsAuto = function() {
//Check if the actions button is opened or not, @@ -333,8 +353,8 @@ kimchi.listVmsAuto = function() { })); if (kimchi.trackingTasks.indexOf(tasks[i].id) == -1) kimchi.trackTask(tasks[i].id, null, function(err) { - wok.message.error(err.message); - }, null); + wok.message.error(err.message); + }, null); } }, null, true); return guests; @@ -752,17 +772,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) { }); guestActions.find("[name=vm-clone]").on("click", function(event) { event.preventDefault(); - var guest = $(this).closest('li[name=guest]').attr("id"); - wok.confirm({ - title: i18n['KCHAPI6006M'], - content: i18n['KCHVM6010M'], - confirm: i18n['KCHAPI6002M'], - cancel: i18n['KCHAPI6003M'] - }, function() { - kimchi.cloneGuest(guest, function(data) { - kimchi.listVmsAuto(); - }); - }, null); + kimchi.vmclone(event); }); guestActions.find("[name=vm-migrate]").on('click', function(event) { event.preventDefault(); @@ -819,6 +829,28 @@ kimchi.guest_main = function() { kimchi.listVmsAuto(); };
+ +kimchi.guest_clonevm_main = function() { + kimchi.initCloneDialog(); +}; + +kimchi.initCloneDialog = function(callback) { + $("#cloneFormOk").on("click", function() { + //Check if input is a number + var numClone = parseInt($('#numberClone').val()); + var err = ""; + if (isNaN(numClone)) { + err = i18n['KCHVM0001E']; + wok.message.error(err,'#alert-modal-container'); + } else { + $("#cloneFormOk").prop("disabled", true); + kimchi.initClone(); + } + }); +}; + + + kimchi.editTemplate = function(guestTemplate, oldPopStat) { if (oldPopStat) { return guestTemplate.replace("vm-action", "vm-action open"); diff --git a/ui/pages/guest-clone.html.tmpl b/ui/pages/guest-clone.html.tmpl new file mode 100644 index 0000000..25907fa --- /dev/null +++ b/ui/pages/guest-clone.html.tmpl @@ -0,0 +1,47 @@ +#* + * Project Kimchi + * + * Copyright IBM Corp, 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. + *# +#unicode UTF-8 +#import gettext +#from wok.cachebust import href +#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True) +#silent _ = t.gettext +#silent _t = t.gettext +<!DOCTYPE html> +<html> +<body> + <div id="clone-guest-window" class="window modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="cloneModalLabel">$_("Clone a Guest")</h4> + </div> + <div id="cloneInfo" class="modal-body"> + <span id="alert-modal-container"></span> + <div class="alert alert-warning" role="alert">$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on the default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")</div> + <div class="form-group"> + <label for="numberClone">$_("Number of times to clone")</label> + <input type="number" class="form-control" id="numberClone" min="1" /> + </div> + <div class="modal-footer"> + <button type="submit" id="cloneFormOk" class="btn btn-default">$_("Continue")</button> + <button type="button" id="cloneFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button> + </div> + </div> + <script> + kimchi.guest_clonevm_main(); + </script> +</body> +</html> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl index 34a44ab..5be0a66 100644 --- a/ui/pages/i18n.json.tmpl +++ b/ui/pages/i18n.json.tmpl @@ -64,7 +64,8 @@ "KCHVM6007M": "$_("Note the guest OS may ignore this request. Would you like to continue?")", "KCHVM6008M": "$_("Virtual Machine delete Confirmation")", "KCHVM6009M": "$_("This virtual machine is not persistent. Power Off will delete it. Continue?")", - "KCHVM6010M": "$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")", + + "KCHVM0001E": "$_("Input is not a number")",
"KCHVMCD6001M": "$_("This CDROM will be detached permanently and you can re-attach it. Continue to detach it?")", "KCHVMCD6003M": "$_("Attaching...")",

Made the change and sent it over to Rodrigo. He'll be resending a new patch soon. On 03/15/2016 10:02 AM, Aline Manera wrote:
Hi all,
When the clone dialog opens, there is not default value set to the input box regarding the number to clone the guest. I suggest to set it to 1 as default.
Socorro, could you do that and resend the patch?
FYI, this patch set will be applied to next branch due the code freeze for 2.1 release.
On 03/15/2016 01:16 AM, Rodrigo Trujillo wrote:
This patch provides the support to clone a guest multiple times. User will be asked to provide the number of clone he/she wants, then UI is going to send that many requests to backend.
Signed-off-by: Socorro Stoppler <socorro@linux.vnet.ibm.com> Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo@linux.vnet.ibm.com> --- ui/js/src/kimchi.guest_main.js | 58 ++++++++++++++++++++++++++++++++---------- ui/pages/guest-clone.html.tmpl | 47 ++++++++++++++++++++++++++++++++++ ui/pages/i18n.json.tmpl | 3 ++- 3 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 ui/pages/guest-clone.html.tmpl
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js index 2542a63..e45f439 100644 --- a/ui/js/src/kimchi.guest_main.js +++ b/ui/js/src/kimchi.guest_main.js @@ -240,6 +240,14 @@ kimchi.vmmigrate = function(event) { wok.window.open('plugins/kimchi/guest-migration.html'); };
+kimchi.vmclone = 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-clone.html'); +}; + kimchi.openVmSerialConsole = function(event) { var button = event.target; var vm = $(button).closest('li[name=guest]'); @@ -294,6 +302,18 @@ kimchi.resetGuestFilter = function() { } };
+ +kimchi.initClone = function() { + var numTimesToClone = $('#numberClone').val(); + for (var i = 0; i < numTimesToClone; i++) { + kimchi.cloneGuest(kimchi.selectedGuest, function(data) { + kimchi.listVmsAuto(); + }); + } + wok.window.close(); +}; + + kimchi.listVmsAuto = function() {
//Check if the actions button is opened or not, @@ -333,8 +353,8 @@ kimchi.listVmsAuto = function() { })); if (kimchi.trackingTasks.indexOf(tasks[i].id) == -1) kimchi.trackTask(tasks[i].id, null, function(err) { - wok.message.error(err.message); - }, null); + wok.message.error(err.message); + }, null); } }, null, true); return guests; @@ -752,17 +772,7 @@ kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) { }); guestActions.find("[name=vm-clone]").on("click", function(event) { event.preventDefault(); - var guest = $(this).closest('li[name=guest]').attr("id"); - wok.confirm({ - title: i18n['KCHAPI6006M'], - content: i18n['KCHVM6010M'], - confirm: i18n['KCHAPI6002M'], - cancel: i18n['KCHAPI6003M'] - }, function() { - kimchi.cloneGuest(guest, function(data) { - kimchi.listVmsAuto(); - }); - }, null); + kimchi.vmclone(event); }); guestActions.find("[name=vm-migrate]").on('click', function(event) { event.preventDefault(); @@ -819,6 +829,28 @@ kimchi.guest_main = function() { kimchi.listVmsAuto(); };
+ +kimchi.guest_clonevm_main = function() { + kimchi.initCloneDialog(); +}; + +kimchi.initCloneDialog = function(callback) { + $("#cloneFormOk").on("click", function() { + //Check if input is a number + var numClone = parseInt($('#numberClone').val()); + var err = ""; + if (isNaN(numClone)) { + err = i18n['KCHVM0001E']; + wok.message.error(err,'#alert-modal-container'); + } else { + $("#cloneFormOk").prop("disabled", true); + kimchi.initClone(); + } + }); +}; + + + kimchi.editTemplate = function(guestTemplate, oldPopStat) { if (oldPopStat) { return guestTemplate.replace("vm-action", "vm-action open"); diff --git a/ui/pages/guest-clone.html.tmpl b/ui/pages/guest-clone.html.tmpl new file mode 100644 index 0000000..25907fa --- /dev/null +++ b/ui/pages/guest-clone.html.tmpl @@ -0,0 +1,47 @@ +#* + * Project Kimchi + * + * Copyright IBM Corp, 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. + *# +#unicode UTF-8 +#import gettext +#from wok.cachebust import href +#silent t = gettext.translation($lang.domain, $lang.localedir, languages=$lang.lang, fallback=True) +#silent _ = t.gettext +#silent _t = t.gettext +<!DOCTYPE html> +<html> +<body> + <div id="clone-guest-window" class="window modal-content"> + <div class="modal-header"> + <h4 class="modal-title" id="cloneModalLabel">$_("Clone a Guest")</h4> + </div> + <div id="cloneInfo" class="modal-body"> + <span id="alert-modal-container"></span> + <div class="alert alert-warning" role="alert">$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on the default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")</div> + <div class="form-group"> + <label for="numberClone">$_("Number of times to clone")</label> + <input type="number" class="form-control" id="numberClone" min="1" /> + </div> + <div class="modal-footer"> + <button type="submit" id="cloneFormOk" class="btn btn-default">$_("Continue")</button> + <button type="button" id="cloneFormCancel" data-dismiss="modal" class="btn btn-default">$_("Cancel")</button> + </div> + </div> + <script> + kimchi.guest_clonevm_main(); + </script> +</body> +</html> diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl index 34a44ab..5be0a66 100644 --- a/ui/pages/i18n.json.tmpl +++ b/ui/pages/i18n.json.tmpl @@ -64,7 +64,8 @@ "KCHVM6007M": "$_("Note the guest OS may ignore this request. Would you like to continue?")", "KCHVM6008M": "$_("Virtual Machine delete Confirmation")", "KCHVM6009M": "$_("This virtual machine is not persistent. Power Off will delete it. Continue?")", - "KCHVM6010M": "$_("When the target guest has SCSI or iSCSI volumes, they will be cloned on default storage pool. The same will happen when the target pool does not have enough space to clone the volumes. Do you want to continue?")", + + "KCHVM0001E": "$_("Input is not a number")",
"KCHVMCD6001M": "$_("This CDROM will be detached permanently and you can re-attach it. Continue to detach it?")", "KCHVMCD6003M": "$_("Attaching...")",
_______________________________________________ Kimchi-devel mailing list Kimchi-devel@ovirt.org http://lists.ovirt.org/mailman/listinfo/kimchi-devel

Tested-by: Socorro Stoppler <socorro@linux.vnet.ibm.com> On 03/14/2016 09:16 PM, Rodrigo Trujillo wrote:
This is the v2 of patchset to fix/provide support to clone a guest multiple times. This patch: - Creates a new function, moved from Wok to name clones; - Use timestamp parameter of the function to avoid clone problems; - Fixes backend imports; - Adds UI.
Rodrigo Trujillo (3): Add function 'get_next_clone_name' Fix issue when clone a vm multiple times Add UI support to clone a guest multiple times
mockmodel.py | 4 +-- model/storagevolumes.py | 4 +-- model/vms.py | 5 ++-- ui/js/src/kimchi.guest_main.js | 58 ++++++++++++++++++++++++++++++++---------- ui/pages/guest-clone.html.tmpl | 47 ++++++++++++++++++++++++++++++++++ ui/pages/i18n.json.tmpl | 3 ++- utils.py | 50 ++++++++++++++++++++++++++++++++++++ 7 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 ui/pages/guest-clone.html.tmpl
participants (3)
-
Aline Manera
-
Rodrigo Trujillo
-
Socorro Stoppler