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 @@ +
+
+
diff --git a/src/sunstone/public/app/tabs/vms-tab/dialogs/spice/html.hbs b/src/sunstone/public/app/tabs/vms-tab/dialogs/spice/html.hbs index 06bfa24bc3..1b801300aa 100644 --- a/src/sunstone/public/app/tabs/vms-tab/dialogs/spice/html.hbs +++ b/src/sunstone/public/app/tabs/vms-tab/dialogs/spice/html.hbs @@ -19,19 +19,24 @@

{{tr "SPICE"}} - - + + + + +

+
+
+
-
+
-
diff --git a/src/sunstone/public/app/tabs/vms-tab/dialogs/vmrc/html.hbs b/src/sunstone/public/app/tabs/vms-tab/dialogs/vmrc/html.hbs index b788eb1ddc..506a79256c 100644 --- a/src/sunstone/public/app/tabs/vms-tab/dialogs/vmrc/html.hbs +++ b/src/sunstone/public/app/tabs/vms-tab/dialogs/vmrc/html.hbs @@ -20,18 +20,21 @@
- - + + -
+
+
+
diff --git a/src/sunstone/public/app/tabs/vms-tab/dialogs/vnc/html.hbs b/src/sunstone/public/app/tabs/vms-tab/dialogs/vnc/html.hbs index 8dd7e38da7..5de6da3d84 100644 --- a/src/sunstone/public/app/tabs/vms-tab/dialogs/vnc/html.hbs +++ b/src/sunstone/public/app/tabs/vms-tab/dialogs/vnc/html.hbs @@ -15,21 +15,26 @@ {{! -------------------------------------------------------------------------- }}
-
+
- - + + -
+
+
+
diff --git a/src/sunstone/public/app/utils/gclient.js b/src/sunstone/public/app/utils/gclient.js index 3620825e50..a4b0e1801a 100644 --- a/src/sunstone/public/app/utils/gclient.js +++ b/src/sunstone/public/app/utils/gclient.js @@ -22,6 +22,7 @@ define(function(require) { var Config = require("sunstone-config"); var Notifier = require("utils/notifier"); var Locale = require("utils/locale"); + var UtilsConnection = require("utils/info-connection/utils"); /** * CONSTRUCTOR @@ -74,7 +75,9 @@ define(function(require) { var tunnel = new Guacamole.WebSocketTunnel(wsprotocol + '//' + host + ':' + port + '/fireedge/guacamole') var guac = this._client = new Guacamole.Client(tunnel); - var vm_name = response.vm_name || ""; + + var info_decode = UtilsConnection.decodeInfoConnection(response.info); + UtilsConnection.printInfoConnection($('.guac_info'), info_decode) // Client display this._display = $("#guacamole-display"); @@ -97,19 +100,19 @@ define(function(require) { guac.onstatechange = function(state) { switch (state) { case 0: - setStatus("Client IDLE to: " + vm_name); + setStatus('Client IDLE'); setLoading(true); break; case 1: - setStatus("Client CONNECTING to: " + vm_name); + setStatus('Client CONNECTING'); setLoading(true); break; case 2: - setStatus("Client WAITING to: " + vm_name); + setStatus('Client WAITING'); setLoading(true); break; case 3: - setStatus("Client CONNECTED to: " + vm_name); + setStatus('Client CONNECTED'); setLoading(false); setTimeout(function() { rescale(that); @@ -117,15 +120,15 @@ define(function(require) { }, 100); break; case 4: - setStatus("Client DISCONNECTING to: " + vm_name); + setStatus('Client DISCONNECTING'); setLoading(true); break; case 5: - setStatus("Client DISCONNECTED to: " + vm_name); + setStatus('Client DISCONNECTED'); setLoading(false); break; default: - setStatus("Client ERROR to: " + vm_name); + setStatus('Client ERROR'); setLoading(false); break; } diff --git a/src/sunstone/public/app/utils/info-connection/info.hbs b/src/sunstone/public/app/utils/info-connection/info.hbs new file mode 100644 index 0000000000..b69aa51425 --- /dev/null +++ b/src/sunstone/public/app/utils/info-connection/info.hbs @@ -0,0 +1,83 @@ +{{! -------------------------------------------------------------------------- }} +{{! Copyright 2002-2021, 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. }} +{{! -------------------------------------------------------------------------- }} + +
+
+ {{!-- STATE --}} + + + {{!-- ID - NAME --}} + #{{ id }} - {{ name }} + + {{!-- SERVICE --}} + {{#if service }} + (Part of: {{ service.NAME }}) + {{/if}} +
+ + + {{!-- START TIME --}} +
+ Started on: {{ humanizeTime start_time }} +
+ + {{!-- NETWORKS --}} + {{#ifEquals networks.length 0 }} + {{!-- dont render when if 0 --}} + {{else}} +
+ {{#ifEquals networks.length 1 }} + + {{#each networks}} {{#if @first}} {{ this }} {{/if}} {{/each}} + + {{else}} + + {{/ifEquals}} +
+ {{/ifEquals}} +
\ No newline at end of file diff --git a/src/sunstone/public/app/utils/info-connection/utils.js b/src/sunstone/public/app/utils/info-connection/utils.js new file mode 100644 index 0000000000..48bbe5a5a0 --- /dev/null +++ b/src/sunstone/public/app/utils/info-connection/utils.js @@ -0,0 +1,57 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2021, 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(require) { + require('foundation') + + var OpenNebulaVM = require('opennebula/vm'); + var OpenNebulaService = require('opennebula/service'); + + var TemplateInfo = require('hbs!./info'); + + function printInfoConnection(context, info) { + context.empty() + info && context.append(TemplateInfo(info)) + $('.ips-dropdown').foundation() + } + + function decodeInfoConnection(info_encode) { + if (!info_encode) return undefined + + try { + var json = atob(info_encode) + var info = JSON.parse(json) + + // state class + var stateId = OpenNebulaVM.STATES[info.state] + var lcmStateId = OpenNebulaVM.LCM_STATES[info.state] + var stateClass = OpenNebulaVM.stateClass(stateId) || OpenNebulaVM.lcmStateClass(lcmStateId) + + // get service from cache or sync + var service = OpenNebulaService.getService(info.service_id) + + return $.extend(info, { stateClass, service }) + + } catch (err) { + console.log(err) + } + } + + return { + 'printInfoConnection': printInfoConnection, + 'decodeInfoConnection': decodeInfoConnection + } +}); diff --git a/src/sunstone/public/app/utils/locale.js b/src/sunstone/public/app/utils/locale.js index 6d215991a9..7638f7d83a 100644 --- a/src/sunstone/public/app/utils/locale.js +++ b/src/sunstone/public/app/utils/locale.js @@ -44,6 +44,8 @@ define(function(require) { tr("November"), tr("December")); function tr(str) { + if (typeof locale === 'undefined') return str; + var tmp = locale[str]; if (tmp == null || tmp == "") { tmp = str; diff --git a/src/sunstone/public/app/utils/spice.js b/src/sunstone/public/app/utils/spice.js index 5c36720ce4..3919ab4467 100644 --- a/src/sunstone/public/app/utils/spice.js +++ b/src/sunstone/public/app/utils/spice.js @@ -16,8 +16,9 @@ define(function(require) { require('spice-main'); + var Config = require('sunstone-config'); - var Notifier = require('utils/notifier'); + var UtilsConnection = require("utils/info-connection/utils"); var _lock = false; var _sc; @@ -42,14 +43,16 @@ define(function(require) { _lock = false; } - function spice_error(e) { + function spice_error() { disconnect(); } function disconnect() { - if (_sc) { - _sc.stop(); - } + try { + if (_sc) { + _sc.stop(); + } + } catch (e) {} } function agent_connected(sc) { @@ -60,26 +63,26 @@ define(function(require) { } function spiceCallback(response) { - var host, port, password, scheme = "ws://", uri, token, vm_name; - + var scheme = "ws://"; if (Config.vncWSS == "yes") { scheme = "wss://"; } - host = window.location.hostname; - port = Config.vncProxyPort; - password = response["password"]; - token = response["token"]; - vm_name = response["vm_name"]; + var host = window.location.hostname; + var port = Config.vncProxyPort; + var password = response["password"]; + var token = response["token"]; + + var info = response.info; + var info_decode = UtilsConnection.decodeInfoConnection(info); + UtilsConnection.printInfoConnection($('.SPICE_info'), info_decode) if ((!host) || (!port)) { console.log("must specify host and port in URL"); return; } - if (_sc) { - _sc.stop(); - } + disconnect() uri = scheme + host + ":" + port + "?token=" + token; @@ -88,7 +91,7 @@ define(function(require) { message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected}); } catch (e) { - spice_error(e) + disconnect() } var url = "spice?"; @@ -97,7 +100,7 @@ define(function(require) { url += "&token=" + token; url += "&password=" + password; url += "&encrypt=" + config['user_config']['vnc_wss']; - url += "&title=" + vm_name; + url += "&info=" + info; $("#open_in_a_new_window_spice").attr('href', url); } diff --git a/src/sunstone/public/app/utils/vmrc.js b/src/sunstone/public/app/utils/vmrc.js index 7d62eb0ee0..5abd1a27d0 100644 --- a/src/sunstone/public/app/utils/vmrc.js +++ b/src/sunstone/public/app/utils/vmrc.js @@ -17,6 +17,8 @@ define(function (require) { var WMKS = require("wmks"); var Config = require("sunstone-config"); + var UtilsConnection = require("utils/info-connection/utils"); + var _lock = false; var _wmks; var _is_encrypted = ""; @@ -59,19 +61,7 @@ define(function (require) { } } - function desktopNameChange(e) { - if (e.detail.name) { - setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " /*+ e.detail.name*/); - } - } - - function credentialsRequired(e) { - setStatus("Something went wrong, more credentials must be given to continue", "Failed"); - } - function render(ticket, host_vmrc, port_vmrc, response){ - var URL = ""; - var hostname = window.location.hostname; var port = window.location.port; var protocol = window.location.protocol; @@ -79,14 +69,19 @@ define(function (require) { var fireedge_host = fireedge_endpoint.split(":")[0]; var fireedge_port = fireedge_endpoint.split(":")[1]; + var info = response.info; + var info_decode = UtilsConnection.decodeInfoConnection(info); + UtilsConnection.printInfoConnection($('.VMRC_info'), info_decode) + const queryString = window.location.search; const urlParams = new URLSearchParams(queryString); // Content of response.data - var ticket = ticket ? ticket : urlParams.get('ticket'); - var host_vmrc = host_vmrc ? host_vmrc : urlParams.get('host'); - var port_vmrc = port_vmrc ? port_vmrc : urlParams.get('port'); + var ticket = ticket || urlParams.get('ticket'); + var host_vmrc = host_vmrc || urlParams.get('host'); + var port_vmrc = port_vmrc || urlParams.get('port'); + var URL = ""; if (protocol === "https:") { URL = "wss"; _is_encrypted ="encrypted"; @@ -97,20 +92,21 @@ define(function (require) { URL += "://" + fireedge_endpoint + "/"; - var re = new RegExp("^(ws|wss):\\/\\/[\\w\\D]*?\\/", "gi"); - var link = URL.replace(re, protocol + "//" + hostname + ":" + port + "fireedge/vmrc?"); + var regex = new RegExp("^(ws|wss):\\/\\/[\\w\\D]*?\\/", "gi"); + var link = URL.replace(regex, protocol + "//" + hostname + ":" + port + "fireedge/vmrc?"); URL += "fireedge/vmrc/" + ticket; link += "host=" + fireedge_host; link += "&port=" + fireedge_port; link += "&ticket=" + ticket; + link += "&info=" + info; try { - _wmks = WMKS.createWMKS("wmksContainer", {}) + _wmks = WMKS.createWMKS('wmksContainer', {}) .register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE, - function (event, data) { - if (typeof cons !== 'undefined' && data.state == cons.ConnectionState.CONNECTED) { - console.log("connection state change : connected"); + function (_, data) { + if (data.state === WMKS.CONST.ConnectionState.CONNECTED) { + console.log("connection state change: connected"); } } ); @@ -119,9 +115,9 @@ define(function (require) { _wmks.eventHandlers["disconnect"] = disconnectedFromServer; _wmks.connect(URL); - _wmks["vm_name"] = response.vm_name ? response.vm_name:""; - link += "&name=" + _wmks["vm_name"]; + $("#VMRC_buttons #open_in_a_new_window").attr("href",link); + } catch (err) { setStatus("Something went wrong, connection is closed", "Failed"); console.log("error start VMRC ", err); @@ -129,14 +125,13 @@ define(function (require) { } function vmrcCallback(response) { - if (response.data){ - - render(response.data.ticket, + if (response.data) { + render( + response.data.ticket, response.data.host, response.data.port, - response); - - + response + ); } } diff --git a/src/sunstone/public/app/utils/vnc.js b/src/sunstone/public/app/utils/vnc.js index 14d398857a..888b35851e 100644 --- a/src/sunstone/public/app/utils/vnc.js +++ b/src/sunstone/public/app/utils/vnc.js @@ -15,14 +15,15 @@ /* -------------------------------------------------------------------------- */ 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 _lock = false; var _rfb; var _message = ""; var _status = "Loading"; var _is_encrypted = ""; - var _vm_name; return { "lockStatus": lockStatus, @@ -51,14 +52,14 @@ define(function(require) { $(".NOVNC_message").text(_message); $("#VNC_status").text(_status); } - + 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"); } @@ -76,15 +77,20 @@ define(function(require) { function vncCallback(response) { var URL = ""; - var proxy_host = window.location.hostname; var proxy_port = Config.vncProxyPort; - var pw = response["password"]; - var token = response["token"]; - var vm_name = _vm_name = response["vm_name"]; + + var pw = response.password; + var token = response.token; + + var info_decode = UtilsConnection.decodeInfoConnection(response.info); + UtilsConnection.printInfoConnection($('.NOVNC_info'), info_decode) + + var proxy_host = window.location.hostname; var protocol = window.location.protocol; var hostname = window.location.hostname; var port = window.location.port; - var rfbConfig = pw? { "credentials": { "password": pw } } : {}; + + var rfbConfig = pw ? { "credentials": { "password": pw } } : {}; if (protocol === "https:") { URL = "wss"; @@ -99,7 +105,7 @@ define(function(require) { URL += "&port=" + proxy_port; URL += "&token=" + token; URL += "&encrypt=" + Config.vncWSS; - URL += "&title=" + vm_name; + URL += "&info=" + response.info; if (!Config.requestVNCPassword) { URL += "&password=" + pw; diff --git a/src/sunstone/public/css/novnc-custom.css b/src/sunstone/public/css/novnc-custom.css index bf4ebff61d..3cf9489bc7 100644 --- a/src/sunstone/public/css/novnc-custom.css +++ b/src/sunstone/public/css/novnc-custom.css @@ -36,8 +36,6 @@ html { #noVNC_password { width: 150px; } -#noVNC_encrypt { -} #noVNC_path { width: 100px; } @@ -85,11 +83,7 @@ html { } #noVNC_status { - font-size: 12px; - padding-top: 4px; - height:32px; text-align: center; - font-weight: bold; } #noVNC_settings_menu { @@ -114,7 +108,6 @@ html { height: 36px; } #noVNC_screen { - text-align: center; width: 100%; display: inline-flex; height: 100%; @@ -395,10 +388,8 @@ html { } #noVNC_status { - z-index: 0; position: absolute; width: 100%; - margin-left: 0px; } #showExtraKeysButton { display: none; } @@ -414,7 +405,6 @@ html { position: relative; width: auto; float: left; - margin-left: 4px; } } diff --git a/src/sunstone/public/css/spice-custom.css b/src/sunstone/public/css/spice-custom.css index 2148ba74d8..f5c8717ab0 100644 --- a/src/sunstone/public/css/spice-custom.css +++ b/src/sunstone/public/css/spice-custom.css @@ -8,13 +8,18 @@ body * { margin: 0; } -#spice-area -{ +main { height: 100%; - padding: 0; - margin-left: auto; - margin-right: auto; + display: grid; + grid-template-rows: auto 1fr; } + +#spice-area { + display: flex; + background-color: rgb(40, 40, 40); + align-items: center; +} + .spice-screen { min-height: 600px; diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 274ed3f606..dbbf6e9c6f 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -258,13 +258,13 @@ if $conf[:webauthn_avail] end #start VNC proxy -$vnc = OpenNebulaVNC.new($conf, logger) +$vnc = SunstoneVNC.new($conf, logger) #init Guacamole server -$guac = OpenNebulaGuac.new(logger) +$guac = SunstoneGuac.new(logger) #init VMRC server -$vmrc = OpenNebulaVMRC.new(logger) +$vmrc = SunstoneVMRC.new(logger) configure do set :run, false @@ -709,10 +709,10 @@ get '/vnc' do erb :login else erb :vnc, :locals => { - :logos_conf => $conf[:locals][:logos_conf], - :oned_conf => $conf[:locals][:oned_conf], - :support => $conf[:locals][:support], - :upgrade => $conf[:locals][:upgrade] + :logos_conf => $conf[:locals][:logos_conf], + :oned_conf => $conf[:locals][:oned_conf], + :support => $conf[:locals][:support], + :upgrade => $conf[:locals][:upgrade] } end end @@ -723,10 +723,10 @@ get '/vmrc' do erb :login else erb :vmrc, :locals => { - :logos_conf => $conf[:locals][:logos_conf], - :oned_conf => $conf[:locals][:oned_conf], - :support => $conf[:locals][:support], - :upgrade => $conf[:locals][:upgrade] + :logos_conf => $conf[:locals][:logos_conf], + :oned_conf => $conf[:locals][:oned_conf], + :support => $conf[:locals][:support], + :upgrade => $conf[:locals][:upgrade] } end end @@ -736,8 +736,12 @@ get '/spice' do if !authorized? erb :login else - params[:title] = CGI::escape(params[:title]) - erb :spice + erb :spice, :locals => { + :logos_conf => $conf[:locals][:logos_conf], + :oned_conf => $conf[:locals][:oned_conf], + :support => $conf[:locals][:support], + :upgrade => $conf[:locals][:upgrade] + } end end diff --git a/src/sunstone/views/spice.erb b/src/sunstone/views/spice.erb index 825a07680d..60f0f1bb1e 100644 --- a/src/sunstone/views/spice.erb +++ b/src/sunstone/views/spice.erb @@ -26,36 +26,81 @@ - + + + + + - <%= params['title']%> - - - - - - - -
-
- - - -
- -
-
+ +
+
+
+
+ +
+
+
+
-
-
-
+
+
+
- +
+ +
+
-
- -
- - + + <% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %> + + diff --git a/src/sunstone/views/vnc.erb b/src/sunstone/views/vnc.erb index 59d511a689..b2345eba2e 100644 --- a/src/sunstone/views/vnc.erb +++ b/src/sunstone/views/vnc.erb @@ -1,97 +1,89 @@ - - - <%= params['vm_name']%> - - - - - - - - - - <% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %> - - - - -
-
-
- - - - - - -
- -
Loading
-
-
- - - - - -
-
+ + + + + + + + + + + + + + +
+
+
+
+ +
Loading
+
+ + + + + +
-
-
-
+
- - \ No newline at end of file +
+
+
+
+
+ + + <% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %> + + + \ No newline at end of file