From 597d3ec20cb9e0d0c19a480cd9f7a0001513ee54 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Tue, 11 Feb 2020 14:33:34 +0100 Subject: [PATCH] F #3969: Support for RDP links in sunstone (#4175) (cherry picked from commit 31880fb5a652d6dc520368ddcdf31f413215b2f4) --- src/sunstone/public/app/opennebula/vm.js | 16 +++- src/sunstone/public/app/sunstone.js | 9 ++ src/sunstone/public/app/sunstone/buttons.hbs | 5 ++ .../form-panels/create/wizard-tabs/network.js | 11 +++ .../create/wizard-tabs/network/nic-tab.js | 27 ++++++ .../wizard-tabs/network/nic-tab/html.hbs | 13 +++ .../templates-tab/form-panels/instantiate.js | 14 +++- .../public/app/tabs/vms-tab/actions.js | 28 +++++++ .../public/app/tabs/vms-tab/buttons.js | 9 +- .../public/app/tabs/vms-tab/hooks/state.js | 7 ++ src/sunstone/public/app/utils/nics-section.js | 42 +++++++++- .../public/app/utils/nics-section/dd.hbs | 15 ++++ src/sunstone/public/app/utils/rdp.js | 83 +++++++++++++++++++ 13 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 src/sunstone/public/app/utils/rdp.js diff --git a/src/sunstone/public/app/opennebula/vm.js b/src/sunstone/public/app/opennebula/vm.js index 01c1745053..d046791f4d 100644 --- a/src/sunstone/public/app/opennebula/vm.js +++ b/src/sunstone/public/app/opennebula/vm.js @@ -671,6 +671,7 @@ define(function(require) { "isDiskGraphsSupported": isDiskGraphsSupported, "isNICAttachSupported": isNICAttachSupported, "isVNCSupported": isVNCSupported, + "isRDPSupported": isRDPSupported, "isSPICESupported": isSPICESupported, "getName": function(id){ return OpenNebulaAction.getName(id, RESOURCE); @@ -799,7 +800,11 @@ define(function(require) { $.each(nic, function(index, value) { $.each(NIC_IP_ATTRS, function(j, attr){ if (value[attr]) { - ips.push(value[attr]); + if ( attr === "IP" && value["RDP"] === "YES") { + ips.push(value[attr] + " RDP"); + } else { + ips.push(value[attr]); + } } }); }); @@ -866,5 +871,14 @@ define(function(require) { $.inArray(state, VNC_STATES) != -1); } + // returns true if the RDP button should be enabled + function isRDPSupported(element) { + return ( element.TEMPLATE && + element.TEMPLATE.NIC && + element.TEMPLATE.NIC.length > 0 && + element.TEMPLATE.NIC.filter(nic => nic.RDP && nic.RDP == "YES") + ); + } + return VM; }); diff --git a/src/sunstone/public/app/sunstone.js b/src/sunstone/public/app/sunstone.js index f7e8c35bf8..ebfd395e69 100644 --- a/src/sunstone/public/app/sunstone.js +++ b/src/sunstone/public/app/sunstone.js @@ -438,6 +438,11 @@ define(function(require) { text = button.text; buttonCode = "
  • " + text + "
  • "; break; + case "vmsremote_buttons": + buttonContext = $("#" + customId + "vmsremote_buttons", buttonsRow); + text = button.text; + buttonCode = "
  • " + text + "
  • "; + break; case "more_select": buttonContext = $("#" + customId + "more_buttons", buttonsRow); text = button.text; @@ -502,6 +507,10 @@ define(function(require) { $("button[data-toggle=" + customId + "vmsmigration_buttons]", actionBlock).remove(); } + if ($("#" + customId + "vmsremote_buttons li", actionBlock).length == 0) { + $("button[data-toggle=" + customId + "vmsremote_buttons]", actionBlock).remove(); + } + if ($("#" + customId + "vmsdelete_buttons li", actionBlock).length == 0) { $("button[data-toggle=" + customId + "vmsdelete_buttons]", actionBlock).remove(); } diff --git a/src/sunstone/public/app/sunstone/buttons.hbs b/src/sunstone/public/app/sunstone/buttons.hbs index 684c9da8cb..3a8b8436e6 100644 --- a/src/sunstone/public/app/sunstone/buttons.hbs +++ b/src/sunstone/public/app/sunstone/buttons.hbs @@ -41,6 +41,11 @@ + + + diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network.js b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network.js index f303d80dce..9568fe0412 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network.js +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network.js @@ -70,6 +70,7 @@ define(function(require) { WizardTab.prototype.renameTabLinks = _renameTabLinks; WizardTab.prototype.addNicTab = _addNicTab; WizardTab.prototype.notify = _notify; + WizardTab.prototype.enableRDP = _enableRDP; return WizardTab; @@ -208,6 +209,7 @@ define(function(require) { }); that.renameTabLinks(context); + that.enableRDP(context); if (templateJSON.NIC) { delete templateJSON.NIC; @@ -279,6 +281,7 @@ define(function(require) { content.attr("nicId", that.numberOfNics); that.renameTabLinks(context); + that.enableRDP(context); that.nicTabObjects[that.numberOfNics] = nicTab; // close icon: removing the tab on click @@ -302,6 +305,7 @@ define(function(require) { } that.renameTabLinks(context); + that.enableRDP(context); that.numberOfNics --; }); } @@ -322,6 +326,13 @@ define(function(require) { } } + function _enableRDP(context) { + const canRDP = $("fieldset#rdp_connection input[type='checkbox']:not(#" + that.nicTabId + "_rdp):checked", context).length === 0; + + if (canRDP) $("fieldset#rdp_connection").show(); + else $("fieldset#rdp_connection", that.context).hide(); + } + function _notify(context, templateJSON) { if (templateJSON.VROUTER == "YES"){ while($("i.remove-tab", context).length > 0){ diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab.js b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab.js index 632afce6e1..4b54b67cf2 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab.js +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab.js @@ -307,6 +307,11 @@ define(function(require) { context.on("change", "input[name='" + that.nicTabId + "_rank_select']", function() { $("input#"+that.nicTabId+"_SCHED_RANK", context).val(this.value); }); + + context.on("change", "#" + that.nicTabId + "_rdp", function() { + const isRDPActivated = $(this).prop('checked'); + _hide_rdp(that.nicTabId, isRDPActivated, context); + }); } function _retrieve(context) { @@ -356,6 +361,10 @@ define(function(require) { nicJSON["PARENT"] = $("#" + this.nicTabId + "_alias_parent", context).val(); } + if($("input#" + this.nicTabId + "_rdp", context).prop("checked")) { + nicJSON["RDP"] = "YES"; + } + return nicJSON; } @@ -464,6 +473,14 @@ define(function(require) { } } + const isRDPActivated = ( + templateJSON["RDP"] && + templateJSON["RDP"] === "YES" && + $("fieldset#rdp_connection input:not(#" + that.nicTabId + "_rdp):checked", context).length === 0 + ) ? true : false; + + $("input#" + this.nicTabId + "_rdp", context).prop("checked", isRDPActivated); + WizardFields.fill(context, templateJSON); } @@ -501,4 +518,14 @@ define(function(require) { } }); } + + function _hide_rdp(nicTabId, isRDPActivated, context) { + $("#template_create_network_tabs_content > div:not(#" + nicTabId + ") fieldset#rdp_connection", context).each(function() { + if (isRDPActivated) { + $(this).hide(); + } else { + $(this).show(); + } + }); + } }); diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab/html.hbs b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab/html.hbs index 3a07b1cf0f..209b994485 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab/html.hbs +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/create/wizard-tabs/network/nic-tab/html.hbs @@ -48,6 +48,19 @@ {{{tip (tr "The Schedule will decide which is the best virtual network")}}} +
    + {{tr "RDP connection"}} +
    + + +
    + +
    diff --git a/src/sunstone/public/app/tabs/templates-tab/form-panels/instantiate.js b/src/sunstone/public/app/tabs/templates-tab/form-panels/instantiate.js index e8d3959fc4..192538002b 100644 --- a/src/sunstone/public/app/tabs/templates-tab/form-panels/instantiate.js +++ b/src/sunstone/public/app/tabs/templates-tab/form-panels/instantiate.js @@ -276,15 +276,21 @@ define(function(require) { var pcis = []; var alias = []; + var rdp = true; $.each(networks, function(){ if (this.TYPE == "NIC"){ pcis.push(this); } else if (this.PARENT) { alias.push(this); } else { + (rdp && this.RDP == "YES") + ? rdp = false + : delete this["RDP"]; + nics.push(this); } }); + debugger tmp_json.NIC = nics; tmp_json.NIC_ALIAS = alias; @@ -337,11 +343,11 @@ define(function(require) { $.extend(tmp_json, CapacityInputs.retrieveChanges(capacityContext)); extra_info["template"] = tmp_json; - for (var i = 0; i < n_times_int; i++) { - extra_info["vm_name"] = vm_name.replace(/%i/gi, i); // replace wildcard + for (var i = 0; i < n_times_int; i++) { + extra_info["vm_name"] = vm_name.replace(/%i/gi, i); // replace wildcard - Sunstone.runAction("Template."+action, [template_id], extra_info); - } + Sunstone.runAction("Template."+action, [template_id], extra_info); + } }); return false; diff --git a/src/sunstone/public/app/tabs/vms-tab/actions.js b/src/sunstone/public/app/tabs/vms-tab/actions.js index 861511d290..0de9fb0770 100644 --- a/src/sunstone/public/app/tabs/vms-tab/actions.js +++ b/src/sunstone/public/app/tabs/vms-tab/actions.js @@ -23,6 +23,7 @@ define(function(require) { var CommonActions = require('utils/common-actions'); var Vnc = require('utils/vnc'); var Spice = require('utils/spice'); + var Rdp = require('utils/rdp'); var TAB_ID = require('./tabId'); var CREATE_DIALOG_ID = require('./form-panels/create/formPanelId'); @@ -171,6 +172,33 @@ define(function(require) { dialog.show(); } }, + "VM.save_rdp" : { + type: "custom", + call: function() { + var vm = Sunstone.getElementRightInfo(TAB_ID); + + if (vm && vm.NAME && vm.TEMPLATE && Array.isArray(vm.TEMPLATE.NIC)) { + var name = vm.NAME; + var nic = vm.TEMPLATE.NIC.find(n => n.RDP === "YES"); + var credentials = {}; + + if (vm.TEMPLATE.CONTEXT) { + var context = vm.TEMPLATE.CONTEXT; + + for (var prop in context) { + var propUpperCase = String(prop).toUpperCase(); + (propUpperCase === "USERNAME" || propUpperCase === "PASSWORD") + && (credentials[propUpperCase] = context[prop]); + } + } + nic && Rdp.downloadFile(nic.IP, name, credentials); + } else { + Notifier.notifyError(Locale.tr("RDP file error")); + return false; + } + } + }, + "VM.startvnc" : { type: "custom", call: function() { diff --git a/src/sunstone/public/app/tabs/vms-tab/buttons.js b/src/sunstone/public/app/tabs/vms-tab/buttons.js index e29464a20c..d17dbba446 100644 --- a/src/sunstone/public/app/tabs/vms-tab/buttons.js +++ b/src/sunstone/public/app/tabs/vms-tab/buttons.js @@ -196,9 +196,16 @@ define(function(require) { }, "VM.startvnc" : { type: "action", - text: ' ' + Locale.tr("VNC"), + text: Locale.tr("VNC"), + layout: "vmsremote_buttons", custom_classes: "only-sunstone-info vnc-sunstone-info" }, + "VM.save_rdp" : { + type: "action", + text: Locale.tr("RDP"), + layout: "vmsremote_buttons", + custom_classes: "only-sunstone-info rdp-sunstone-info" + }, "VM.startspice" : { type: "action", text: ' ' + Locale.tr("SPICE"), diff --git a/src/sunstone/public/app/tabs/vms-tab/hooks/state.js b/src/sunstone/public/app/tabs/vms-tab/hooks/state.js index be991cc502..f487bffbb8 100644 --- a/src/sunstone/public/app/tabs/vms-tab/hooks/state.js +++ b/src/sunstone/public/app/tabs/vms-tab/hooks/state.js @@ -48,6 +48,13 @@ define(function(require) { $(".vnc-sunstone-info").hide(); } + // Enable / disable rdp button + if (OpenNebulaVM.isRDPSupported(element)) { + $(".rdp-sunstone-info").show(); + } else { + $(".rdp-sunstone-info").hide(); + } + if (OpenNebulaVM.isSPICESupported(element)) { $(".spice-sunstone-info").show(); } else { diff --git a/src/sunstone/public/app/utils/nics-section.js b/src/sunstone/public/app/utils/nics-section.js index 99de3819e0..b2deb5922d 100644 --- a/src/sunstone/public/app/utils/nics-section.js +++ b/src/sunstone/public/app/utils/nics-section.js @@ -439,6 +439,12 @@ define(function(require) { $(".SCHED_RANK", dd_context).val(this.value); }); + $("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_rdp", dd_context).on("change", function() { + const isRDPActivated = $(this).prop('checked'); + const idAccordion = "#provision_accordion_dd_" + dd_context["dd_id"]; + _hide_rdp(idAccordion, isRDPActivated, context); + }); + if ( options.nic && options.nic["NETWORK_MODE"] && options.nic["NETWORK_MODE"] === "auto" ) { $("input#provision_accordion_dd_"+provision_nic_accordion_dd_id+"_network_mode", dd_context).prop("checked", true); @@ -492,6 +498,17 @@ define(function(require) { _fill_alias(options.nic.PARENT); } + // fill rdp connection + const isRDPActivated = ( + options.nic && + options.nic["RDP"] && + options.nic["RDP"] === "YES" && + $("fieldset#rdp_connection input:not(#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_rdp):checked", context).length === 0 + ) ? true : false; + + $("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_rdp", context).prop("checked", isRDPActivated); + _enableRDP("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context) + provision_nic_accordion_dd_id += 1; vnetsTable.initialize(); @@ -535,11 +552,13 @@ define(function(require) { dd_context.remove(); var index = _nics.findIndex(nic => nic.NAME === ("NIC" + dd_context["nic_id"])); - + _nics.splice(index, 1); - + nicId --; + _enableRDP("#provision_accordion_dd_"+dd_context["nic_id"], context) + return false; }); @@ -590,6 +609,8 @@ define(function(require) { _generate_provision_network_table($(".accordion", context), options); nicId ++; + + _enableRDP("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context) }); if (options.click_add_button == true){ @@ -656,4 +677,21 @@ define(function(require) { } }); } + + function _hide_rdp(idAccordion, isRDPActivated, context) { + $(".accordion-item > div:not(" + idAccordion + ") fieldset#rdp_connection", context).each(function() { + if (isRDPActivated) { + $(this).hide(); + } else { + $(this).show(); + } + }); + } + + function _enableRDP(idAccordion, context) { + const canRDP = $("fieldset#rdp_connection input[type='checkbox']:not(" + idAccordion + "_rdp):checked", context).length === 0; + + if (canRDP) $("fieldset#rdp_connection", context).has("input:not(:checked)").show(); + else $("fieldset#rdp_connection", context).has("input:not(:checked)").hide(); + } }); diff --git a/src/sunstone/public/app/utils/nics-section/dd.hbs b/src/sunstone/public/app/utils/nics-section/dd.hbs index 7ee6fc9a3e..b0d3cc0e9d 100644 --- a/src/sunstone/public/app/utils/nics-section/dd.hbs +++ b/src/sunstone/public/app/utils/nics-section/dd.hbs @@ -57,6 +57,21 @@
    +
    + {{tr "RDP connection"}} +
    +
    + + +
    + +
    +
    diff --git a/src/sunstone/public/app/utils/rdp.js b/src/sunstone/public/app/utils/rdp.js new file mode 100644 index 0000000000..4747e3a7d1 --- /dev/null +++ b/src/sunstone/public/app/utils/rdp.js @@ -0,0 +1,83 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2019, OpenNebula Project, OpenNebula Systems */ +/* */ +/* 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. */ +/* -------------------------------------------------------------------------- */ + +define(function() { + return { + "fileText": _fileText, + "downloadFile": _downloadFile + }; + + function _downloadFile(ip, name, credentials) { + var file = _fileText(ip, credentials.USERNAME, credentials.PASSWORD); + + var link = $("", { + href: 'data:text/plain;charset=utf-8,' + encodeURIComponent(file), + download: name + ".rdp", + }).css({ + display: 'none', + }).appendTo("body"); + + link.get(0).click(function(e) { + e.preventDefault(); + }); + + link.remove(); + } + + function _fileText(ip, username, password) { + let file = "" + + file += "screen mode id:i:2\n"; + file += "desktopwidth:i:1280\n"; + file += "desktopheight:i:960\n"; + file += "session bpp:i:32\n"; + file += "winposstr:s:2,3,1430,104,2230,704\n"; + file += "compression:i:1\n"; + file += "keyboardhook:i:2\n"; + file += "displayconnectionbar:i:1\n"; + file += "disable wallpaper:i:1\n"; + file += "disable full window drag:i:1\n"; + file += "allow desktop composition:i:0\n"; + file += "allow font smoothing:i:0\n"; + file += "disable menu anims:i:1\n"; + file += "disable themes:i:0\n"; + file += "disable cursor setting:i:0\n"; + file += "bitmapcachepersistenable:i:1\n"; + file += "full address:s:" + ip + "\n"; + if (username) { file += "username:s:" + username + "\n"; } + if (password) { file += "password:s:" + password + "\n"; } + file += "audiomode:i:0\n"; + file += "redirectprinters:i:1\n"; + file += "redirectcomports:i:0\n"; + file += "redirectsmartcards:i:1\n"; + file += "redirectclipboard:i:1\n"; + file += "redirectposdevices:i:0\n"; + file += "autoreconnection enabled:i:1\n"; + file += "authentication level:i:0\n"; + file += "prompt for credentials:i:0\n"; + file += "negotiate security layer:i:1\n"; + file += "remoteapplicationmode:i:0\n"; + file += "alternate shell:s:\n"; + file += "shell working directory:s:\n"; + file += "gatewayhostname:s:\n"; + file += "gatewayusagemethod:i:4\n"; + file += "gatewaycredentialssource:i:4\n"; + file += "gatewayprofileusagemethod:i:0\n"; + file += "promptcredentialonce:i:1\n"; + + return file; + } +});