diff --git a/install.sh b/install.sh index 6d26cf5301..328c575936 100755 --- a/install.sh +++ b/install.sh @@ -951,9 +951,6 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \ src/mad/ruby/ssh_stream.rb \ src/vnm_mad/one_vnm.rb \ src/oca/ruby/opennebula.rb \ - src/sunstone/OpenNebulaVNC.rb \ - src/sunstone/opennebula_guac.rb \ - src/sunstone/opennebula_vmrc.rb \ src/sunstone/OpenNebulaAddons.rb \ src/vmm_mad/remotes/vcenter/vcenter_driver.rb \ src/vmm_mad/remotes/nsx/nsx_driver.rb \ @@ -2477,12 +2474,16 @@ SUNSTONE_ETC_VIEW_MIXED="src/sunstone/etc/sunstone-views/mixed/admin.yaml \ src/sunstone/etc/sunstone-views/mixed/groupadmin.yaml" SUNSTONE_MODELS_FILES="src/sunstone/models/OpenNebulaJSON.rb \ - src/sunstone/models/SunstoneServer.rb \ - src/sunstone/models/SunstoneViews.rb \ - src/sunstone/models/OpenNebula2FA/SunstoneWebAuthn.rb \ - src/sunstone/models/OpenNebula2FA/sunstone_qr_code.rb \ - src/sunstone/models/OpenNebula2FA/sunstone_optp.rb \ - src/sunstone/models/OpenNebula2FA/sunstone_2f_auth.rb" + src/sunstone/models/SunstoneServer.rb \ + src/sunstone/models/SunstoneViews.rb \ + src/sunstone/models/sunstone_vm_helper.rb \ + src/sunstone/models/sunstone_vnc.rb \ + src/sunstone/models/sunstone_guac.rb \ + src/sunstone/models/sunstone_vmrc.rb \ + src/sunstone/models/OpenNebula2FA/SunstoneWebAuthn.rb \ + src/sunstone/models/OpenNebula2FA/sunstone_qr_code.rb \ + src/sunstone/models/OpenNebula2FA/sunstone_optp.rb \ + src/sunstone/models/OpenNebula2FA/sunstone_2f_auth.rb" SUNSTONE_MODELS_JSON_FILES="src/sunstone/models/OpenNebulaJSON/HostJSON.rb \ src/sunstone/models/OpenNebulaJSON/ImageJSON.rb \ diff --git a/share/linters/.rubocop.yml b/share/linters/.rubocop.yml index 25341ef7e4..0bfc091a7f 100644 --- a/share/linters/.rubocop.yml +++ b/share/linters/.rubocop.yml @@ -240,7 +240,10 @@ AllCops: - src/sunstone/test/spec/host_spec.rb - src/sunstone/test/spec/user_spec.rb - src/sunstone/OpenNebulaAddons.rb - - src/sunstone/OpenNebulaVNC.rb + - src/sunstone/models/sunstone_vnc.rb + - src/sunstone/models/sunstone_guac.rb + - src/sunstone/models/sunstone_vmrc.rb + - src/sunstone/models/sunstone_vm_helper.rb - src/sunstone/models/OpenNebulaJSON.rb - src/sunstone/models/SunstoneViews.rb - src/sunstone/models/SunstoneServer.rb diff --git a/src/sunstone/bin/novnc-server b/src/sunstone/bin/novnc-server index 4120bfb9f0..27e59fba90 100755 --- a/src/sunstone/bin/novnc-server +++ b/src/sunstone/bin/novnc-server @@ -25,6 +25,7 @@ if !ONE_LOCATION SHARE_LOCATION = '/usr/share/one' ETC_LOCATION = '/etc/one' RUBY_LIB_LOCATION = '/usr/lib/one/ruby' + SUNSTONE_ROOT_DIR = '/usr/lib/one/sunstone' GEMS_LOCATION = '/usr/share/one/gems' else VAR_LOCATION = ONE_LOCATION + '/var' @@ -32,13 +33,13 @@ else SHARE_LOCATION = ONE_LOCATION + '/share' ETC_LOCATION = ONE_LOCATION + '/etc' RUBY_LIB_LOCATION = ONE_LOCATION + '/lib/ruby' + SUNSTONE_ROOT_DIR = ONE_LOCATION + '/lib/sunstone' GEMS_LOCATION = ONE_LOCATION + '/share/gems' end VNC_LOG = LOG_LOCATION + "/novnc.log" CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-server.conf" PLUGIN_CONFIGURATION_FILE = ETC_LOCATION + "/sunstone-plugins.yaml" -SUNSTONE_ROOT_DIR = File.dirname(__FILE__) if File.directory?(GEMS_LOCATION) $LOAD_PATH.reject! {|l| l =~ /vendor_ruby/ } @@ -53,7 +54,7 @@ $LOAD_PATH << SUNSTONE_ROOT_DIR + '/models' require 'logger' require 'yaml' -require 'OpenNebulaVNC' +require 'sunstone_vnc' $log=Logger.new(VNC_LOG) @@ -65,7 +66,7 @@ rescue Exception => e exit 1 end -vnc=OpenNebulaVNC.new(conf, $log) +vnc=SunstoneVNC.new(conf, $log) if ARGV[0] res=case ARGV[0].downcase.to_sym diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index f60b29cf69..7d21e457fa 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -19,9 +19,10 @@ require 'CloudServer' require 'OpenNebulaJSON' include OpenNebulaJSON -require 'OpenNebulaVNC' -require 'opennebula_guac' -require 'opennebula_vmrc' +require 'sunstone_vnc' +require 'sunstone_guac' +require 'sunstone_vmrc' +require 'sunstone_vm_helper' require 'OpenNebulaAddons' require 'OpenNebulaJSON/JSONUtils' #include JSONUtils diff --git a/src/sunstone/opennebula_guac.rb b/src/sunstone/models/sunstone_guac.rb similarity index 98% rename from src/sunstone/opennebula_guac.rb rename to src/sunstone/models/sunstone_guac.rb index 8966f3862d..f64d8e021f 100644 --- a/src/sunstone/opennebula_guac.rb +++ b/src/sunstone/models/sunstone_guac.rb @@ -109,7 +109,7 @@ GUAC_STATES = [ ] # Class for Guacamole connection configuration -class OpenNebulaGuac +class SunstoneGuac attr_reader :proxy_port @@ -159,6 +159,9 @@ class OpenNebulaGuac return error(400, 'Fireedge_key is not available') end + info = SunstoneVMHelper.get_remote_info(vm_resource) + encode_info = Base64.encode64(info.to_json) + data = encrypt_data( { 'connection' => { @@ -174,7 +177,7 @@ class OpenNebulaGuac } ) - [200, { :token => data }.to_json] + [200, { :token => data, :info => encode_info }.to_json] end private diff --git a/src/sunstone/models/sunstone_vm_helper.rb b/src/sunstone/models/sunstone_vm_helper.rb new file mode 100644 index 0000000000..9ad4e92d90 --- /dev/null +++ b/src/sunstone/models/sunstone_vm_helper.rb @@ -0,0 +1,88 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2020, 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. # +#--------------------------------------------------------------------------- # + +module SunstoneVMHelper + + class << self + + def state_to_str(id, lcm_id) + id = id.to_i + state_str = VirtualMachine::VM_STATE[id] + + if state_str == 'ACTIVE' + lcm_id = lcm_id.to_i + return VirtualMachine::LCM_STATE[lcm_id] + end + + return state_str + end + + def get_ips(vm) + ips = [] + + vm_nics = [] + + if !vm['TEMPLATE']['NIC'].nil? + vm_nics = [vm['TEMPLATE']['NIC']].flatten + end + + if !vm['TEMPLATE']['PCI'].nil? + vm_nics = [vm_nics, vm['TEMPLATE']['PCI']].flatten + end + + vm_nics.each do |nic| + %w[IP EXTERNAL_IP IP6_GLOBAL IP6_ULA IP6 + VROUTER_IP VROUTER_IP6_GLOBAL VROUTER_IP6_ULA].each do |attr| + if nic.key?(attr) + ips.push(nic[attr]) + end + end + end + + VirtualMachine::EXTERNAL_IP_ATTRS.each do |attr| + external_ip = vm['MONITORING'][attr] + + if !external_ip.nil? && !ips.include?(external_ip) + ips.push(external_ip) + end + end + + return ips + end + + + def get_remote_info(vm_resource) + vm = vm_resource.to_hash['VM'] + + service_id = vm['USER_TEMPLATE']['SERVICE_ID'] + + info = { + :id => vm['ID'], + :name => vm['NAME'], + :state => state_to_str(vm['STATE'], vm['LCM_STATE']), + :start_time => vm['STIME'], + :networks => get_ips(vm) + } + + info[:service_id] = service_id if service_id + + return info + end + + end + +end + \ No newline at end of file diff --git a/src/sunstone/opennebula_vmrc.rb b/src/sunstone/models/sunstone_vmrc.rb similarity index 99% rename from src/sunstone/opennebula_vmrc.rb rename to src/sunstone/models/sunstone_vmrc.rb index e2082beb86..865665cc4e 100644 --- a/src/sunstone/opennebula_vmrc.rb +++ b/src/sunstone/models/sunstone_vmrc.rb @@ -107,7 +107,7 @@ VMRC_STATES = [ ] # Class for necessary VMRC ticket creation -class OpenNebulaVMRC +class SunstoneVMRC attr_reader :proxy_port diff --git a/src/sunstone/OpenNebulaVNC.rb b/src/sunstone/models/sunstone_vnc.rb similarity index 98% rename from src/sunstone/OpenNebulaVNC.rb rename to src/sunstone/models/sunstone_vnc.rb index bb5340cff6..d51bc8fde8 100644 --- a/src/sunstone/OpenNebulaVNC.rb +++ b/src/sunstone/models/sunstone_vnc.rb @@ -103,7 +103,7 @@ VNC_STATES = [ #68, #HOTPLUG_SAVEAS_STOPPED ] -class OpenNebulaVNC +class SunstoneVNC attr_reader :proxy_port @@ -243,10 +243,13 @@ class OpenNebulaVNC return error(500, "Cannot create VNC proxy token") end + info = SunstoneVMHelper.get_remote_info(vm_resource) + encode_info = Base64.encode64(info.to_json) + info = { :password => vnc_pw, :token => random_str, - :vm_name => vm_resource['NAME'] + :info => encode_info } return [200, info.to_json] diff --git a/src/sunstone/public/app/console/spice.js b/src/sunstone/public/app/console/spice.js index 65ccf5f511..0359a3aa34 100644 --- a/src/sunstone/public/app/console/spice.js +++ b/src/sunstone/public/app/console/spice.js @@ -17,7 +17,8 @@ define(function(require) { require('spice-main'); - var host = null, port = null; + var UtilsConnection = require("utils/info-connection/utils"); + var sc; function spice_set_cookie(name, value, days) { @@ -41,10 +42,16 @@ define(function(require) { } function connect() { - var host, port, password, scheme = "ws://", uri; + var info = spice_query_var('info', undefined); + var info_decode = UtilsConnection.decodeInfoConnection(info); + UtilsConnection.printInfoConnection($('.SPICE_info'), info_decode) + + if (info_decode && info_decode.name) { + document.title = info_decode.name + } // By default, use the host and port of server that served this file - host = spice_query_var('host', window.location.hostname); + var host = spice_query_var('host', window.location.hostname); // Note that using the web server port only makes sense // if your web server has a reverse proxy to relay the WebSocket @@ -57,20 +64,21 @@ define(function(require) { default_port = 443; } } - port = spice_query_var('port', default_port); + + var scheme = "ws://" if (window.location.protocol == 'https:') { scheme = "wss://"; } // If a token variable is passed in, set the parameter in a cookie. // This is used by nova-spiceproxy. - token = spice_query_var('token', null); + var token = spice_query_var('token', null); if (token) { spice_set_cookie('token', token, 1) } - password = spice_query_var('password', ''); - path = spice_query_var('path', 'websockify'); + var password = spice_query_var('password', ''); + var port = spice_query_var('port', default_port); if ((!host) || (!port)) { console.log("must specify host and port in URL"); @@ -81,7 +89,7 @@ define(function(require) { sc.stop(); } - uri = scheme + host + ":" + port + "?token=" + token; + var uri = scheme + host + ":" + port + "?token=" + token; try { sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div", @@ -91,7 +99,6 @@ define(function(require) { alert(e.toString()); disconnect(); } - } function disconnect() { diff --git a/src/sunstone/public/app/console/vmrc.js b/src/sunstone/public/app/console/vmrc.js index a5237aa440..19ae43855d 100644 --- a/src/sunstone/public/app/console/vmrc.js +++ b/src/sunstone/public/app/console/vmrc.js @@ -17,8 +17,10 @@ define(function(require) { require("jquery"); require("jquery-ui"); + var WMKS = require("wmks"); - var Config = require("sunstone-config"); + var UtilsConnection = require("utils/info-connection/utils"); + var _wmks; var _is_encrypted = ""; @@ -38,16 +40,6 @@ define(function(require) { setStatus("Something went wrong, connection is closed", "Failed"); } } - - function desktopNameChange(name) { - if (e.detail.name) { - setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " + name); - } - } - - function credentialsRequired(e) { - setStatus("Something went wrong, more credentials must be given to continue", "Failed"); - } function sendCtrlAltDel() { if (_wmks) { _wmks.sendCAD(); } @@ -95,7 +87,14 @@ define(function(require) { var host = getQueryVariable("host"); var port = getQueryVariable("port"); var ticket = getQueryVariable("ticket"); - var vm_name = getQueryVariable("name"); + + var info = spice_query_var('info', undefined); + var info_decode = UtilsConnection.decodeInfoConnection(info); + UtilsConnection.printInfoConnection($('.VMRC_info'), info_decode) + + if (info_decode && info_decode.name) { + document.title = info_decode.name + } if (window.location.protocol === "https:") { URL = "wss"; @@ -120,7 +119,7 @@ define(function(require) { try{ _wmks = WMKS.createWMKS("wmksContainer", {}) .register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE, - function (event, data) { + function (_, data) { if (typeof cons !== 'undefined' && data.state == cons.ConnectionState.CONNECTED) { console.log("connection state change : connected"); } @@ -129,9 +128,9 @@ define(function(require) { _wmks.eventHandlers["connectionstatechange"].push(connected); _wmks.eventHandlers["disconnect"] = disconnectedFromServer; + _wmks.vm_name = info_decode && info_decode.name; _wmks.connect(URL); - _wmks["vm_name"] = vm_name; }catch(err){ setStatus("Something went wrong, connection is closed", "Failed"); console.log("error start VMRC ", err); diff --git a/src/sunstone/public/app/console/vnc.js b/src/sunstone/public/app/console/vnc.js index b138754b3e..ebd95224ae 100644 --- a/src/sunstone/public/app/console/vnc.js +++ b/src/sunstone/public/app/console/vnc.js @@ -15,11 +15,12 @@ /* -------------------------------------------------------------------------- */ define(function(require) { - var RFB = require("vnc-rfb").default; var Config = require("sunstone-config"); + var UtilsConnection = require("utils/info-connection/utils"); + + var RFB = require("vnc-rfb").default; var _rfb; var _is_encrypted = ""; - var _vm_name; function setStatus(message="", status=""){ $(".NOVNC_message").text(message); @@ -27,12 +28,12 @@ define(function(require) { } function connected(){ - setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + (_vm_name || _rfb._fb_name)); + setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + _rfb._fb_name); } function disconnectedFromServer(e){ if (e.detail.clean) { - setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + (_vm_name || _rfb._fb_name)); + setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + _rfb._fb_name); } else { setStatus("Something went wrong, connection is closed", "Failed"); } @@ -40,7 +41,7 @@ define(function(require) { function desktopNameChange(e) { if (e.detail.name) { - setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + (_vm_name || e.detail.name)); + setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + e.detail.name); } } @@ -127,13 +128,20 @@ define(function(require) { } return(false); } - token = window.token; + var URL = ""; var proxy_host = window.location.hostname; var proxy_port = Config.vncProxyPort; var token = getQueryVariable("token"); var password = getQueryVariable("password"); - _vm_name = getQueryVariable("title") || undefined; + + var info = getQueryVariable('info') || undefined; + var info_decode = UtilsConnection.decodeInfoConnection(info); + UtilsConnection.printInfoConnection($('.NOVNC_info'), info_decode) + + if (info_decode && info_decode.name) { + document.title = info_decode.name + } var rfbConfig = password? { "credentials": { "password": password } } : {}; diff --git a/src/sunstone/public/app/opennebula/service.js b/src/sunstone/public/app/opennebula/service.js index 515ba8e96b..bcef95bed2 100644 --- a/src/sunstone/public/app/opennebula/service.js +++ b/src/sunstone/public/app/opennebula/service.js @@ -147,11 +147,49 @@ define(function(require) { }, "STATES": STATES, "filterDoneServices": _filterDoneServices, + "getService": _getService, "getName": function(id){ return OpenNebulaAction.getName(id, CACHE_NAME); } } + function _promiseGetService({ id, success, async = true } = {}) { + return $.ajax({ + url: 'service/' + id, + type: 'GET', + success: success, + async: async + }); + } + + function _getServiceById({ services, id } = {}) { + return Array.isArray(services) && + services.find(function(service) { + return service && service[RESOURCE] && service[RESOURCE].ID === id + }); + } + + function _getService(id) { + if (!id) return undefined; + + var service = undefined; + var cache = OpenNebulaAction.cache(CACHE_NAME); + + if (cache && cache.data) { + service = _getServiceById({ services: cache.data, id: id }) + } + + if (!service || service.length === 0) { + _promiseGetService({ + id: id, + async: false, + success: function(res) { service = res } + }) + } + + return service ? service[RESOURCE] : undefined + } + function _filterDoneServices(services) { return $.grep(services, function(service) { return service.DOCUMENT.TEMPLATE.BODY.state !== STATES.DONE; diff --git a/src/sunstone/public/app/tabs/vms-tab/dialogs/guac/html.hbs b/src/sunstone/public/app/tabs/vms-tab/dialogs/guac/html.hbs index 0abb201c12..8cfc11edc8 100644 --- a/src/sunstone/public/app/tabs/vms-tab/dialogs/guac/html.hbs +++ b/src/sunstone/public/app/tabs/vms-tab/dialogs/guac/html.hbs @@ -29,6 +29,9 @@ +