diff --git a/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb b/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb index 23801b915b..7ce484f82d 100644 --- a/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/TemplateJSON.rb @@ -21,13 +21,13 @@ module OpenNebulaJSON include JSONUtils def create(template_json) - template_hash = parse_json(template_json, 'template') + template_hash = parse_json(template_json, 'vmtemplate') if OpenNebula.is_error?(template_hash) return template_hash end if template_hash['template_raw'] - template = vm_hash['template_raw'] + template = template_hash['template_raw'] else template = template_to_str(template_hash) end diff --git a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb index 55f6cada51..7b04a7b923 100644 --- a/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/VirtualMachineJSON.rb @@ -28,6 +28,12 @@ module OpenNebulaJSON if vm_hash['vm_raw'] template = vm_hash['vm_raw'] + elsif vm_hash['template_id'] + template_id = vm_hash['template_id'] + + template = "TEMPLATE_ID = #{template_id}" + template << "\nNAME = #{vm_hash[:vm_name]}" if vm_hash[:vm_name] + else template = template_to_str(vm_hash) end @@ -55,7 +61,7 @@ module OpenNebulaJSON when "restart" then self.restart when "saveas" then self.save_as(action_hash['params']) when "shutdown" then self.shutdown - when "resubmit" then self.resubmit + when "resubmit" then self.resubmit else error_msg = "#{action_hash['perform']} action not " << " available for this resource" diff --git a/src/sunstone/public/js/plugins/templates-tab.js b/src/sunstone/public/js/plugins/templates-tab.js index d666c3f95e..cb558aa5c7 100644 --- a/src/sunstone/public/js/plugins/templates-tab.js +++ b/src/sunstone/public/js/plugins/templates-tab.js @@ -36,7 +36,459 @@ var templates_tab_content = \ '; -var create_template_tmpl = ""; +var create_template_tmpl = '
'; var templates_select = ""; var template_list_json = {}; @@ -287,7 +739,7 @@ function updateTemplateSelect(){ templates_select = makeSelectOptions(dataTable_templates,1,3,5,"No"); //update static selectors: - + $('#create_vm_dialog #template_id').html(templates_select); } // Callback to update an element in the dataTable @@ -447,7 +899,770 @@ function updateTemplateInfo(request,template){ // Prepare the template creation dialog function setupCreateTemplateDialog(){ - + //Helper functions for the dialog operations + + // Called when changing tabs. Since we use the same form for both + // KVM, XEN and others we need to do some operation to update it + var vmTabChange = function(event,ui){ + // ui.tab // anchor element of the selected (clicked) tab + // ui.panel // element, that contains the selected/clicked tab contents + // ui.index // zero-based index of the selected (clicked) tab + switch(ui.index){ + case 0: + enable_kvm(); + break; + case 1: + enable_xen(); + break; + case 2: + break; + case 3: + break; + } + } + + //Using kvm wizard. Updates mandatory tag, optional tags, disable + //XEN-only (and others) items, enables KVM items + var enable_kvm = function(){ + man_class="kvm"; + opt_class="kvm_opt"; + $(xen_items).attr("disabled","disabled"); + $(kvm_items).removeAttr("disabled"); + //$(items+':disabled').hide(); + + + //KVM particularities: + // * Add no_type option for disks + // * Add driver default option for boot and select it - hide some fields + // * Set the raw type to kvm + // * Show the inputs section + $('div#disks select#TYPE option:selected').removeAttr("selected"); + $('div#disks select#TYPE').prepend( + ''); + $('div#disks select#TYPE option#no_type').attr("selected","selected"); + + $('select#boot_method option').removeAttr("selected"); + $('select#boot_method option#no_boot').html("Driver default"); + $('select#boot_method option').removeAttr("selected"); + $('.kernel, .bootloader', $('div#os_boot_opts')).hide(); + + $('div#disks select#BUS').append( + ''); + + + + $('input#TYPE', section_raw).val("kvm"); + + $(section_inputs).show(); + }; + + // Using XEN wizard. Update mandatory and optional classes, disable + // KVM-only (and other) items, enable XEN fields... + enable_xen = function(){ + man_class="xen"; + opt_class="xen_opt"; + $(kvm_items).attr("disabled","disabled"); + $(kvm_items).css("background",""); + $(xen_items).removeAttr("disabled"); + //$(items+':disabled').hide(); + + + // XEN particularities: + // * Remove no_type option from disks + // * Remove driver default boot method + // * Set the raw section to XEN + // * Hide the inputs section + $('div#disks select#TYPE option#no_type').remove(); + + $('select#boot_method option:selected').removeAttr("selected"); + $('select#boot_method option#no_boot').html("Please choose"); + $('.kernel, .bootloader', $('div#os_boot_opts')).hide(); + + $('div#disks select#BUS option#virtio').remove(); + + $('input#TYPE', section_raw).val("xen"); + $(section_inputs).hide(); //not present for xen + }; + + //This function checks that all mandatory items within a section + //have some value. Returns true if so, false if not. + var mandatory_filter = function(context){ + var man_items = "."+man_class; + + //find enabled mandatory items in this context + man_items = $(man_items+' input:visible, '+man_items+' select:visible',context); + var r = true; + + //we fail it the item is enabled and has no value + $.each(man_items,function(){ + if ($(this).parents(".vm_param").attr("disabled") || + !($(this).val().length)) { + r = false; + return false; + }; + }); + return r; + + }; + + //Adds an option element to a multiple select box. Before doing so, + //it checks that the desired filter is passed + var box_add_element = function(context,box_tag,filter){ + var value=""; + var params= $('.vm_param',context); + var inputs= $('input:enabled',params); + var selects = $('select:enabled',params); + var fields = $.merge(inputs,selects); + + //are fields passing the filter? + var result = filter(); + if (!result) { + notifyError("There are mandatory parameters missing in this section"); + return false; + } + + value={}; + + //With each enabled field we form a JSON object + var id = null; + $.each(fields,function(){ + if (!($(this).parents(".vm_param").attr("disabled")) && + $(this).val().length){ + //Pick up parent's ID if we do not have one + id = $(this).attr('id').length ? $(this).attr('id') : $(this).parent().attr('id'); + value[id] = $(this).val(); + } + }); + var value_string = JSON.stringify(value); + var option= ''; + $('select'+box_tag,context).append(option); + return false; + }; + + //Removes selected elements from a multiple select box + var box_remove_element = function(section_tag,box_tag){ + var context = $(section_tag); + $('select'+box_tag+' :selected',context).remove(); + return false; + }; + + //Given the JSON of a VM template (or of a section of it), it crawls + //the fields of certain section (context) and add their name and + //values to the template JSON. + var addSectionJSON = function(template_json,context){ + var params= $('.vm_param',context); + var inputs= $('input:enabled',params); + var selects = $('select:enabled',params); + var fields = $.merge(inputs,selects); + + fields.each(function(){ + if (!($(this).parents(".vm_param").attr("disabled"))){ //if ! disabled + if ($(this).val().length){ //if has a length + template_json[$(this).attr('id')]=$(this).val(); + } + } + }); + } + + // Given an array (usually empty), a section (context) and a tag for + // a multiple select in that section, it adds the contents of the + // box as objects in the array. + // TODO: Make it return a new array? + var addBoxJSON = function(array,context,box_tag){ + $('select'+box_tag+' option',context).each(function(){ + array.push( JSON.parse($(this).val()) ); + }); + } + + //Given an object, removes those elements which are empty + //Used to clean up a template JSON before submitting + //it to opennebula.js + var removeEmptyObjects = function(obj){ + for (elem in obj){ + var remove = false; + var value = obj[elem]; + if (value instanceof Array) + { + if (value.length == 0) + remove = true; + } + else if (value instanceof Object) + { + var obj_length = 0; + for (e in value) + obj_length += 1; + if (obj_length == 0) + remove = true; + } + else + { + value = String(value); + if (value.length == 0) + remove = true; + } + if (remove) + delete obj[elem]; + } + return obj; + } + + //Toggles the icon when a section is folded/unfolded + var iconToggle = function(){ + $('.icon_right').toggle( + function(e){ + $('span',e.currentTarget).removeClass("ui-icon-plusthick"); + $('span',e.currentTarget).addClass("ui-icon-minusthick"); + },function(e){ + $('span',e.currentTarget).removeClass("ui-icon-minusthick"); + $('span',e.currentTarget).addClass("ui-icon-plusthick"); + }); + } + + // Set ups the capacity section + var capacity_setup = function(){ + //Actually there is nothing to set up, but it used to be + //possible to hide it like others + /* + $('fieldset',section_capacity).hide(); + $('#add_capacity',section_capacity).click(function(){ + $('fieldset',section_capacity).toggle(); + return false; + }); + */ + + } + + //Sets up the OS_BOOT section + var os_boot_setup = function(){ + $('fieldset',section_os_boot).hide(); + $('.bootloader, .kernel',section_os_boot).hide(); + + $('#add_os_boot_opts',section_os_boot).click(function(){ + $('fieldset',section_os_boot).toggle(); + return false; + }); + + + //Chrome workaround + $('#boot_method').change(function(){ + $(this).trigger("click"); + }); + + //Depending on the boot method we enable/disable some options + $('#boot_method',section_os_boot).click(function(){ + select = $(this).val(); + switch (select) + { + case "kernel": + $('.bootloader',section_os_boot).hide(); + $('.bootloader',section_os_boot).attr("disabled","disabled"); + $('.kernel',section_os_boot).show(); + $('.kernel',section_os_boot).removeAttr("disabled"); + break; + case "bootloader": + $('.kernel',section_os_boot).hide(); + $('.kernel',section_os_boot).attr("disabled","disabled"); + $('.bootloader',section_os_boot).show(); + $('.bootloader',section_os_boot).removeAttr("disabled"); + break; + default: + $('.kernel, .bootloader',section_os_boot).hide(); + $('.kernel, .bootloader',section_os_boot).attr("disabled","disabled"); + $('.kernel input, .bootloader input',section_os_boot).val(""); + }; + }); + }; + + // Sets up the disk section + var disks_setup = function(){ + + $('fieldset',section_disks).hide(); + $('.vm_param', section_disks).hide(); + //$('#image_vs_disk',section_disks).show(); + + $('#add_disks', section_disks).click(function(){ + $('fieldset',section_disks).toggle(); + return false; + }); + + //Depending on adding a disk or a image we need to show/hide + //different options and make then mandatory or not + $('#image_vs_disk input',section_disks).click(function(){ + //$('fieldset',section_disks).show(); + $('.vm_param', section_disks).show(); + var select = $('#image_vs_disk :checked',section_disks).val(); + switch (select) + { + case "disk": + $('.add_image',section_disks).hide(); + $('.add_image',section_disks).attr("disabled","disabled"); + $('.add_disk',section_disks).show(); + $('.add_disk',section_disks).removeAttr("disabled"); + $('#TARGET',section_disks).parent().removeClass(opt_class); + $('#TARGET',section_disks).parent().addClass(man_class); + break; + case "image": + $('.add_disk',section_disks).hide(); + $('.add_disk',section_disks).attr("disabled","disabled"); + $('.add_image',section_disks).show(); + $('.add_image',section_disks).removeAttr("disabled"); + $('#TARGET',section_disks).parent().removeClass(man_class); + $('#TARGET',section_disks).parent().addClass(opt_class); + break; + } + $('#SIZE',section_disks).parent().hide(); + $('#SIZE',section_disks).parent().attr("disabled","disabled"); + $('#FORMAT',section_disks).parent().hide(); + $('#SIZE',section_disks).parent().attr("disabled","disabled"); + $('#TYPE :selected',section_disks).removeAttr("selected"); + }); + + //Chrome workaround + $('select#TYPE',section_disks).change(function(){ + $(this).trigger('click'); + }); + + //Depending on the type of disk we need to show/hide + //different options and make then mandatory or not + $('select#TYPE',section_disks).click(function(){ + var select = $(this).val(); + switch (select) { + //size,format,target + case "swap": + //size mandatory + $('#SIZE',section_disks).parent().show(); + $('#SIZE',section_disks).parent().removeAttr("disabled"); + $('#SIZE',section_disks).parent().removeClass(opt_class); + $('#SIZE',section_disks).parent().addClass(man_class); + + //target optional + $('#TARGET',section_disks).parent().removeClass(man_class); + $('#TARGET',section_disks).parent().addClass(opt_class); + + //format hidden + $('#FORMAT',section_disks).parent().hide(); + $('#FORMAT',section_disks).parent().attr("disabled","disabled"); + break; + case "fs": + //size mandatory + $('#SIZE',section_disks).parent().show(); + $('#SIZE',section_disks).parent().removeAttr("disabled"); + $('#SIZE',section_disks).parent().removeClass(opt_class); + $('#SIZE',section_disks).parent().addClass(man_class); + + //target mandatory + $('#TARGET',section_disks).parent().removeClass(opt_class); + $('#TARGET',section_disks).parent().addClass(man_class); + + //format mandatory + $('#FORMAT',section_disks).parent().show(); + $('#FORMAT',section_disks).parent().removeAttr("disabled"); + $('#FORMAT',section_disks).parent().removeClass(opt_class); + $('#FORMAT',section_disks).parent().addClass(man_class); + + break; + case "block": + //size shown and optional + $('#SIZE',section_disks).parent().show(); + $('#SIZE',section_disks).parent().removeAttr("disabled"); + $('#SIZE',section_disks).parent().removeClass(man_class); + $('#SIZE',section_disks).parent().addClass(opt_class); + + //target mandatory + $('#TARGET',section_disks).parent().removeClass(opt_class); + $('#TARGET',section_disks).parent().addClass(man_class); + + //format hidden + $('#FORMAT',section_disks).parent().hide(); + $('#FORMAT',section_disks).parent().attr("disabled","disabled"); + break; + case "floppy": + case "disk": + case "cdrom": + default: + //size hidden + $('#SIZE',section_disks).parent().hide(); + $('#SIZE',section_disks).parent().attr("disabled","disabled"); + + //target mandatory + $('#TARGET',section_disks).parent().removeClass(opt_class); + $('#TARGET',section_disks).parent().addClass(man_class); + + //format optional + $('#FORMAT',section_disks).parent().hide(); + $('#FORMAT',section_disks).parent().attr("disabled","disabled"); + } + }); + + //Our filter for the disks section fields is the mandatory + //filter for this section + var diskFilter = function(){ + return mandatory_filter(section_disks); + }; + + $('#add_disk_button',section_disks).click(function(){ + box_add_element(section_disks,'#disks_box',diskFilter); + return false; + }); + $('#remove_disk_button',section_disks).click(function(){ + box_remove_element(section_disks,'#disks_box'); + return false; + }); + }; + + // Sets up the network section + var networks_setup = function(){ + + $('.vm_param',section_networks).hide(); + $('fieldset',section_networks).hide(); + + $('#add_networks',section_networks).click(function(){ + $('fieldset',section_networks).toggle(); + return false; + }); + + //Depending on adding predefined network or not we show/hide + //some fields + $('#network_vs_niccfg input',section_networks).click(function(){ + + select = $('#network_vs_niccfg :checked',section_networks).val(); + switch (select) { + case "network": + $('.niccfg',section_networks).hide(); + $('.niccfg',section_networks).attr("disabled","disabled"); + $('.network',section_networks).show(); + $('.network',section_networks).removeAttr("disabled"); + break; + case "niccfg": + $('.network',section_networks).hide(); + $('.network',section_networks).attr("disabled","disabled"); + $('.niccfg',section_networks).show(); + $('.niccfg',section_networks).removeAttr("disabled"); + break; + } + }); + + //The filter to add a new network checks that we have selected a + //network, or that the ip or mac are set + //TODO: Improve this check + var nicFilter = function(){ + return mandatory_filter(section_networks); + }; + + $('#add_nic_button',section_networks).click(function(){ + box_add_element(section_networks,'#nics_box',nicFilter); + return false; + }); + $('#remove_nic_button',section_networks).click(function(){ + box_remove_element(section_networks,'#nics_box'); + return false; + }); + + }; + + //Sets up the input section - basicly enabling adding and removing from box + var inputs_setup = function() { + $('fieldset',section_inputs).hide(); + + $('#add_inputs',section_inputs).click(function(){ + $('fieldset',section_inputs).toggle(); + return false; + }); + + $('#add_input_button',section_inputs).click(function(){ + //no filter + box_add_element(section_inputs,'#inputs_box',True); + return false; + }); + $('#remove_input_button',section_inputs).click(function(){ + box_remove_element(section_inputs,'#inputs_box'); + return false; + }); + }; + + //Set up the graphics section + var graphics_setup = function(){ + $('fieldset',section_graphics).hide(); + $('.vm_param',section_graphics).hide(); + $('select#TYPE',section_graphics).parent().show(); + + $('#add_graphics',section_graphics).click(function(){ + $('fieldset',section_graphics).toggle(); + return false; + }); + + //Chrome workaround + $('select#TYPE',section_graphics).change(function(){ + $(this).trigger("click"); + }); + $('select#TYPE',section_graphics).click(function(){ + g_type = $(this).val(); + switch (g_type) { + case "vnc": + $('#LISTEN',section_graphics).parent().show(); + $('#PORT',section_graphics).parent().show(); + $('#PASSWD',section_graphics).parent().show(); + $('#KEYMAP',section_graphics).parent().show(); + $('#PORT',section_graphics).parent().removeAttr("disabled"); + $('#PASSWD',section_graphics).parent().removeAttr("disabled"); + $('#KEYMAP',section_graphics).parent().removeAttr("disabled"); + break; + case "sdl": + $('#LISTEN',section_graphics).parent().show(); + $('#PORT',section_graphics).parent().hide(); + $('#PASSWD',section_graphics).parent().hide(); + $('#KEYMAP',section_graphics).parent().hide(); + $('#PORT',section_graphics).parent().attr("disabled","disabled"); + $('#PASSWD',section_graphics).parent().attr("disabled","disabled"); + $('#KEYMAP',section_graphics).parent().attr("disabled","disabled"); + break; + default: + $('#LISTEN',section_graphics).parent().hide(); + $('#PORT',section_graphics).parent().hide(); + $('#PASSWD',section_graphics).parent().hide(); + $('#KEYMAP',section_graphics).parent().hide(); + + } + }); + + } + + //Set up the context section - TODO: Apply improvements here... + var context_setup = function(){ + $('fieldset',section_context).hide(); + + $('#add_context',section_context).click(function(){ + $('fieldset',section_context).toggle(); + return false; + }); + + $('#add_context_button', section_context).click(function(){ + var name = $('#var_name',section_context).val(); + var value = $('#var_value',section_context).val(); + if (!name.length || !value.length) { + notifyError("Context variable name and value must be filled in"); + return false; + } + option= ''; + $('select#context_box',section_context).append(option); + return false; + }); + + $('#remove_context_button', section_context).click(function(){ + box_remove_element(section_context,'#context_box'); + return false; + }); + + + }; + + // Set up the placement section + var placement_setup = function(){ + $('fieldset',section_placement).hide(); + + $('#add_placement',section_placement).click(function(){ + $('fieldset',section_placement).toggle(); + return false; + }); + + }; + + // Set up the raw section + var raw_setup = function(){ + $('fieldset',section_raw).hide(); + + $('#add_raw',section_raw).click(function(){ + $('fieldset',section_raw).toggle(); + return false; + }); + }; + + //***CREATE VM DIALOG MAIN BODY*** + + $('div#dialogs').append(''); + //Insert HTML in place + $('#create_template_dialog').html(create_template_tmpl); + //Enable tabs + $('#template_create_tabs').tabs({ + select:vmTabChange + }); + + //Prepare jquery dialog + var height = Math.floor($(window).height()*0.8); //set height to a percentage of the window + $('#create_template_dialog').dialog({ + autoOpen: false, + modal: true, + width: 700, + height: height + }); + + // Enhace buttons + $('#create_template_dialog button').button(); + // Setup tips200 + setupTips($('#create_template_dialog')); + + //Enable different icon for folded/unfolded categories + iconToggle(); //toogle +/- buttons + + //Sections, used to stay within their scope + var section_capacity = $('#capacity'); + var section_os_boot = $('#os_boot_opts'); + var section_disks = $('#disks'); + var section_networks = $('#networks'); + var section_inputs = $('#inputs'); + var section_graphics = $('#graphics'); + var section_context = $('#context'); + var section_placement = $('#placement'); + var section_raw = $('#raw'); + + //Different selector for items of kvm and xen (mandatory and optional) + var items = '.vm_section input,.vm_section select'; + var kvm_man_items = '.kvm input,.kvm select'; + var kvm_opt_items = '.kvm_opt input, .kvm_opt select'; + var kvm_items = kvm_man_items +','+kvm_opt_items; + var xen_man_items = '.xen input,.xen select'; + var xen_opt_items = '.xen_opt input, .xen_opt select'; + var xen_items = xen_man_items +','+ xen_opt_items; + + //Starting template type, optional items class and mandatory items class + var templ_type = "kvm"; + var opt_class=".kvm_opt"; + var man_class=".kvm"; + + enable_kvm(); //enable all kvm options + + //Fold/unfold all sections button + $('#fold_unfold_vm_params').toggle( + function(){ + $('.vm_section fieldset').show(); + return false; + }, + function(){ + $('.vm_section fieldset').hide(); + $('.vm_section fieldset').first().show(); //Show capacity opts + return false; + }); + + //initialise all sections + capacity_setup(); + os_boot_setup(); + disks_setup(); + networks_setup(); + inputs_setup(); + graphics_setup(); + context_setup(); + placement_setup(); + raw_setup(); + + //Process form + $('button#create_template_form_easy').click(function(){ + //validate form + + var vm_json = {}; + + //process capacity options + var scope = section_capacity; + + if (!mandatory_filter(scope)){ + notifyError("There are mandatory fields missing in the capacity section"); + return false; + }; + addSectionJSON(vm_json,scope); + + //process os_boot_opts + scope= section_os_boot; + switch (templ_type){ + case "xen": + boot_method = $('#boot_method option:selected',scope).val(); + if (!boot_method.length){ + notifyError("Xen templates must specify a boot method"); + return false;} + }; + + if (!mandatory_filter(scope)){ + notifyError("There are mandatory fields missing in the OS Boot options section"); + return false; + }; + vm_json["OS"] = {}; + addSectionJSON(vm_json["OS"],scope); + + //process disks -> fetch from box + scope = section_disks; + vm_json["DISK"] = []; + addBoxJSON(vm_json["DISK"],scope,'#disks_box'); + + //process nics -> fetch from box + scope = section_networks; + vm_json["NIC"] = []; + addBoxJSON(vm_json["NIC"],scope,'#nics_box'); + + //process inputs -> fetch from box + scope = section_inputs; + vm_json["INPUT"] = []; + addBoxJSON(vm_json["INPUT"],scope,'#inputs_box'); + + //process graphics -> fetch fields with value + scope = section_graphics; + vm_json["GRAPHICS"] = {}; + addSectionJSON(vm_json["GRAPHICS"],scope); + + //context + scope = section_context; + var context = $('#CONTEXT',scope).val(); + vm_json["CONTEXT"] = {}; + $('#context_box option',scope).each(function(){ + name = $(this).attr("name"); + value = $(this).val(); + vm_json["CONTEXT"][name]=value; + }); + + //placement -> fetch with value + scope = section_placement; + addSectionJSON(vm_json,scope); + + //raw -> if value set type to driver and fetch + scope = section_raw; + vm_json["RAW"] = {}; + addSectionJSON(vm_json["RAW"],scope); + + // remove empty elements + vm_json = removeEmptyObjects(vm_json); + + //wrap it in the "vmtemplate" object + vm_json = {vmtemplate: vm_json}; + + + Sunstone.runAction("Template.create",vm_json); + + $('#create_template_dialog').dialog('close'); + return false; + }); + + //Handle manual forms + $('button#create_template_form_manual').click(function(){ + var template = $('#textarea_vm_template').val(); + + //wrap it in the "vm" object + template = {"vmtemplate": {"template_raw": template}}; + + Sunstone.runAction("Template.create",template); + $('#create_template_dialog').dialog('close'); + return false; + }); + + //Reset form - empty boxes + $('button#reset_vm_form').click(function(){ + $('select#disks_box option',section_disks).remove(); + $('select#nics_box option',section_networks).remove(); + $('select#inputs_box option',section_inputs).remove(); + return true; + }); + + } diff --git a/src/sunstone/public/js/plugins/vms-tab.js b/src/sunstone/public/js/plugins/vms-tab.js index 9fbbb65bc5..d42e254b12 100644 --- a/src/sunstone/public/js/plugins/vms-tab.js +++ b/src/sunstone/public/js/plugins/vms-tab.js @@ -39,460 +39,23 @@ var vms_tab_content = \ '; -var create_vm_tmpl = -'