mirror of
https://github.com/OpenNebula/one.git
synced 2025-02-28 17:57:22 +03:00
parent
e61277c20e
commit
381e86fcdc
@ -2520,6 +2520,7 @@ SUNSTONE_MODELS_FILES="src/sunstone/models/OpenNebulaJSON.rb \
|
||||
src/sunstone/models/SunstoneServer.rb \
|
||||
src/sunstone/models/SunstoneViews.rb \
|
||||
src/sunstone/models/sunstone_vm_helper.rb \
|
||||
src/sunstone/models/sunstone_remotes.rb \
|
||||
src/sunstone/models/sunstone_vnc.rb \
|
||||
src/sunstone/models/sunstone_guac.rb \
|
||||
src/sunstone/models/sunstone_vmrc.rb \
|
||||
@ -2554,6 +2555,7 @@ SUNSTONE_VIEWS_FILES="src/sunstone/views/index.erb \
|
||||
src/sunstone/views/vnc.erb \
|
||||
src/sunstone/views/vmrc.erb \
|
||||
src/sunstone/views/spice.erb \
|
||||
src/sunstone/views/guac.erb \
|
||||
src/sunstone/views/_login_standard.erb \
|
||||
src/sunstone/views/_login_x509.erb"
|
||||
|
||||
@ -2567,6 +2569,8 @@ SUNSTONE_PUBLIC_JS_CONSOLE_FILES="src/sunstone/public/dist/console/vnc.js \
|
||||
src/sunstone/public/dist/console/vnc.js.map \
|
||||
src/sunstone/public/dist/console/spice.js \
|
||||
src/sunstone/public/dist/console/spice.js.map \
|
||||
src/sunstone/public/dist/console/guacamole.js \
|
||||
src/sunstone/public/dist/console/guacamole.js.map \
|
||||
src/sunstone/public/dist/console/vmrc.js \
|
||||
src/sunstone/public/dist/console/vmrc.js.map"
|
||||
|
||||
@ -2579,7 +2583,9 @@ SUNSTONE_ROUTES_FILES="src/sunstone/routes/oneflow.rb \
|
||||
|
||||
SUNSTONE_PUBLIC_CSS_FILES="src/sunstone/public/css/app.min.css \
|
||||
src/sunstone/public/css/opensans/opensans.woff \
|
||||
src/sunstone/public/css/vmrc-custom.css \
|
||||
src/sunstone/public/css/novnc-custom.css \
|
||||
src/sunstone/public/css/guac-custom.css \
|
||||
src/sunstone/public/css/spice-custom.css"
|
||||
|
||||
SUNSTONE_PUBLIC_FONT_AWSOME="src/sunstone/public/bower_components/fontawesome/web-fonts-with-css/webfonts/fa-brands-400.eot \
|
||||
|
@ -43,10 +43,12 @@ const vmrcProxy = createProxyMiddleware(endpointVmrc, {
|
||||
// eslint-disable-next-line consistent-return
|
||||
router: req => {
|
||||
if (req && req.url) {
|
||||
const ticket = req.url.split('/')[3] || ''
|
||||
// Needs to be reviewed require('path')
|
||||
const ticket = req.url.split('/')[3]
|
||||
const filterTicket = ticket.split('?')[0]
|
||||
try {
|
||||
const esxi = readFileSync(
|
||||
`${global.VMRC_TOKENS || ''}/${ticket}`
|
||||
`${global.VMRC_TOKENS || ''}/${filterTicket}`
|
||||
).toString()
|
||||
return esxi
|
||||
} catch (error) {
|
||||
|
@ -76,9 +76,9 @@ if ARGV[0]
|
||||
when :start
|
||||
vnc.start
|
||||
when :stop
|
||||
vnc.stop(true)
|
||||
vnc.stop
|
||||
when :restart
|
||||
vnc.stop(true)
|
||||
vnc.stop
|
||||
sleep 1
|
||||
vnc.start
|
||||
when :status
|
||||
@ -92,4 +92,3 @@ if ARGV[0]
|
||||
else
|
||||
exit(-1)
|
||||
end
|
||||
|
||||
|
@ -23,6 +23,7 @@ require 'json'
|
||||
require 'opennebula'
|
||||
require 'base64'
|
||||
require 'openssl'
|
||||
require 'sunstone_remotes'
|
||||
|
||||
if !ONE_LOCATION
|
||||
VAR_LOCATION = '/var/lib/one/'
|
||||
@ -32,93 +33,18 @@ end
|
||||
|
||||
FIREEDGE_KEY = VAR_LOCATION + '/.one/fireedge_key'
|
||||
|
||||
GUAC_STATES = [
|
||||
# 0, # LCM_INIT
|
||||
# 1, # PROLOG
|
||||
# 2, # BOOT
|
||||
'3', # RUNNING
|
||||
'4', # MIGRATE
|
||||
# 5, # SAVE_STOP
|
||||
# 6, # SAVE_SUSPEND
|
||||
# 7, # SAVE_MIGRATE
|
||||
# 8, # PROLOG_MIGRATE
|
||||
# 9, # PROLOG_RESUME
|
||||
# 10, # EPILOG_STOP
|
||||
# 11, # EPILOG
|
||||
'12', # SHUTDOWN
|
||||
'13', # CANCEL
|
||||
# 14, # FAILURE
|
||||
# 15, # CLEANUP_RESUBMIT
|
||||
'16', # UNKNOWN
|
||||
'17', # HOTPLUG
|
||||
'18', # SHUTDOWN_POWEROFF
|
||||
# 19, # BOOT_UNKNOWN
|
||||
# 20, # BOOT_POWEROFF
|
||||
# 21, # BOOT_SUSPENDED
|
||||
# 22, # BOOT_STOPPED
|
||||
# 23, # CLEANUP_DELETE
|
||||
'24', # HOTPLUG_SNAPSHOT
|
||||
'25', # HOTPLUG_NIC
|
||||
'26', # HOTPLUG_SAVEAS
|
||||
'27', # HOTPLUG_SAVEAS_POWEROFF
|
||||
'28', # HOTPLUG_SAVEAS_SUSPENDED
|
||||
'29', # SHUTDOWN_UNDEPLOY
|
||||
# 30, # EPILOG_UNDEPLOY
|
||||
# 31, # PROLOG_UNDEPLOY
|
||||
# 32, # BOOT_UNDEPLOY
|
||||
# 33, # HOTPLUG_PROLOG_POWEROFF
|
||||
# 34, # HOTPLUG_EPILOG_POWEROFF
|
||||
# 35, # BOOT_MIGRATE
|
||||
# 36, # BOOT_FAILURE
|
||||
# 37, # BOOT_MIGRATE_FAILURE
|
||||
# 38, # PROLOG_MIGRATE_FAILURE
|
||||
# 39, # PROLOG_FAILURE
|
||||
# 40, # EPILOG_FAILURE
|
||||
# 41, # EPILOG_STOP_FAILURE
|
||||
# 42, # EPILOG_UNDEPLOY_FAILURE
|
||||
# 43, # PROLOG_MIGRATE_POWEROFF
|
||||
# 44, # PROLOG_MIGRATE_POWEROFF_FAILURE
|
||||
# 45, # PROLOG_MIGRATE_SUSPEND
|
||||
# 46, # PROLOG_MIGRATE_SUSPEND_FAILURE
|
||||
# 47, # BOOT_UNDEPLOY_FAILURE
|
||||
# 48, # BOOT_STOPPED_FAILURE
|
||||
# 49, # PROLOG_RESUME_FAILURE
|
||||
# 50, # PROLOG_UNDEPLOY_FAILURE
|
||||
# 51, # DISK_SNAPSHOT_POWEROFF
|
||||
# 52, # DISK_SNAPSHOT_REVERT_POWEROFF
|
||||
# 53, # DISK_SNAPSHOT_DELETE_POWEROFF
|
||||
# 54, # DISK_SNAPSHOT_SUSPENDED
|
||||
# 55, # DISK_SNAPSHOT_REVERT_SUSPENDED
|
||||
# 56, # DISK_SNAPSHOT_DELETE_SUSPENDED
|
||||
'57', # DISK_SNAPSHOT
|
||||
'58', # DISK_SNAPSHOT_REVERT
|
||||
# 59, # DISK_SNAPSHOT_DELETE
|
||||
# 60, # PROLOG_MIGRATE_UNKNOWN
|
||||
# 61, # PROLOG_MIGRATE_UNKNOWN_FAILURE
|
||||
'62' # DISK_RESIZE
|
||||
# 63, # DISK_RESIZE_POWEROFF
|
||||
# 64, # DISK_RESIZE_UNDEPLOYED
|
||||
# 65, #HOTPLUG_NIC_POWEROFF
|
||||
# 66, # HOTPLUG_RESIZE
|
||||
# 67, # HOTPLUG_SAVEAS_UNDEPLOYED
|
||||
# 68, # HOTPLUG_SAVEAS_STOPPED
|
||||
]
|
||||
|
||||
# Class for Guacamole connection configuration
|
||||
class SunstoneGuac
|
||||
class SunstoneGuac < SunstoneRemoteConnections
|
||||
|
||||
attr_reader :proxy_port
|
||||
|
||||
def initialize(logger, options = {})
|
||||
opts={ :json_errors => true }.merge(options)
|
||||
|
||||
@options = opts
|
||||
@logger = logger
|
||||
super
|
||||
end
|
||||
|
||||
def proxy(vm_resource, type_connection = 'vnc')
|
||||
# Check configurations and VM attributes
|
||||
if !GUAC_STATES.include?(vm_resource['LCM_STATE'])
|
||||
if !allowed_console_states.include?(vm_resource['LCM_STATE'])
|
||||
error_message = "Wrong state (#{vm_resource['LCM_STATE']})
|
||||
to open a Guacamole session"
|
||||
return error(400, error_message)
|
||||
@ -175,13 +101,6 @@ class SunstoneGuac
|
||||
|
||||
private
|
||||
|
||||
def error(code, msg)
|
||||
@logger.error(msg)
|
||||
return [code, msg] unless @options[:json_error]
|
||||
|
||||
[code, OpenNebula::Error.new(msg).to_json]
|
||||
end
|
||||
|
||||
def get_config_vnc(vm_resource)
|
||||
# If it is a vCenter VM
|
||||
if vm_resource['USER_TEMPLATE/HYPERVISOR'] == 'vcenter'
|
||||
@ -194,7 +113,8 @@ class SunstoneGuac
|
||||
return error(400, error_message)
|
||||
end
|
||||
else
|
||||
hostname = vm_resource['/VM/HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']
|
||||
hostname =
|
||||
vm_resource['/VM/HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']
|
||||
end
|
||||
|
||||
{
|
||||
@ -211,10 +131,11 @@ class SunstoneGuac
|
||||
end
|
||||
|
||||
def get_config_rdp(vm_resource)
|
||||
hostname = vm_resource["TEMPLATE/NIC[RDP='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC[RDP='YES'][1]/IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[RDP='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[RDP='YES'][1]/IP"]
|
||||
hostname =
|
||||
vm_resource["TEMPLATE/NIC[RDP='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC[RDP='YES'][1]/IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[RDP='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[RDP='YES'][1]/IP"]
|
||||
|
||||
if hostname.nil?
|
||||
error_message = 'Wrong configuration. Cannot find a NIC with RDP'
|
||||
@ -237,10 +158,11 @@ class SunstoneGuac
|
||||
end
|
||||
|
||||
def get_config_ssh(vm_resource)
|
||||
hostname = vm_resource["TEMPLATE/NIC[SSH='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC[SSH='YES'][1]/IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[SSH='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[SSH='YES'][1]/IP"]
|
||||
hostname =
|
||||
vm_resource["TEMPLATE/NIC[SSH='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC[SSH='YES'][1]/IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[SSH='YES'][1]/EXTERNAL_IP"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[SSH='YES'][1]/IP"]
|
||||
|
||||
if hostname.nil?
|
||||
error_message = 'Wrong configuration. Cannot find a NIC with SSH'
|
||||
|
123
src/sunstone/models/sunstone_remotes.rb
Normal file
123
src/sunstone/models/sunstone_remotes.rb
Normal file
@ -0,0 +1,123 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
require 'rubygems'
|
||||
require 'json'
|
||||
require 'opennebula'
|
||||
|
||||
# The following states are the ones where
|
||||
# the remote connections are allowed.
|
||||
ALLOWED_CONSOLE_STATES = [
|
||||
# '0', # LCM_INIT
|
||||
# '1', # PROLOG
|
||||
# '2', # BOOT
|
||||
'3', # RUNNING
|
||||
'4', # MIGRATE
|
||||
# '5', # SAVE_STOP
|
||||
# '6', # SAVE_SUSPEND
|
||||
# '7', # SAVE_MIGRATE
|
||||
# '8', # PROLOG_MIGRATE
|
||||
# '9', # PROLOG_RESUME
|
||||
# '10', # EPILOG_STOP
|
||||
# '11', # EPILOG
|
||||
'12', # SHUTDOWN
|
||||
'13', # CANCEL
|
||||
# '14', # FAILURE
|
||||
# '15', # CLEANUP_RESUBMIT
|
||||
'16', # UNKNOWN
|
||||
'17', # HOTPLUG
|
||||
'18', # SHUTDOWN_POWEROFF
|
||||
# '19', # BOOT_UNKNOWN
|
||||
# '20', # BOOT_POWEROFF
|
||||
# '21', # BOOT_SUSPENDED
|
||||
# '22', # BOOT_STOPPED
|
||||
# '23', # CLEANUP_DELETE
|
||||
'24', # HOTPLUG_SNAPSHOT
|
||||
'25', # HOTPLUG_NIC
|
||||
'26', # HOTPLUG_SAVEAS
|
||||
'27', # HOTPLUG_SAVEAS_POWEROFF
|
||||
'28', # HOTPLUG_SAVEAS_SUSPENDED
|
||||
'29', # SHUTDOWN_UNDEPLOY
|
||||
# '30', # EPILOG_UNDEPLOY
|
||||
# '31', # PROLOG_UNDEPLOY
|
||||
# '32', # BOOT_UNDEPLOY
|
||||
# '33', # HOTPLUG_PROLOG_POWEROFF
|
||||
# '34', # HOTPLUG_EPILOG_POWEROFF
|
||||
# '35', # BOOT_MIGRATE
|
||||
# '36', # BOOT_FAILURE
|
||||
# '37', # BOOT_MIGRATE_FAILURE
|
||||
# '38', # PROLOG_MIGRATE_FAILURE
|
||||
# '39', # PROLOG_FAILURE
|
||||
# '40', # EPILOG_FAILURE
|
||||
# '41', # EPILOG_STOP_FAILURE
|
||||
# '42', # EPILOG_UNDEPLOY_FAILURE
|
||||
# '43', # PROLOG_MIGRATE_POWEROFF
|
||||
# '44', # PROLOG_MIGRATE_POWEROFF_FAILURE
|
||||
# '45', # PROLOG_MIGRATE_SUSPEND
|
||||
# '46', # PROLOG_MIGRATE_SUSPEND_FAILURE
|
||||
# '47', # BOOT_UNDEPLOY_FAILURE
|
||||
# '48', # BOOT_STOPPED_FAILURE
|
||||
# '49', # PROLOG_RESUME_FAILURE
|
||||
# '50', # PROLOG_UNDEPLOY_FAILURE
|
||||
# '51', # DISK_SNAPSHOT_POWEROFF
|
||||
# '52', # DISK_SNAPSHOT_REVERT_POWEROFF
|
||||
# '53', # DISK_SNAPSHOT_DELETE_POWEROFF
|
||||
# '54', # DISK_SNAPSHOT_SUSPENDED
|
||||
# '55', # DISK_SNAPSHOT_REVERT_SUSPENDED
|
||||
# '56', # DISK_SNAPSHOT_DELETE_SUSPENDED
|
||||
'57', # DISK_SNAPSHOT
|
||||
'58', # DISK_SNAPSHOT_REVERT
|
||||
# '59', # DISK_SNAPSHOT_DELETE
|
||||
# '60', # PROLOG_MIGRATE_UNKNOWN
|
||||
# '61', # PROLOG_MIGRATE_UNKNOWN_FAILURE
|
||||
'62' # DISK_RESIZE
|
||||
# '63', # DISK_RESIZE_POWEROFF
|
||||
# '64', # DISK_RESIZE_UNDEPLOYED
|
||||
# '65', # HOTPLUG_NIC_POWEROFF
|
||||
# '66', # HOTPLUG_RESIZE
|
||||
# '67', # HOTPLUG_SAVEAS_UNDEPLOYED
|
||||
# '68', # HOTPLUG_SAVEAS_STOPPED
|
||||
]
|
||||
|
||||
# This class provides an abstracion with the common code
|
||||
# inside the remote connections classes.
|
||||
class SunstoneRemoteConnections
|
||||
|
||||
attr_accessor :options, :logger
|
||||
|
||||
def initialize(logger, options = {})
|
||||
opts = {
|
||||
:json_errors => true
|
||||
}.merge(options)
|
||||
|
||||
@options = opts
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def allowed_console_states
|
||||
ALLOWED_CONSOLE_STATES
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def error(code, msg)
|
||||
@logger.error(msg)
|
||||
return [code, msg] unless @options[:json_error]
|
||||
|
||||
[code, OpenNebula::Error.new(msg).to_json]
|
||||
end
|
||||
|
||||
end
|
@ -25,6 +25,7 @@ require 'base64'
|
||||
require 'openssl'
|
||||
require 'vcenter_driver'
|
||||
require 'fileutils'
|
||||
require 'sunstone_remotes'
|
||||
|
||||
if !ONE_LOCATION
|
||||
VMRC_TICKETS = '/var/lib/one/sunstone_vmrc_tokens/'
|
||||
@ -34,93 +35,18 @@ end
|
||||
|
||||
FileUtils.mkdir_p VMRC_TICKETS
|
||||
|
||||
VMRC_STATES = [
|
||||
# 0, # LCM_INIT
|
||||
# 1, # PROLOG
|
||||
# 2, # BOOT
|
||||
'3', # RUNNING
|
||||
'4', # MIGRATE
|
||||
# 5, # SAVE_STOP
|
||||
# 6, # SAVE_SUSPEND
|
||||
# 7, # SAVE_MIGRATE
|
||||
# 8, # PROLOG_MIGRATE
|
||||
# 9, # PROLOG_RESUME
|
||||
# 10, # EPILOG_STOP
|
||||
# 11, # EPILOG
|
||||
'12', # SHUTDOWN
|
||||
'13', # CANCEL
|
||||
# 14, # FAILURE
|
||||
# 15, # CLEANUP_RESUBMIT
|
||||
'16', # UNKNOWN
|
||||
'17', # HOTPLUG
|
||||
'18', # SHUTDOWN_POWEROFF
|
||||
# 19, # BOOT_UNKNOWN
|
||||
# 20, # BOOT_POWEROFF
|
||||
# 21, # BOOT_SUSPENDED
|
||||
# 22, # BOOT_STOPPED
|
||||
# 23, # CLEANUP_DELETE
|
||||
'24', # HOTPLUG_SNAPSHOT
|
||||
'25', # HOTPLUG_NIC
|
||||
'26', # HOTPLUG_SAVEAS
|
||||
'27', # HOTPLUG_SAVEAS_POWEROFF
|
||||
'28', # HOTPLUG_SAVEAS_SUSPENDED
|
||||
'29', # SHUTDOWN_UNDEPLOY
|
||||
# 30, # EPILOG_UNDEPLOY
|
||||
# 31, # PROLOG_UNDEPLOY
|
||||
# 32, # BOOT_UNDEPLOY
|
||||
# 33, # HOTPLUG_PROLOG_POWEROFF
|
||||
# 34, # HOTPLUG_EPILOG_POWEROFF
|
||||
# 35, # BOOT_MIGRATE
|
||||
# 36, # BOOT_FAILURE
|
||||
# 37, # BOOT_MIGRATE_FAILURE
|
||||
# 38, # PROLOG_MIGRATE_FAILURE
|
||||
# 39, # PROLOG_FAILURE
|
||||
# 40, # EPILOG_FAILURE
|
||||
# 41, # EPILOG_STOP_FAILURE
|
||||
# 42, # EPILOG_UNDEPLOY_FAILURE
|
||||
# 43, # PROLOG_MIGRATE_POWEROFF
|
||||
# 44, # PROLOG_MIGRATE_POWEROFF_FAILURE
|
||||
# 45, # PROLOG_MIGRATE_SUSPEND
|
||||
# 46, # PROLOG_MIGRATE_SUSPEND_FAILURE
|
||||
# 47, # BOOT_UNDEPLOY_FAILURE
|
||||
# 48, # BOOT_STOPPED_FAILURE
|
||||
# 49, # PROLOG_RESUME_FAILURE
|
||||
# 50, # PROLOG_UNDEPLOY_FAILURE
|
||||
# 51, # DISK_SNAPSHOT_POWEROFF
|
||||
# 52, # DISK_SNAPSHOT_REVERT_POWEROFF
|
||||
# 53, # DISK_SNAPSHOT_DELETE_POWEROFF
|
||||
# 54, # DISK_SNAPSHOT_SUSPENDED
|
||||
# 55, # DISK_SNAPSHOT_REVERT_SUSPENDED
|
||||
# 56, # DISK_SNAPSHOT_DELETE_SUSPENDED
|
||||
'57', # DISK_SNAPSHOT
|
||||
'58', # DISK_SNAPSHOT_REVERT
|
||||
# 59, # DISK_SNAPSHOT_DELETE
|
||||
# 60, # PROLOG_MIGRATE_UNKNOWN
|
||||
# 61, # PROLOG_MIGRATE_UNKNOWN_FAILURE
|
||||
'62' # DISK_RESIZE
|
||||
# 63, # DISK_RESIZE_POWEROFF
|
||||
# 64, # DISK_RESIZE_UNDEPLOYED
|
||||
# 65, # HOTPLUG_NIC_POWEROFF
|
||||
# 66, # HOTPLUG_RESIZE
|
||||
# 67, # HOTPLUG_SAVEAS_UNDEPLOYED
|
||||
# 68, # HOTPLUG_SAVEAS_STOPPED
|
||||
]
|
||||
|
||||
# Class for necessary VMRC ticket creation
|
||||
class SunstoneVMRC
|
||||
class SunstoneVMRC < SunstoneRemoteConnections
|
||||
|
||||
attr_reader :proxy_port
|
||||
|
||||
def initialize(logger, options = {})
|
||||
opts={ :json_errors => true }.merge(options)
|
||||
|
||||
@options = opts
|
||||
@logger = logger
|
||||
super
|
||||
end
|
||||
|
||||
def proxy(vm_resource, client = nil)
|
||||
# Check configurations and VM attributes
|
||||
unless VMRC_STATES.include?(vm_resource['LCM_STATE'])
|
||||
unless allowed_console_states.include?(vm_resource['LCM_STATE'])
|
||||
error_message = "Wrong state (#{vm_resource['LCM_STATE']}) to
|
||||
open a VMRC session"
|
||||
return error(400, error_message)
|
||||
@ -158,7 +84,11 @@ class SunstoneVMRC
|
||||
:ticket => parameters[:ticket]
|
||||
}
|
||||
|
||||
file = File.open(VMRC_TICKETS + VCenterDriver::FileHelper.sanitize(data[:ticket]), 'w')
|
||||
file = File.open(
|
||||
VMRC_TICKETS +
|
||||
VCenterDriver::FileHelper.sanitize(data[:ticket]),
|
||||
'w'
|
||||
)
|
||||
file.write('https://' + data[:host] + ':' + data[:port].to_s)
|
||||
file.close
|
||||
|
||||
@ -168,14 +98,4 @@ class SunstoneVMRC
|
||||
[200, { :data => data, :info => encode_info }.to_json]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def error(code, msg)
|
||||
if @options[:json_errors]
|
||||
[code, OpenNebula::Error.new(msg).to_json]
|
||||
else
|
||||
[code, msg]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -14,106 +14,40 @@
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
#
|
||||
# This class provides support for launching and stopping a websockify proxy
|
||||
#
|
||||
|
||||
require 'rubygems'
|
||||
require 'json'
|
||||
require 'opennebula'
|
||||
|
||||
require 'sunstone_remotes'
|
||||
|
||||
if !ONE_LOCATION
|
||||
NOVNC_LOCK_FILE = "/var/lock/one/.novnc.lock"
|
||||
NOVNC_LOCK_FILE = '/var/lock/one/.novnc.lock'
|
||||
else
|
||||
NOVNC_LOCK_FILE= ONE_LOCATION + "/var/.novnc.lock"
|
||||
NOVNC_LOCK_FILE= ONE_LOCATION + '/var/.novnc.lock'
|
||||
end
|
||||
|
||||
TOKEN_EXPIRE_SECONDS = 4
|
||||
|
||||
VNC_STATES = [
|
||||
#0, #LCM_INIT
|
||||
#1, #PROLOG
|
||||
#2, #BOOT
|
||||
"3", #RUNNING
|
||||
"4", #MIGRATE
|
||||
#5, #SAVE_STOP
|
||||
#6, #SAVE_SUSPEND
|
||||
#7, #SAVE_MIGRATE
|
||||
#8, #PROLOG_MIGRATE
|
||||
#9, #PROLOG_RESUME
|
||||
#10, #EPILOG_STOP
|
||||
#11, #EPILOG
|
||||
"12", #SHUTDOWN
|
||||
"13", #CANCEL
|
||||
#14, #FAILURE
|
||||
#15, #CLEANUP_RESUBMIT
|
||||
"16", #UNKNOWN
|
||||
"17", #HOTPLUG
|
||||
"18", #SHUTDOWN_POWEROFF
|
||||
#19, #BOOT_UNKNOWN
|
||||
#20, #BOOT_POWEROFF
|
||||
#21, #BOOT_SUSPENDED
|
||||
#22, #BOOT_STOPPED
|
||||
#23, #CLEANUP_DELETE
|
||||
"24", #HOTPLUG_SNAPSHOT
|
||||
"25", #HOTPLUG_NIC
|
||||
"26", #HOTPLUG_SAVEAS
|
||||
"27", #HOTPLUG_SAVEAS_POWEROFF
|
||||
"28", #HOTPLUG_SAVEAS_SUSPENDED
|
||||
"29", #SHUTDOWN_UNDEPLOY
|
||||
#30, #EPILOG_UNDEPLOY
|
||||
#31, #PROLOG_UNDEPLOY
|
||||
#32, #BOOT_UNDEPLOY
|
||||
#33, #HOTPLUG_PROLOG_POWEROFF
|
||||
#34, #HOTPLUG_EPILOG_POWEROFF
|
||||
#35, #BOOT_MIGRATE
|
||||
#36, #BOOT_FAILURE
|
||||
#37, #BOOT_MIGRATE_FAILURE
|
||||
#38, #PROLOG_MIGRATE_FAILURE
|
||||
#39, #PROLOG_FAILURE
|
||||
#40, #EPILOG_FAILURE
|
||||
#41, #EPILOG_STOP_FAILURE
|
||||
#42, #EPILOG_UNDEPLOY_FAILURE
|
||||
#43, #PROLOG_MIGRATE_POWEROFF
|
||||
#44, #PROLOG_MIGRATE_POWEROFF_FAILURE
|
||||
#45, #PROLOG_MIGRATE_SUSPEND
|
||||
#46, #PROLOG_MIGRATE_SUSPEND_FAILURE
|
||||
#47, #BOOT_UNDEPLOY_FAILURE
|
||||
#48, #BOOT_STOPPED_FAILURE
|
||||
#49, #PROLOG_RESUME_FAILURE
|
||||
#50, #PROLOG_UNDEPLOY_FAILURE
|
||||
#51, #DISK_SNAPSHOT_POWEROFF
|
||||
#52, #DISK_SNAPSHOT_REVERT_POWEROFF
|
||||
#53, #DISK_SNAPSHOT_DELETE_POWEROFF
|
||||
#54, #DISK_SNAPSHOT_SUSPENDED
|
||||
#55, #DISK_SNAPSHOT_REVERT_SUSPENDED
|
||||
#56, #DISK_SNAPSHOT_DELETE_SUSPENDED
|
||||
"57", #DISK_SNAPSHOT
|
||||
"58", #DISK_SNAPSHOT_REVERT
|
||||
#59, #DISK_SNAPSHOT_DELETE
|
||||
#60, #PROLOG_MIGRATE_UNKNOWN
|
||||
#61, #PROLOG_MIGRATE_UNKNOWN_FAILURE
|
||||
"62" #DISK_RESIZE
|
||||
#63, #DISK_RESIZE_POWEROFF
|
||||
#64 #DISK_RESIZE_UNDEPLOYED
|
||||
#65 #HOTPLUG_NIC_POWEROFF
|
||||
#66 #HOTPLUG_RESIZE
|
||||
#67, #HOTPLUG_SAVEAS_UNDEPLOYED
|
||||
#68, #HOTPLUG_SAVEAS_STOPPED
|
||||
]
|
||||
|
||||
class SunstoneVNC
|
||||
#
|
||||
# This class provides support for launching and stopping a websockify proxy
|
||||
#
|
||||
class SunstoneVNC < SunstoneRemoteConnections
|
||||
|
||||
attr_reader :proxy_port
|
||||
|
||||
def initialize(config, logger, options = {})
|
||||
opts={ :json_errors => true,
|
||||
:token_folder_name => 'sunstone_vnc_tokens'}.merge(options)
|
||||
super(logger, options)
|
||||
|
||||
# Add token folder to options
|
||||
opts = {
|
||||
:token_folder_name => 'sunstone_vnc_tokens'
|
||||
}.merge(@options)
|
||||
|
||||
@options = opts
|
||||
|
||||
# Create configuration variables
|
||||
@pipe = nil
|
||||
@token_folder = File.join(VAR_LOCATION, opts[:token_folder_name])
|
||||
@proxy_path = File.join(SHARE_LOCATION, "websockify/run")
|
||||
@proxy_path = File.join(SHARE_LOCATION, 'websockify/run')
|
||||
@proxy_port = config[:vnc_proxy_port]
|
||||
|
||||
@proxy_ipv6 = config[:vnc_proxy_ipv6]
|
||||
@ -122,20 +56,18 @@ class SunstoneVNC
|
||||
|
||||
@lock_file = NOVNC_LOCK_FILE
|
||||
|
||||
if (@wss == "yes") || (@wss == "only") || (@wss == true)
|
||||
if (@wss == 'yes') || (@wss == 'only') || (@wss == true)
|
||||
@enable_wss = true
|
||||
@cert = config[:vnc_proxy_cert]
|
||||
@key = config[:vnc_proxy_key]
|
||||
else
|
||||
@enable_wss = false
|
||||
end
|
||||
@options = opts
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def start
|
||||
if is_running?
|
||||
message="VNC server already running"
|
||||
if running?
|
||||
message='VNC server already running'
|
||||
STDERR.puts message
|
||||
@logger.info message
|
||||
return false
|
||||
@ -147,32 +79,32 @@ class SunstoneVNC
|
||||
|
||||
if @enable_wss
|
||||
proxy_options << " --cert #{@cert}"
|
||||
proxy_options << " --key #{@key}" if @key && @key.size > 0
|
||||
proxy_options << " --ssl-only" if @wss == "only"
|
||||
proxy_options << " --key #{@key}" if @key && !@key.empty?
|
||||
proxy_options << ' --ssl-only' if @wss == 'only'
|
||||
end
|
||||
|
||||
if @proxy_ipv6
|
||||
proxy_options << " -6"
|
||||
proxy_options << ' -6'
|
||||
end
|
||||
|
||||
python = 'python3' if system("python3 -c True")
|
||||
python = 'python' if system("python -c True")
|
||||
python = 'python3' if system('python3 -c True')
|
||||
python = 'python' if system('python -c True')
|
||||
|
||||
cmd ="#{python} #{@proxy_path} #{proxy_options} #{@proxy_port}"
|
||||
|
||||
begin
|
||||
@logger.info { "Starting VNC proxy: #{cmd}" }
|
||||
pid=start_daemon(cmd, VNC_LOG)
|
||||
rescue Exception => e
|
||||
rescue StandardError => e
|
||||
@logger.error e.message
|
||||
return false
|
||||
end
|
||||
|
||||
begin
|
||||
File.open(@lock_file, "w") do |f|
|
||||
File.open(@lock_file, 'w') do |f|
|
||||
f.write(pid.to_s)
|
||||
end
|
||||
rescue Exception => e
|
||||
rescue StandardError => e
|
||||
@logger.error e.message
|
||||
Process.kill('-KILL', pid)
|
||||
|
||||
@ -181,8 +113,8 @@ class SunstoneVNC
|
||||
|
||||
sleep 1
|
||||
|
||||
if !is_running?
|
||||
message="Error starting VNC proxy"
|
||||
if !running?
|
||||
message='Error starting VNC proxy'
|
||||
STDERR.puts message
|
||||
@logger.error message
|
||||
File.delete(@lock_file) if File.exist?(@lock_file)
|
||||
@ -190,24 +122,35 @@ class SunstoneVNC
|
||||
return false
|
||||
end
|
||||
|
||||
STDOUT.puts "VNC proxy started"
|
||||
STDOUT.puts 'VNC proxy started'
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def proxy(vm_resource)
|
||||
# Check configurations and VM attributes
|
||||
unless is_lockfile?
|
||||
return error(400, "VNC Proxy is not running")
|
||||
unless lockfile?
|
||||
return error(400, 'VNC Proxy is not running')
|
||||
end
|
||||
|
||||
if !VNC_STATES.include?(vm_resource['LCM_STATE'])
|
||||
return error(400,"Wrong state (#{vm_resource['LCM_STATE']}) to open a VNC session")
|
||||
if !allowed_console_states.include?(vm_resource['LCM_STATE'])
|
||||
return error(
|
||||
400,
|
||||
"Wrong state (#{vm_resource['LCM_STATE']}) " \
|
||||
'to open a VNC session'
|
||||
)
|
||||
end
|
||||
|
||||
if vm_resource['TEMPLATE/GRAPHICS/TYPE'].nil? ||
|
||||
!(["vnc", "spice"].include?(vm_resource['TEMPLATE/GRAPHICS/TYPE'].downcase))
|
||||
return error(400,"VM has no VNC configured")
|
||||
if vm_resource['TEMPLATE/GRAPHICS/TYPE'].nil?
|
||||
return error(400, 'VM has no VNC configured')
|
||||
end
|
||||
|
||||
include_vnc_or_spice = %w[vnc spice].include?(
|
||||
vm_resource['TEMPLATE/GRAPHICS/TYPE'].downcase
|
||||
)
|
||||
|
||||
if !include_vnc_or_spice
|
||||
return error(400, 'VM has no VNC configured')
|
||||
end
|
||||
|
||||
# Proxy data
|
||||
@ -216,16 +159,21 @@ class SunstoneVNC
|
||||
vnc_pw = vm_resource['TEMPLATE/GRAPHICS/PASSWD']
|
||||
|
||||
# If it is a vCenter VM
|
||||
if vm_resource['USER_TEMPLATE/HYPERVISOR'] == "vcenter"
|
||||
if vm_resource['MONITORING/VCENTER_ESX_HOST']
|
||||
host = vm_resource['MONITORING/VCENTER_ESX_HOST']
|
||||
else
|
||||
return error(400,"Could not determine the vCenter ESX host where the VM is running. Wait till the VCENTER_ESX_HOST attribute is retrieved once the host has been monitored")
|
||||
if vm_resource['USER_TEMPLATE/HYPERVISOR'] == 'vcenter'
|
||||
unless vm_resource['MONITORING/VCENTER_ESX_HOST']
|
||||
return error(
|
||||
400,
|
||||
'Could not determine the vCenter ESX host where ' \
|
||||
'the VM is running. Wait till the VCENTER_ESX_HOST ' \
|
||||
'attribute is retrieved once the host has been monitored'
|
||||
)
|
||||
end
|
||||
|
||||
host = vm_resource['MONITORING/VCENTER_ESX_HOST']
|
||||
end
|
||||
|
||||
# Generate token random_str: host:port
|
||||
random_str = rand(36**20).to_s(36) #random string a-z0-9 length 20
|
||||
random_str = rand(36**20).to_s(36) # Random string a-z0-9 length 20
|
||||
token = "#{random_str}: #{host}:#{vnc_port}"
|
||||
token_file = 'one-'+vm_resource['ID']
|
||||
|
||||
@ -234,53 +182,45 @@ class SunstoneVNC
|
||||
f = File.open(File.join(@token_folder, token_file), 'w')
|
||||
f.write(token)
|
||||
f.close
|
||||
rescue Exception => e
|
||||
rescue StandardError => e
|
||||
@logger.error e.message
|
||||
return error(500, "Cannot create VNC proxy token")
|
||||
return error(500, 'Cannot create VNC proxy token')
|
||||
end
|
||||
|
||||
info = SunstoneVMHelper.get_remote_info(vm_resource)
|
||||
encode_info = Base64.encode64(info.to_json)
|
||||
|
||||
info = {
|
||||
info = {
|
||||
:password => vnc_pw,
|
||||
:token => random_str,
|
||||
:info => encode_info
|
||||
}
|
||||
|
||||
return [200, info.to_json]
|
||||
[200, info.to_json]
|
||||
end
|
||||
|
||||
# Delete proxy token file
|
||||
def delete_token(filename)
|
||||
begin
|
||||
File.delete(File.join(@token_folder, filename))
|
||||
rescue => e
|
||||
rescue StandardError => e
|
||||
@logger.error "Error deleting token file for VM #{vm_id}"
|
||||
@logger.error e.message
|
||||
end
|
||||
end
|
||||
|
||||
def stop(force=false)
|
||||
def stop
|
||||
pid=get_pid
|
||||
|
||||
if pid
|
||||
@logger.info "Killing VNC proxy"
|
||||
@logger.info 'Killing VNC proxy'
|
||||
|
||||
signal=(force ? '-KILL' : '-TERM')
|
||||
Process.kill(signal ,pid)
|
||||
Process.kill('-KILL', pid)
|
||||
|
||||
sleep 1
|
||||
|
||||
begin
|
||||
Process.getpgid(pid)
|
||||
|
||||
Process.kill('-KILL', pid)
|
||||
rescue
|
||||
end
|
||||
|
||||
if is_running?
|
||||
message="VNC server is still running"
|
||||
if running?
|
||||
message = 'VNC server is still running'
|
||||
STDERR.puts message
|
||||
@logger.error message
|
||||
return false
|
||||
@ -288,9 +228,9 @@ class SunstoneVNC
|
||||
|
||||
delete_token_dir
|
||||
|
||||
STDOUT.puts "VNC proxy stopped"
|
||||
STDOUT.puts 'VNC proxy stopped'
|
||||
else
|
||||
message="VNC server is not running"
|
||||
message = 'VNC server is not running'
|
||||
@logger.info message
|
||||
STDERR.puts message
|
||||
end
|
||||
@ -298,49 +238,41 @@ class SunstoneVNC
|
||||
end
|
||||
|
||||
def status
|
||||
if is_running?
|
||||
STDOUT.puts "VNC is running"
|
||||
if running?
|
||||
STDOUT.puts 'VNC is running'
|
||||
true
|
||||
else
|
||||
STDOUT.puts "VNC is NOT running"
|
||||
STDOUT.puts 'VNC is NOT running'
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def error(code, msg)
|
||||
if @options[:json_errors]
|
||||
return [code,OpenNebula::Error.new(msg).to_json]
|
||||
else
|
||||
return [code,msg]
|
||||
end
|
||||
end
|
||||
|
||||
def create_token_dir
|
||||
delete_token_dir
|
||||
begin
|
||||
Dir.mkdir(@token_folder) if !File.exist?(@token_folder)
|
||||
rescue Exception => e
|
||||
@logger.error "Cannot create token folder"
|
||||
Dir.mkdir(@token_folder) unless File.exist?(@token_folder)
|
||||
rescue StandardError => e
|
||||
@logger.error 'Cannot create token folder'
|
||||
@logger.error e.message
|
||||
end
|
||||
end
|
||||
|
||||
def delete_token_dir
|
||||
if File.exist?(@token_folder)
|
||||
begin
|
||||
Dir.glob("#{@token_folder}/*").each do |file|
|
||||
File.delete(file)
|
||||
end
|
||||
rescue => e
|
||||
@logger.error "Error deleting token folder"
|
||||
@logger.error e.message
|
||||
return unless File.exist?(@token_folder)
|
||||
|
||||
begin
|
||||
Dir.glob("#{@token_folder}/*").each do |file|
|
||||
File.delete(file)
|
||||
end
|
||||
rescue StandardError => e
|
||||
@logger.error 'Error deleting token folder'
|
||||
@logger.error e.message
|
||||
end
|
||||
end
|
||||
|
||||
def is_running?
|
||||
def running?
|
||||
if File.exist?(@lock_file)
|
||||
pid=File.read(@lock_file).strip
|
||||
|
||||
@ -348,15 +280,15 @@ class SunstoneVNC
|
||||
return pid.to_i
|
||||
end
|
||||
|
||||
@logger.info "Deleting stale lock file"
|
||||
@logger.info 'Deleting stale lock file'
|
||||
File.delete(@lock_file)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
alias_method :get_pid, :is_running?
|
||||
alias get_pid running?
|
||||
|
||||
def is_lockfile?
|
||||
def lockfile?
|
||||
dn = File.dirname(@lock_file)
|
||||
bn = File.basename(@lock_file)
|
||||
|
||||
@ -370,37 +302,20 @@ class SunstoneVNC
|
||||
end
|
||||
end
|
||||
|
||||
if RUBY_VERSION<'1.9'
|
||||
def spawn(*args)
|
||||
fork {
|
||||
command=args[0..-2]
|
||||
|
||||
# Close stdin and point out and err to log file
|
||||
$stdout.reopen(VNC_LOG, "a")
|
||||
$stderr.reopen(VNC_LOG, "a")
|
||||
$stdin.close
|
||||
|
||||
# Detach process from the parent
|
||||
Process.setsid
|
||||
|
||||
exec(*command)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def start_daemon(cmd, log)
|
||||
options={
|
||||
:pgroup => true,
|
||||
:in => :close,
|
||||
[:out, :err] => [log, "a"],
|
||||
:close_others => true }
|
||||
[:out, :err] => [log, 'a'],
|
||||
:close_others => true
|
||||
}
|
||||
|
||||
params=cmd.split(" ")+[options]
|
||||
pid=spawn( *params )
|
||||
params=cmd.split(' ')+[options]
|
||||
pid=spawn(*params)
|
||||
|
||||
Process.detach(pid)
|
||||
|
||||
pid
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -93,6 +93,11 @@ module.exports = function(grunt) {
|
||||
name: "console/spice",
|
||||
include: ["almond"],
|
||||
insertRequire: ["console/spice"]
|
||||
},
|
||||
{
|
||||
name: "console/guacamole",
|
||||
include: ["almond"],
|
||||
insertRequire: ["console/guacamole"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -15,5 +15,24 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
define(function(require) {
|
||||
return 'vmrcVMDialog';
|
||||
var GuacController = require('utils/guacamole/controller');
|
||||
|
||||
try {
|
||||
var endpoint = new URL(window.location.href);
|
||||
var encoded_socket = endpoint.searchParams.get("socket");
|
||||
var socket_string = atob(encoded_socket);
|
||||
|
||||
var url = new URL(socket_string);
|
||||
var params = url.searchParams;
|
||||
var token = params.get("token");
|
||||
var info = params.get("info");
|
||||
|
||||
var controller = new GuacController();
|
||||
controller.setInformation(info);
|
||||
controller.setConnection(token);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
$('#guacamole-state').empty().text('Failed');
|
||||
}
|
||||
|
||||
});
|
@ -29,70 +29,43 @@ define(function(require) {
|
||||
document.cookie = name + "=" + value + expires + "; path=/";
|
||||
};
|
||||
|
||||
function spice_query_var(name, defvalue) {
|
||||
var match = RegExp('[?&]' + name + '=([^&]*)')
|
||||
.exec(window.location.search);
|
||||
return match ?
|
||||
decodeURIComponent(match[1].replace(/\+/g, ' '))
|
||||
: defvalue;
|
||||
}
|
||||
|
||||
function spice_error(e) {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
function connect() {
|
||||
var info = spice_query_var('info', undefined);
|
||||
var endpoint = new URL(window.location.href);
|
||||
var encoded_socket = endpoint.searchParams.get("socket");
|
||||
var socket_string = atob(encoded_socket);
|
||||
|
||||
var socket_endpoint = new URL(socket_string);
|
||||
var password = socket_endpoint.searchParams.get("password");
|
||||
var token = socket_endpoint.searchParams.get("token");
|
||||
var info = socket_endpoint.searchParams.get("info");
|
||||
|
||||
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
|
||||
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
|
||||
// traffic to the correct destination port.
|
||||
var default_port = window.location.port;
|
||||
if (!default_port) {
|
||||
if (window.location.protocol == 'http:') {
|
||||
default_port = 80;
|
||||
} else if (window.location.protocol == 'https:') {
|
||||
default_port = 443;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
var token = spice_query_var('token', null);
|
||||
if (token) {
|
||||
spice_set_cookie('token', token, 1)
|
||||
}
|
||||
|
||||
var password = spice_query_var('password', '');
|
||||
var port = spice_query_var('port', default_port);
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sc) {
|
||||
sc.stop();
|
||||
}
|
||||
|
||||
var uri = scheme + host + ":" + port + "?token=" + token;
|
||||
|
||||
try {
|
||||
sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected});
|
||||
sc = new SpiceMainConn({
|
||||
uri: socket_string,
|
||||
screen_id: "spice-screen",
|
||||
dump_id: "debug-div",
|
||||
message_id: "message-div",
|
||||
password: password,
|
||||
onerror: spice_error,
|
||||
onagent: agent_connected
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
alert(e.toString());
|
||||
@ -101,7 +74,6 @@ define(function(require) {
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
console.log(">> disconnect");
|
||||
if (sc) {
|
||||
sc.stop();
|
||||
}
|
||||
@ -111,7 +83,6 @@ define(function(require) {
|
||||
document.getElementById('spice-area').removeEventListener('dragover', handle_file_dragover, false);
|
||||
document.getElementById('spice-area').removeEventListener('drop', handle_file_drop, false);
|
||||
}
|
||||
console.log("<< disconnect");
|
||||
}
|
||||
|
||||
function agent_connected(sc) {
|
||||
|
@ -26,16 +26,16 @@ define(function(require) {
|
||||
|
||||
function setStatus(message="", status=""){
|
||||
$(".VMRC_message").text(message);
|
||||
$("#VMRC_status").text(status);
|
||||
$("#VMRC_status_msg").text(status);
|
||||
}
|
||||
|
||||
function connected(){
|
||||
setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " + _wmks.vm_name);
|
||||
setStatus(null, "VMRC " + _wmks.connectionState);
|
||||
}
|
||||
|
||||
function disconnectedFromServer(e){
|
||||
if (e.detail.clean) {
|
||||
setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " + _wmks.vm_name);
|
||||
setStatus(null, "VMRC " + _wmks.connectionState);
|
||||
} else {
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
@ -45,76 +45,44 @@ define(function(require) {
|
||||
if (_wmks) { _wmks.sendCAD(); }
|
||||
}
|
||||
|
||||
function updateState(state, msg) {
|
||||
var s, sb, cad, level;
|
||||
s = document.querySelector("#VMRC_status");
|
||||
sb = document.querySelector("#VMRC_status_bar");
|
||||
cad = document.querySelector("#sendCtrlAltDelButton");
|
||||
switch (state) {
|
||||
case "failed": level = "error"; break;
|
||||
case "fatal": level = "error"; break;
|
||||
case "normal": level = "normal"; break;
|
||||
case "disconnected": level = "normal"; break;
|
||||
case "loaded": level = "normal"; break;
|
||||
default: level = "warn"; break;
|
||||
}
|
||||
function enterFullScreen() {
|
||||
if (_wmks) { _wmks.enterFullScreen(); }
|
||||
}
|
||||
|
||||
function selectLanguage() {
|
||||
if(!_wmks) return;
|
||||
var keyboardLayoutId = $('#selectLanguage').find(":selected").val();
|
||||
_wmks.setOption('keyboardLayoutId',keyboardLayoutId);
|
||||
}
|
||||
|
||||
if (state === "normal") {
|
||||
cad.disabled = false;
|
||||
} else {
|
||||
cad.disabled = true;
|
||||
xvpInit(0);
|
||||
}
|
||||
|
||||
if (typeof(msg) !== "undefined") {
|
||||
sb.setAttribute("class", "VMRC_status_" + level);
|
||||
s.innerHTML = msg;
|
||||
}
|
||||
function keyboardSelector() {
|
||||
$('#selectLanguage').toggle();
|
||||
}
|
||||
|
||||
function getQueryVariable(variable)
|
||||
{
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if(pair[0] == variable){return pair[1];}
|
||||
}
|
||||
return(false);
|
||||
function updateScreen() {
|
||||
_wmks.updateScreen();
|
||||
}
|
||||
|
||||
var URL = "";
|
||||
var host = getQueryVariable("host");
|
||||
var port = getQueryVariable("port");
|
||||
var ticket = getQueryVariable("ticket");
|
||||
|
||||
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";
|
||||
_is_encrypted = "encrypted";
|
||||
} else {
|
||||
URL = "ws";
|
||||
_is_encrypted = "unencrypted";
|
||||
}
|
||||
URL += "://" + host;
|
||||
URL += ":" + port;
|
||||
URL += "/vmrc/" + ticket;
|
||||
|
||||
document.querySelector("#sendCtrlAltDelButton").style.display = "inline";
|
||||
document.querySelector("#sendCtrlAltDelButton").onclick = sendCtrlAltDel;
|
||||
|
||||
document.querySelector("#fullScreenButton").onclick = enterFullScreen;
|
||||
document.querySelector("#keyboardSelector").onclick = keyboardSelector;
|
||||
document.querySelector("#selectLanguage").onchange = selectLanguage;
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
updateState("failed",
|
||||
"Must specify host and port in URL");
|
||||
return;
|
||||
}
|
||||
var endpoint = new URL(window.location.href);
|
||||
var encoded_socket = endpoint.searchParams.get("socket");
|
||||
var socket_string = atob(encoded_socket);
|
||||
|
||||
var socket_endpoint = new URL(socket_string);
|
||||
var host = socket_endpoint.searchParams.get("host");
|
||||
var port = socket_endpoint.searchParams.get("port");
|
||||
var info = socket_endpoint.searchParams.get("info");
|
||||
var ticket = socket_endpoint.searchParams.get("ticket");
|
||||
|
||||
var info_decode = UtilsConnection.decodeInfoConnection(info);
|
||||
UtilsConnection.printInfoConnection($('.VMRC_info'), info_decode)
|
||||
|
||||
|
||||
try{
|
||||
_wmks = WMKS.createWMKS("wmksContainer", {})
|
||||
@ -129,9 +97,11 @@ define(function(require) {
|
||||
_wmks.eventHandlers["connectionstatechange"].push(connected);
|
||||
_wmks.eventHandlers["disconnect"] = disconnectedFromServer;
|
||||
_wmks.vm_name = info_decode && info_decode.name;
|
||||
|
||||
_wmks.connect(URL);
|
||||
|
||||
_wmks.connect(socket_string);
|
||||
}catch(err){
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
|
||||
$(window).resize(updateScreen);
|
||||
});
|
||||
|
@ -24,16 +24,16 @@ define(function(require) {
|
||||
|
||||
function setStatus(message="", status=""){
|
||||
$(".NOVNC_message").text(message);
|
||||
$("#noVNC_status").text(status);
|
||||
$("#noVNC_status_msg").text(status);
|
||||
}
|
||||
|
||||
function connected(){
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + _rfb._fb_name);
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state);
|
||||
}
|
||||
|
||||
function disconnectedFromServer(e){
|
||||
if (e.detail.clean) {
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + _rfb._fb_name);
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state);
|
||||
} else {
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
@ -41,7 +41,7 @@ define(function(require) {
|
||||
|
||||
function desktopNameChange(e) {
|
||||
if (e.detail.name) {
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + e.detail.name);
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,69 +106,27 @@ define(function(require) {
|
||||
}
|
||||
}
|
||||
|
||||
function getQueryVariable(variable) {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
|
||||
for (var i=0;i<vars.length;i++) {
|
||||
var pair = vars[i].split("=");
|
||||
|
||||
if (pair[0] == variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var URL = "";
|
||||
var proxy_host = window.location.hostname;
|
||||
var proxy_port = Config.vncProxyPort;
|
||||
var token = getQueryVariable("token");
|
||||
var password = getQueryVariable("password");
|
||||
var endpoint = new URL(window.location.href);
|
||||
var encoded_socket = endpoint.searchParams.get("socket");
|
||||
var socket_string = atob(encoded_socket);
|
||||
|
||||
var socket_endpoint = new URL(socket_string);
|
||||
var password = socket_endpoint.searchParams.get("password");
|
||||
var info = socket_endpoint.searchParams.get("info");
|
||||
|
||||
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 } } : {};
|
||||
|
||||
if (window.location.protocol === "https:") {
|
||||
URL = "wss";
|
||||
_is_encrypted = "encrypted";
|
||||
} else {
|
||||
URL = "ws";
|
||||
_is_encrypted = "unencrypted";
|
||||
}
|
||||
|
||||
URL += "://" + window.location.hostname;
|
||||
URL += ":" + proxy_port;
|
||||
URL += "?host=" + proxy_host;
|
||||
URL += "&port=" + proxy_port;
|
||||
|
||||
if(token){
|
||||
URL += "&token=" + token;
|
||||
}
|
||||
URL += "&encrypt=" + Config.vncWSS;
|
||||
|
||||
document.querySelector("#sendCtrlAltDelButton").style.display = "inline";
|
||||
document.querySelector("#sendCtrlAltDelButton").onclick = sendCtrlAltDel;
|
||||
document.querySelector("#xvpShutdownButton").onclick = xvpShutdown;
|
||||
document.querySelector("#xvpRebootButton").onclick = xvpReboot;
|
||||
document.querySelector("#xvpResetButton").onclick = xvpReset;
|
||||
|
||||
if ((!proxy_host) || (!proxy_port)) {
|
||||
updateState("failed",
|
||||
"Must specify host and port in URL");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_rfb = new RFB(document.querySelector("#VNC_canvas"), URL, rfbConfig);
|
||||
_rfb = new RFB(document.querySelector("#VNC_canvas"), socket_string, rfbConfig);
|
||||
_rfb.addEventListener("connect", connected);
|
||||
_rfb.addEventListener("disconnect", disconnectedFromServer);
|
||||
_rfb.addEventListener("desktopname", desktopNameChange);
|
||||
|
@ -743,8 +743,6 @@ define(function(require) {
|
||||
require("./vms-tab/dialogs/attach-nic"),
|
||||
require("./vms-tab/dialogs/revert"),
|
||||
require("./vms-tab/dialogs/snapshot"),
|
||||
require("./vms-tab/dialogs/vnc"),
|
||||
require("./vms-tab/dialogs/spice"),
|
||||
require("./users-tab/dialogs/login-token")
|
||||
];
|
||||
|
||||
|
@ -38,9 +38,6 @@ define(function(require) {
|
||||
var TAB_ID = require("../tabId");
|
||||
var _accordionId = 0;
|
||||
|
||||
var VNC_DIALOG_ID = require("tabs/vms-tab/dialogs/vnc/dialogId");
|
||||
var SPICE_DIALOG_ID = require("tabs/vms-tab/dialogs/spice/dialogId");
|
||||
|
||||
return {
|
||||
"generate": generate_provision_vms_list,
|
||||
"show": show_provision_vm_list,
|
||||
@ -796,15 +793,15 @@ define(function(require) {
|
||||
success: function(_, response){
|
||||
if (OpenNebulaVM.isVNCSupported(vm_data)) {
|
||||
|
||||
var dialog = Sunstone.getDialog(VNC_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
var urlAndLink = Vnc.getURLAndLink(response);
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(urlAndLink.link);
|
||||
|
||||
button.removeAttr("disabled");
|
||||
} else if (OpenNebulaVM.isSPICESupported(vm_data)) {
|
||||
var dialog = Sunstone.getDialog(SPICE_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
var urlAndLink = Spice.getURLAndLink(response);
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(urlAndLink.link);
|
||||
|
||||
button.removeAttr("disabled");
|
||||
} else {
|
||||
|
@ -19,7 +19,6 @@ define(function(require) {
|
||||
var Buttons = require("./vms-tab/buttons");
|
||||
var Actions = require("./vms-tab/actions");
|
||||
var Table = require("./vms-tab/datatable");
|
||||
require("utils/vnc");
|
||||
var TAB_ID = require("./vms-tab/tabId");
|
||||
var DATATABLE_ID = "dataTableVms";
|
||||
|
||||
@ -35,10 +34,6 @@ define(function(require) {
|
||||
require("./vms-tab/dialogs/attach-nic"),
|
||||
require("./vms-tab/dialogs/snapshot"),
|
||||
require("./vms-tab/dialogs/revert"),
|
||||
require("./vms-tab/dialogs/vnc"),
|
||||
require("./vms-tab/dialogs/vmrc"),
|
||||
require("./vms-tab/dialogs/spice"),
|
||||
require("./vms-tab/dialogs/guac"),
|
||||
require("./vms-tab/dialogs/saveas-template")
|
||||
];
|
||||
|
||||
|
@ -21,23 +21,16 @@ define(function(require) {
|
||||
var Locale = require('utils/locale');
|
||||
var OpenNebulaVM = require('opennebula/vm');
|
||||
var CommonActions = require('utils/common-actions');
|
||||
var Vnc = require('utils/vnc');
|
||||
var Vmrc = require('utils/vmrc');
|
||||
var Spice = require('utils/spice');
|
||||
var Files = require('utils/files');
|
||||
|
||||
var CREATE_APP_DIALOG_ID = require('tabs/marketplaceapps-tab/form-panels/create/formPanelId');
|
||||
var CREATE_DIALOG_ID = require('./form-panels/create/formPanelId');
|
||||
var DEPLOY_DIALOG_ID = require('./dialogs/deploy/dialogId');
|
||||
var GUAC_DIALOG_ID = require('./dialogs/guac/dialogId');
|
||||
var MARKETPLACEAPPS_TAB_ID = require('tabs/marketplaceapps-tab/tabId');
|
||||
var MIGRATE_DIALOG_ID = require('./dialogs/migrate/dialogId');
|
||||
var SAVE_AS_TEMPLATE_DIALOG_ID = require('./dialogs/saveas-template/dialogId');
|
||||
var SPICE_DIALOG_ID = require('./dialogs/spice/dialogId');
|
||||
var TAB_ID = require('./tabId');
|
||||
var UPDATECONF_FORM_ID = require('./form-panels/updateconf/formPanelId');
|
||||
var VMRC_DIALOG_ID = require('./dialogs/vmrc/dialogId');
|
||||
var VNC_DIALOG_ID = require('./dialogs/vnc/dialogId');
|
||||
|
||||
var XML_ROOT = "VM";
|
||||
var RESOURCE = "VM";
|
||||
@ -257,13 +250,7 @@ define(function(require) {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
if (!Vnc.lockStatus()) {
|
||||
Vnc.lock();
|
||||
Sunstone.runAction("VM.startvnc_action", elem);
|
||||
} else {
|
||||
Notifier.notifyError(Locale.tr("VNC Connection in progress"))
|
||||
return false;
|
||||
}
|
||||
Sunstone.runAction("VM.startvnc_action", elem);
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -271,13 +258,20 @@ define(function(require) {
|
||||
type: "single",
|
||||
call: OpenNebulaVM.vnc,
|
||||
callback: function(request, response) {
|
||||
var dialog = Sunstone.getDialog(VNC_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
var link = getLink(response,{
|
||||
port: Config.vncProxyPort,
|
||||
connnection_type: 'vnc',
|
||||
extra_params: [
|
||||
'port=' + Config.vncProxyPort,
|
||||
'encrypt=' + Config.vncWSS,
|
||||
!Config.requestVNCPassword && 'password=' + response.password
|
||||
]
|
||||
});
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(link);
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
Vnc.unlock();
|
||||
},
|
||||
notify: true
|
||||
},
|
||||
@ -285,14 +279,8 @@ define(function(require) {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
if (!Vmrc.lockStatus()) {
|
||||
Vmrc.lock();
|
||||
var vm_name = OpenNebulaVM.getName(elem);
|
||||
Sunstone.runAction("VM.startvmrc_action", elem, vm_name);
|
||||
} else {
|
||||
Notifier.notifyError(Locale.tr("VMRC Connection in progress"))
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -300,14 +288,19 @@ define(function(require) {
|
||||
type: "single",
|
||||
call: OpenNebulaVM.vmrc,
|
||||
callback: function(request, response) {
|
||||
var dialog = Sunstone.getDialog(VMRC_DIALOG_ID);
|
||||
response["vm_name"] = request.request.data[0].extra_param;
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
response["vm_name"] = request.request.data[0].extra_param;
|
||||
var fireedge_endpoint = new URL(Config.publicFireedgeEndpoint);
|
||||
var link = getLink(response,{
|
||||
host: fireedge_endpoint.hostname,
|
||||
port: fireedge_endpoint.port,
|
||||
connnection_type: 'vmrc',
|
||||
extra_path: '/fireedge/vmrc/' + response.data.ticket,
|
||||
});
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(link);
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
Vmrc.unlock();
|
||||
},
|
||||
notify: true
|
||||
},
|
||||
@ -315,13 +308,7 @@ define(function(require) {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
if (!Spice.lockStatus()) {
|
||||
Spice.lock();
|
||||
Sunstone.runAction("VM.startspice_action", elem);
|
||||
} else {
|
||||
Notifier.notifyError(Locale.tr("SPICE Connection in progress"))
|
||||
return false;
|
||||
}
|
||||
Sunstone.runAction("VM.startspice_action", elem);
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -329,13 +316,19 @@ define(function(require) {
|
||||
type: "single",
|
||||
call: OpenNebulaVM.vnc,
|
||||
callback: function(request, response) {
|
||||
var dialog = Sunstone.getDialog(SPICE_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
var link = getLink(response, {
|
||||
port: Config.vncProxyPort,
|
||||
connnection_type: 'spice',
|
||||
extra_params: [
|
||||
'password=' + response.password,
|
||||
'encrypt=' + config.user_config.vnc_wss,
|
||||
]
|
||||
});
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(link);
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
Spice.unlock();
|
||||
},
|
||||
notify: true
|
||||
},
|
||||
@ -376,9 +369,12 @@ define(function(require) {
|
||||
type: "single",
|
||||
call: OpenNebulaVM.guac,
|
||||
callback: function(_, response) {
|
||||
var dialog = Sunstone.getDialog(GUAC_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
var link = getLink(response, {
|
||||
connnection_type: 'guac',
|
||||
extra_path: '/fireedge/guacamole'
|
||||
});
|
||||
// Open in a new tab the noVNC connection
|
||||
window.open(link);
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
@ -461,5 +457,46 @@ define(function(require) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Object} response Callback response with the token and info
|
||||
* @param {Object} options
|
||||
* @returns
|
||||
*/
|
||||
function getLink(response, options){
|
||||
options = $.extend({
|
||||
host: undefined,
|
||||
port: undefined,
|
||||
connnection_type: '',
|
||||
extra_path: '',
|
||||
extra_params: []
|
||||
}, options);
|
||||
|
||||
var params = options.extra_params.concat([
|
||||
response.token && 'token=' + response.token,
|
||||
response.info && 'info=' + response.info
|
||||
]).filter(Boolean);
|
||||
|
||||
var endpoint = new URL(window.location.href);
|
||||
var websocketProtocol = endpoint.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
|
||||
var websocket = websocketProtocol + '//';
|
||||
|
||||
if (options.host && options.port)
|
||||
websocket += options.host + ':' + options.port
|
||||
else if (options.port)
|
||||
websocket += endpoint.hostname + ':' + options.port
|
||||
else
|
||||
websocket += endpoint.host;
|
||||
|
||||
websocket += options.extra_path + '?' + params.join("&");
|
||||
|
||||
var encoded_socket = btoa(websocket);
|
||||
|
||||
var link = endpoint.origin + "/" + options.connnection_type + "?socket=" + encoded_socket;
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
return _actions;
|
||||
});
|
||||
|
@ -201,6 +201,7 @@ define(function(require) {
|
||||
},
|
||||
"VM.startvnc" : {
|
||||
type: "action",
|
||||
id: "vm_vnc_action",
|
||||
text: Locale.tr("VNC"),
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info vnc-sunstone-info vnc-button"
|
||||
|
@ -1,89 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
|
||||
var BaseDialog = require('utils/dialogs/dialog');
|
||||
var GuacController = require('utils/guacamole/controller');
|
||||
var Locale = require("utils/locale");
|
||||
var Notifier = require("utils/notifier");
|
||||
var Sunstone = require('sunstone');
|
||||
|
||||
var TemplateHTML = require('hbs!./guac/html');
|
||||
|
||||
var DIALOG_ID = require('./guac/dialogId');
|
||||
|
||||
function Dialog() {
|
||||
this.dialogId = DIALOG_ID;
|
||||
|
||||
BaseDialog.call(this);
|
||||
};
|
||||
|
||||
Dialog.DIALOG_ID = DIALOG_ID;
|
||||
Dialog.prototype = Object.create(BaseDialog.prototype);
|
||||
Dialog.prototype.constructor = Dialog;
|
||||
Dialog.prototype.html = _html;
|
||||
Dialog.prototype.onShow = _onShow;
|
||||
Dialog.prototype.onClose = _onClose;
|
||||
Dialog.prototype.setup = _setup;
|
||||
Dialog.prototype.setElement = _setElement;
|
||||
|
||||
return Dialog;
|
||||
|
||||
/* FUNCTION DEFINITIONS */
|
||||
|
||||
function _html() {
|
||||
return TemplateHTML({ 'dialogId': this.dialogId });
|
||||
}
|
||||
|
||||
function _setup(context) {
|
||||
$("#open_in_a_new_window_gclient", context).on("click", function() {
|
||||
var dialog = Sunstone.getDialog(DIALOG_ID);
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onShow() {
|
||||
var token = this.element.token;
|
||||
var info = this.element.info;
|
||||
|
||||
if (!token) {
|
||||
Notifier.notifyError(
|
||||
Locale.tr("The OpenNebula service for remote console is not running, please contact your administrator.")
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
this.controller = new GuacController();
|
||||
this.controller.setInformation(info);
|
||||
this.controller.setConnection(token);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onClose() {
|
||||
this.controller.disconnect();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setElement(element) {
|
||||
this.element = element;
|
||||
}
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
return 'guacVMDialog';
|
||||
});
|
@ -1,63 +0,0 @@
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
{{! 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. }}
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
|
||||
<div id="{{dialogId}}" class="reveal full" data-reveal>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h5 class="subheader">
|
||||
<span id="guacamole-state"></span>
|
||||
<span id="guacamole-loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</span>
|
||||
<span id="guacamole-buttons" class="right">
|
||||
<button class="button alert" id="sendCtrlAltDelButton_gclient">
|
||||
{{tr "Send CtrlAltDel"}}
|
||||
</button>
|
||||
<button class="button primary" id="oskButton_gclient">
|
||||
<i class="fas fa-keyboard fa-fw"></i>
|
||||
</button>
|
||||
<button class="button primary" id="mouseButton_gclient">
|
||||
<i class="fas fa-mouse-pointer fa-fw"></i>
|
||||
</button>
|
||||
<button class="button primary" id="takeScreenshot_gclient">
|
||||
<i class="fas fa-camera fa-fw" title="{{tr 'Take screenshot'}}"></i>
|
||||
</button>
|
||||
<button class="button secondary" data-close aria-label="{{tr "Close modal"}}" type="button" title="Close Guacamole">
|
||||
<i class="fas fa-times-circle fa-fw"></i>
|
||||
</button>
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="large-12 columns">
|
||||
<div class="guacamole_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="guacamole-main" class="guacamole-main">
|
||||
<div id="guacamole-display" class="guacamole-display"></div>
|
||||
</div>
|
||||
<!-- On-screen keyboard -->
|
||||
<div class="osk-container" id="osk-container">
|
||||
<div class="osk-container-header" id="osk-container-header">
|
||||
<div class="buttons">
|
||||
<button class="close" id="osk-close">x</button>
|
||||
</div>
|
||||
<div class="layouts">
|
||||
<select id="osk-qwerty"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="osk" id="osk"></div>
|
||||
</div>
|
||||
</div>
|
@ -1,90 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
/*
|
||||
DEPENDENCIES
|
||||
*/
|
||||
|
||||
var BaseDialog = require('utils/dialogs/dialog');
|
||||
var TemplateHTML = require('hbs!./spice/html');
|
||||
var Sunstone = require('sunstone');
|
||||
var Spice = require('utils/spice');
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
var DIALOG_ID = require('./spice/dialogId');
|
||||
var TAB_ID = require('../tabId')
|
||||
|
||||
/*
|
||||
CONSTRUCTOR
|
||||
*/
|
||||
|
||||
function Dialog() {
|
||||
this.dialogId = DIALOG_ID;
|
||||
|
||||
BaseDialog.call(this);
|
||||
};
|
||||
|
||||
Dialog.DIALOG_ID = DIALOG_ID;
|
||||
Dialog.prototype = Object.create(BaseDialog.prototype);
|
||||
Dialog.prototype.constructor = Dialog;
|
||||
Dialog.prototype.html = _html;
|
||||
Dialog.prototype.onShow = _onShow;
|
||||
Dialog.prototype.onClose = _onClose;
|
||||
Dialog.prototype.setup = _setup;
|
||||
Dialog.prototype.setElement = _setElement;
|
||||
|
||||
return Dialog;
|
||||
|
||||
/*
|
||||
FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
function _html() {
|
||||
return TemplateHTML({
|
||||
'dialogId': this.dialogId
|
||||
});
|
||||
}
|
||||
|
||||
function _setup(context) {
|
||||
var that = this;
|
||||
|
||||
$("#open_in_a_new_window_spice", context).on("click", function() {
|
||||
var dialog = Sunstone.getDialog(DIALOG_ID);
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onShow(context) {
|
||||
Spice.spiceCallback(this.element);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onClose(context) {
|
||||
Spice.disconnect();
|
||||
Spice.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setElement(element) {
|
||||
this.element = element
|
||||
}
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
return 'spiceVMDialog';
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
{{! 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. }}
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
|
||||
<div id="{{dialogId}}" class="reveal full" data-reveal>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h3 class="subheader" id="spice_dialog">
|
||||
{{tr "SPICE"}}
|
||||
<span id="vnc_buttons" class="right">
|
||||
<a class="button" id="open_in_a_new_window_spice" href="#" target="_blank" title="{{tr "Open in a new window"}}">
|
||||
<i class="fas fa-external-link-alt detach-spice-icon"/>
|
||||
</a>
|
||||
<button class="button secondary" data-close aria-label="{{tr "Close modal"}}" aria-label="{{tr "Close modal"}}">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="large-12 columns">
|
||||
<div class="SPICE_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reveal-body" style="width:100%; overflow-x:auto">
|
||||
<div id="spice-area" style="text-align: center; background: rgb(40, 40, 40);">
|
||||
<div id="spice-screen" class="spice-screen"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="message-div" class="spice-message" hidden></div>
|
||||
</div>
|
@ -1,94 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
/*
|
||||
DEPENDENCIES
|
||||
*/
|
||||
|
||||
var BaseDialog = require('utils/dialogs/dialog');
|
||||
var Sunstone = require('sunstone');
|
||||
var TemplateHTML = require('hbs!./vmrc/html');
|
||||
var Vmrc = require('utils/vmrc');
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
var DIALOG_ID = require('./vmrc/dialogId');
|
||||
|
||||
/*
|
||||
CONSTRUCTOR
|
||||
*/
|
||||
|
||||
function Dialog() {
|
||||
this.dialogId = DIALOG_ID;
|
||||
|
||||
BaseDialog.call(this);
|
||||
};
|
||||
|
||||
Dialog.DIALOG_ID = DIALOG_ID;
|
||||
Dialog.prototype = Object.create(BaseDialog.prototype);
|
||||
Dialog.prototype.constructor = Dialog;
|
||||
Dialog.prototype.html = _html;
|
||||
Dialog.prototype.onShow = _onShow;
|
||||
Dialog.prototype.onClose = _onClose;
|
||||
Dialog.prototype.setup = _setup;
|
||||
Dialog.prototype.setElement = _setElement;
|
||||
|
||||
return Dialog;
|
||||
|
||||
/*
|
||||
FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
function _html() {
|
||||
return TemplateHTML({
|
||||
'dialogId': this.dialogId
|
||||
});
|
||||
}
|
||||
|
||||
function _setup(context) {
|
||||
$("#open_in_a_new_window", context).on("click", function() {
|
||||
var dialog = Sunstone.getDialog(DIALOG_ID);
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
$('#sendCtrlAltDelButton', context).click(function() {
|
||||
Vmrc.sendCtrlAltDel();
|
||||
return false;
|
||||
});
|
||||
|
||||
$(window).resize(Vmrc.updateScreen);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onShow(context) {
|
||||
Vmrc.vmrcCallback(this.element);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onClose(context) {
|
||||
Vmrc.disconnect();
|
||||
Vmrc.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setElement(element) {
|
||||
this.element = element
|
||||
}
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
{{! 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. }}
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
|
||||
<div id="{{dialogId}}" class="reveal full" data-reveal>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h5 class="subheader" id="vmrc_dialog">
|
||||
<span id="VMRC_status"></span>
|
||||
<span id="VMRC_buttons" class="right">
|
||||
<button class="button alert" id="sendCtrlAltDelButton">
|
||||
{{tr "Send CtrlAltDel"}}
|
||||
</button>
|
||||
<a class="button" id="open_in_a_new_window" href="#" target="_blank" title="{{tr "Open in a new window"}}">
|
||||
<i class="fas fa-external-link-alt detach-vmrc-icon" />
|
||||
</a>
|
||||
<button class="button secondary" data-close aria-label="{{tr "Close modal"}}" title="{{tr "Close VMRC"}}">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="large-12 columns">
|
||||
<div class="VMRC_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reveal-body text-center" style="width:100%; overflow-x:auto">
|
||||
<div id="wmksContainer" style="position:absolute;width:100%;height:100%; left:0em"></div>
|
||||
</div>
|
||||
</div>
|
@ -1,95 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
/*
|
||||
DEPENDENCIES
|
||||
*/
|
||||
|
||||
var BaseDialog = require('utils/dialogs/dialog');
|
||||
var TemplateHTML = require('hbs!./vnc/html');
|
||||
var Sunstone = require('sunstone');
|
||||
var Vnc = require('utils/vnc');
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
var DIALOG_ID = require('./vnc/dialogId');
|
||||
var TAB_ID = require('../tabId')
|
||||
|
||||
/*
|
||||
CONSTRUCTOR
|
||||
*/
|
||||
|
||||
function Dialog() {
|
||||
this.dialogId = DIALOG_ID;
|
||||
|
||||
BaseDialog.call(this);
|
||||
};
|
||||
|
||||
Dialog.DIALOG_ID = DIALOG_ID;
|
||||
Dialog.prototype = Object.create(BaseDialog.prototype);
|
||||
Dialog.prototype.constructor = Dialog;
|
||||
Dialog.prototype.html = _html;
|
||||
Dialog.prototype.onShow = _onShow;
|
||||
Dialog.prototype.onClose = _onClose;
|
||||
Dialog.prototype.setup = _setup;
|
||||
Dialog.prototype.setElement = _setElement;
|
||||
|
||||
return Dialog;
|
||||
|
||||
/*
|
||||
FUNCTION DEFINITIONS
|
||||
*/
|
||||
|
||||
function _html() {
|
||||
return TemplateHTML({
|
||||
'dialogId': this.dialogId
|
||||
});
|
||||
}
|
||||
|
||||
function _setup(context) {
|
||||
var that = this;
|
||||
|
||||
$("#open_in_a_new_window", context).on("click", function() {
|
||||
var dialog = Sunstone.getDialog(DIALOG_ID);
|
||||
dialog.hide();
|
||||
});
|
||||
|
||||
$('#sendCtrlAltDelButton', context).click(function() {
|
||||
Vnc.sendCtrlAltDel();
|
||||
return false;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onShow(context) {
|
||||
Vnc.vncCallback(this.element);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onClose(context) {
|
||||
Vnc.disconnect();
|
||||
Vnc.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setElement(element) {
|
||||
this.element = element
|
||||
}
|
||||
});
|
@ -1,19 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
return 'vncVMDialog';
|
||||
});
|
@ -1,45 +0,0 @@
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
{{! 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. }}
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
|
||||
<div id="{{dialogId}}" class="reveal full" data-reveal>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h5 class="subheader" id="vnc_dialog">
|
||||
<span id="VNC_status"></span>
|
||||
<span id="VNC_buttons" class="right">
|
||||
<button class="button alert" id="sendCtrlAltDelButton">
|
||||
{{tr "Send CtrlAltDel"}}
|
||||
</button>
|
||||
<a class="button" id="open_in_a_new_window" href="#" target="_blank" title="{{tr "Open in a new window"}}">
|
||||
<i class="fas fa-external-link-alt detach-vnc-icon"/>
|
||||
</a>
|
||||
<button class="button secondary" data-close aria-label="{{tr "Close modal"}}" title="{{tr "Close VNC"}}">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="large-12 columns">
|
||||
<div class="NOVNC_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="reveal-body text-center" style="width:100%; overflow-x:auto">
|
||||
<div id="VNC_canvas" width="640px">
|
||||
<div class="NOVNC_message"></div>
|
||||
</div>
|
||||
<div id="VNC_status_bar" class="VNC_status_bar"></div>
|
||||
</div>
|
||||
</div>
|
@ -38,10 +38,10 @@ define(function(require) {
|
||||
closeOskButton: document.getElementById('osk-close'),
|
||||
|
||||
/* Buttons */
|
||||
sendCtrlAltDelButton: document.getElementById('sendCtrlAltDelButton_gclient'),
|
||||
mouseButton: document.getElementById('mouseButton_gclient'),
|
||||
screenshotButton: document.getElementById('takeScreenshot_gclient'),
|
||||
oskButton: document.getElementById('oskButton_gclient'),
|
||||
sendCtrlAltDelButton: document.getElementById('sendCtrlAltDelButton'),
|
||||
mouseButton: document.getElementById('mouseButton'),
|
||||
screenshotButton: document.getElementById('takeScreenshot'),
|
||||
oskButton: document.getElementById('oskButton'),
|
||||
};
|
||||
|
||||
var throttleResizeFunction = Utils.throttle(containerResized, 250);
|
||||
|
@ -26,7 +26,7 @@ define(function(require) {
|
||||
loadLayouts();
|
||||
changeLayout(DEFAULT_LAYOUT);
|
||||
|
||||
$('#osk-container').draggable();
|
||||
// $('#osk-container').draggable();
|
||||
|
||||
function loadLayouts() {
|
||||
$('#osk-qwerty').empty();
|
||||
|
@ -27,6 +27,10 @@ define(function(require) {
|
||||
context.empty()
|
||||
info && context.append(TemplateInfo(info))
|
||||
UtilsFoundation.update(context);
|
||||
|
||||
if (info && info.name) {
|
||||
document.title = info.name;
|
||||
}
|
||||
}
|
||||
|
||||
function decodeInfoConnection(info_encode) {
|
||||
|
@ -19,27 +19,15 @@ define(function(require) {
|
||||
Sunstone = require('sunstone'),
|
||||
Config = require("sunstone-config"),
|
||||
OpenNebulaVM = require("opennebula/vm"),
|
||||
Vnc = require('utils/vnc'),
|
||||
Spice = require('utils/spice'),
|
||||
FireedgeValidator = require('utils/fireedge-validator'),
|
||||
Notifier = require('utils/notifier');
|
||||
|
||||
function _callSpice(data) {
|
||||
if (!Spice.lockStatus() && data.hasOwnProperty('id')) {
|
||||
Spice.lock();
|
||||
Sunstone.runAction('VM.startspice_action', String(data.id));
|
||||
} else {
|
||||
Notifier.notifyError(Locale.tr('SPICE Connection in progress'));
|
||||
}
|
||||
if (data.hasOwnProperty('id')) Sunstone.runAction('VM.startspice_action', String(data.id));
|
||||
}
|
||||
|
||||
function _callVNC(data) {
|
||||
if (!Vnc.lockStatus() && data.hasOwnProperty('id')) {
|
||||
Vnc.lock();
|
||||
Sunstone.runAction('VM.startvnc_action', String(data.id));
|
||||
} else {
|
||||
Notifier.notifyError(Locale.tr('VNC Connection in progress'));
|
||||
}
|
||||
if (data.hasOwnProperty('id')) Sunstone.runAction('VM.startvnc_action', String(data.id));
|
||||
}
|
||||
|
||||
function _callSaveRDP(data) {
|
||||
|
@ -1,106 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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('spice-main');
|
||||
|
||||
var Config = require('sunstone-config');
|
||||
var UtilsConnection = require("utils/info-connection/utils");
|
||||
|
||||
var _lock = false;
|
||||
var _sc;
|
||||
|
||||
return {
|
||||
'lockStatus': lockStatus,
|
||||
'lock': lock,
|
||||
'unlock': unlock,
|
||||
'spiceCallback': spiceCallback,
|
||||
'disconnect': disconnect
|
||||
}
|
||||
|
||||
function lockStatus() {
|
||||
return _lock;
|
||||
}
|
||||
|
||||
function lock() {
|
||||
_lock = true;
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
_lock = false;
|
||||
}
|
||||
|
||||
function spice_error() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
try {
|
||||
if (_sc) {
|
||||
_sc.stop();
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function agent_connected(sc) {
|
||||
window.addEventListener('resize', handle_resize);
|
||||
window.spice_connection = this;
|
||||
|
||||
resize_helper(this);
|
||||
}
|
||||
|
||||
function spiceCallback(response) {
|
||||
var scheme = "ws://";
|
||||
if (Config.vncWSS == "yes") {
|
||||
scheme = "wss://";
|
||||
}
|
||||
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
|
||||
disconnect()
|
||||
|
||||
uri = scheme + host + ":" + port + "?token=" + token;
|
||||
|
||||
try {
|
||||
_sc = new SpiceMainConn({uri: uri, screen_id: "spice-screen", dump_id: "debug-div",
|
||||
message_id: "message-div", password: password, onerror: spice_error, onagent: agent_connected});
|
||||
}
|
||||
catch (e) {
|
||||
disconnect()
|
||||
}
|
||||
|
||||
var url = "spice?";
|
||||
url += "host=" + host;
|
||||
url += "&port=" + port;
|
||||
url += "&token=" + token;
|
||||
url += "&password=" + password;
|
||||
url += "&encrypt=" + config['user_config']['vnc_wss'];
|
||||
url += "&info=" + info;
|
||||
|
||||
$("#open_in_a_new_window_spice").attr('href', url);
|
||||
}
|
||||
});
|
@ -1,153 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
var WMKS = require("wmks");
|
||||
var Config = require("sunstone-config");
|
||||
var UtilsConnection = require("utils/info-connection/utils");
|
||||
|
||||
var _lock = false;
|
||||
var _wmks;
|
||||
var _is_encrypted = "";
|
||||
var vm_name = ""
|
||||
|
||||
return {
|
||||
"lockStatus": lockStatus,
|
||||
"lock": lock,
|
||||
"unlock": unlock,
|
||||
"vmrcCallback": vmrcCallback,
|
||||
"disconnect": disconnect,
|
||||
"sendCtrlAltDel": sendCtrlAltDel,
|
||||
"updateScreen": updateScreen
|
||||
};
|
||||
|
||||
function lockStatus() {
|
||||
return _lock;
|
||||
}
|
||||
|
||||
function lock() {
|
||||
_lock = true;
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
_lock = false;
|
||||
}
|
||||
|
||||
function setStatus(message = "", status = "") {
|
||||
$(".VMRC_message").text(message);
|
||||
$("#VMRC_status").text(status);
|
||||
}
|
||||
|
||||
function connected() {
|
||||
setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " + vm_name);
|
||||
}
|
||||
|
||||
function disconnectedFromServer(e) {
|
||||
if (e.detail.clean) {
|
||||
setStatus(null, "VMRC " + _wmks.connectionState + " (" + _is_encrypted + ") to: " + vm_name);
|
||||
} else {
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
}
|
||||
|
||||
function render(ticket, host_vmrc, port_vmrc, response){
|
||||
var hostname = window.location.hostname;
|
||||
var port = window.location.port;
|
||||
var protocol = window.location.protocol;
|
||||
var fireedge_endpoint = Config.publicFireedgeEndpoint.split("//")[1];
|
||||
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);
|
||||
|
||||
// set vm name on title
|
||||
vm_name = (info_decode && info_decode.name) ? info_decode.name : "";
|
||||
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
|
||||
// Content of response.data
|
||||
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";
|
||||
} else {
|
||||
URL = "ws";
|
||||
_is_encrypted ="unencrypted";
|
||||
}
|
||||
|
||||
URL += "://" + fireedge_endpoint + "/";
|
||||
|
||||
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', {})
|
||||
.register(WMKS.CONST.Events.CONNECTION_STATE_CHANGE,
|
||||
function (_, data) {
|
||||
if (data.state === WMKS.CONST.ConnectionState.CONNECTED) {
|
||||
console.log("connection state change: connected");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
_wmks.eventHandlers["connectionstatechange"].push(connected);
|
||||
_wmks.eventHandlers["disconnect"] = disconnectedFromServer;
|
||||
|
||||
_wmks.connect(URL);
|
||||
|
||||
$("#VMRC_buttons #open_in_a_new_window").attr("href",link);
|
||||
|
||||
} catch (err) {
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
}
|
||||
|
||||
function vmrcCallback(response) {
|
||||
if (response.data) {
|
||||
render(
|
||||
response.data.ticket,
|
||||
response.data.host,
|
||||
response.data.port,
|
||||
response
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (_wmks) { _wmks.disconnect(); }
|
||||
}
|
||||
|
||||
function sendCtrlAltDel() {
|
||||
if (_wmks) { _wmks.sendCAD(); }
|
||||
}
|
||||
|
||||
function updateScreen() {
|
||||
_wmks.updateScreen();
|
||||
}
|
||||
});
|
@ -1,136 +0,0 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* 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) {
|
||||
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 = "";
|
||||
|
||||
return {
|
||||
"lockStatus": lockStatus,
|
||||
"lock": lock,
|
||||
"unlock": unlock,
|
||||
"vncCallback": vncCallback,
|
||||
"disconnect": disconnect,
|
||||
"sendCtrlAltDel": sendCtrlAltDel
|
||||
};
|
||||
|
||||
function lockStatus() {
|
||||
return _lock;
|
||||
}
|
||||
|
||||
function lock() {
|
||||
_lock = true;
|
||||
}
|
||||
|
||||
function unlock() {
|
||||
_lock = false;
|
||||
}
|
||||
|
||||
function setStatus(message="", status=""){
|
||||
_message = message;
|
||||
_status = status;
|
||||
$(".NOVNC_message").text(_message);
|
||||
$("#VNC_status").text(_status);
|
||||
}
|
||||
|
||||
function connected(){
|
||||
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: " + _rfb._fb_name);
|
||||
} else {
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
}
|
||||
|
||||
function desktopNameChange(e) {
|
||||
if (e.detail.name) {
|
||||
setStatus(null, "VNC " + _rfb._rfb_connection_state + " (" + _is_encrypted + ") to: " + e.detail.name);
|
||||
}
|
||||
}
|
||||
|
||||
function credentialsRequired(e) {
|
||||
setStatus("Something went wrong, more credentials must be given to continue", "Failed");
|
||||
}
|
||||
|
||||
function vncCallback(response) {
|
||||
var URL = "";
|
||||
var proxy_port = Config.vncProxyPort;
|
||||
|
||||
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 } } : {};
|
||||
|
||||
if (protocol === "https:") {
|
||||
URL = "wss";
|
||||
_is_encrypted ="encrypted";
|
||||
} else {
|
||||
URL = "ws";
|
||||
_is_encrypted ="unencrypted";
|
||||
}
|
||||
URL += "://" + hostname;
|
||||
URL += ":" + proxy_port;
|
||||
URL += "?host=" + proxy_host;
|
||||
URL += "&port=" + proxy_port;
|
||||
URL += "&token=" + token;
|
||||
URL += "&encrypt=" + Config.vncWSS;
|
||||
URL += "&info=" + response.info;
|
||||
|
||||
if (!Config.requestVNCPassword) {
|
||||
URL += "&password=" + pw;
|
||||
}
|
||||
var re = new RegExp("^(ws|wss):\\/\\/[\\w\\D]*?\\?", "gi");
|
||||
var link = URL.replace(re, protocol + "//" + hostname + ":" + port + "/vnc?");
|
||||
|
||||
try{
|
||||
_rfb = new RFB(document.querySelector("#VNC_canvas"), URL, rfbConfig);
|
||||
_rfb.addEventListener("connect", connected);
|
||||
_rfb.addEventListener("disconnect", disconnectedFromServer);
|
||||
_rfb.addEventListener("desktopname", desktopNameChange);
|
||||
_rfb.addEventListener("credentialsrequired", credentialsRequired);
|
||||
}catch(err){
|
||||
setStatus("Something went wrong, connection is closed", "Failed");
|
||||
}
|
||||
|
||||
$("#open_in_a_new_window").attr("href", link);
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (_rfb) { _rfb.disconnect(); }
|
||||
}
|
||||
|
||||
function sendCtrlAltDel() {
|
||||
if (_rfb) { _rfb.sendCtrlAltDel(); }
|
||||
}
|
||||
});
|
327
src/sunstone/public/css/guac-custom.css
Normal file
327
src/sunstone/public/css/guac-custom.css
Normal file
@ -0,0 +1,327 @@
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-family: Helvetica;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
html {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
.remote-buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
display: inline-flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
background-color: rgb(40, 40, 40);
|
||||
}
|
||||
|
||||
.guacamole-main{
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.guacamole-status {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.remote-logo{
|
||||
border-radius: 50%;
|
||||
background-color: #282828;
|
||||
padding: 0.4em;
|
||||
margin-right: 1em;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.guacamole-state{
|
||||
white-space: nowrap;
|
||||
max-width: 20em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
|
||||
/* left-align the status text on lower resolutions */
|
||||
@media screen and (max-width: 800px){
|
||||
.guacamole-status {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: auto;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.guacamole-main {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
place-content: center;
|
||||
background-color: #282828;
|
||||
}
|
||||
|
||||
.guacamole-main > div {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.guacamole-main .guacamole-display {
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.osk-container {
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.59);
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
left: 0;
|
||||
display: none;
|
||||
border: 1px solid #acacac;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 20px #acacac;
|
||||
}
|
||||
|
||||
.osk-container-header {
|
||||
background: linear-gradient(to top, #ebebeb, #d5d5d5);
|
||||
color: #4d494d;
|
||||
font-size: 11pt;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
border-top: 1px solid #f3f1f3;
|
||||
border-bottom: 1px solid #b1aeb1;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
}
|
||||
|
||||
.osk-container-header .buttons {
|
||||
padding-left: 8px;
|
||||
padding-top: 3px;
|
||||
float: left;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.osk-container-header .buttons .close {
|
||||
background: #ff5c5c;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.osk-container-header .layouts {
|
||||
padding-right: 8px;
|
||||
padding-top: 3px;
|
||||
float: right;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
|
||||
.guac-keyboard {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: default;
|
||||
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.guac-keyboard,
|
||||
.guac-keyboard * {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key-container {
|
||||
display: inline-block;
|
||||
margin: 0.05em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key {
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #444;
|
||||
|
||||
border: 0.125em solid #666;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
color: white;
|
||||
font-size: 40%;
|
||||
font-weight: lighter;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.25),
|
||||
-1px 1px 0 rgba(0, 0, 0, 0.25),
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.25);
|
||||
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.highlight {
|
||||
background: #666;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
/* Align some keys to the left */
|
||||
.guac-keyboard .guac-keyboard-key-caps,
|
||||
.guac-keyboard .guac-keyboard-key-enter,
|
||||
.guac-keyboard .guac-keyboard-key-tab,
|
||||
.guac-keyboard .guac-keyboard-key-lalt,
|
||||
.guac-keyboard .guac-keyboard-key-ralt,
|
||||
.guac-keyboard .guac-keyboard-key-alt-gr,
|
||||
.guac-keyboard .guac-keyboard-key-lctrl,
|
||||
.guac-keyboard .guac-keyboard-key-rctrl,
|
||||
.guac-keyboard .guac-keyboard-key-lshift,
|
||||
.guac-keyboard .guac-keyboard-key-rshift {
|
||||
text-align: left;
|
||||
padding-left: 0.75em;
|
||||
}
|
||||
|
||||
/* Active shift */
|
||||
.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key-rshift,
|
||||
.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key-lshift,
|
||||
|
||||
/* Active ctrl */
|
||||
.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key-rctrl,
|
||||
.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key-lctrl,
|
||||
|
||||
/* Active alt */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key-ralt,
|
||||
.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key-lalt,
|
||||
|
||||
/* Active alt-gr */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr .guac-keyboard-key-alt-gr,
|
||||
|
||||
/* Active caps */
|
||||
.guac-keyboard.guac-keyboard-modifier-caps .guac-keyboard-key-caps,
|
||||
|
||||
/* Active super */
|
||||
.guac-keyboard.guac-keyboard-modifier-super .guac-keyboard-key-super {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.guac-keyboard-pressed {
|
||||
background: #822;
|
||||
border-color: #D44;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-alpha,
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-movement {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-main {
|
||||
|
||||
/* IE10 */
|
||||
display: -ms-flexbox;
|
||||
-ms-flex-align: stretch;
|
||||
-ms-flex-direction: row;
|
||||
|
||||
/* Ancient Mozilla */
|
||||
display: -moz-box;
|
||||
-moz-box-align: stretch;
|
||||
-moz-box-orient: horizontal;
|
||||
|
||||
/* Ancient WebKit */
|
||||
display: -webkit-box;
|
||||
-webkit-box-align: stretch;
|
||||
-webkit-box-orient: horizontal;
|
||||
|
||||
/* Old WebKit */
|
||||
display: -webkit-flex;
|
||||
-webkit-align-items: stretch;
|
||||
-webkit-flex-direction: row;
|
||||
|
||||
/* W3C */
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: row;
|
||||
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-movement {
|
||||
-ms-flex: 1 1 auto;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-gap {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Hide keycaps requiring modifiers which are NOT currently active. */
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-caps)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-caps,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-shift)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-shift,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-alt-gr)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-alt-gr,
|
||||
|
||||
/* Hide keycaps NOT requiring modifiers which ARE currently active, where that
|
||||
modifier is used to determine which cap is displayed for the current key. */
|
||||
.guac-keyboard.guac-keyboard-modifier-shift
|
||||
.guac-keyboard-key.guac-keyboard-uses-shift
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-shift),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-caps
|
||||
.guac-keyboard-key.guac-keyboard-uses-caps
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-caps),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr
|
||||
.guac-keyboard-key.guac-keyboard-uses-alt-gr
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-alt-gr) {
|
||||
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
/* Fade out keys which do not use AltGr if AltGr is active */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr
|
||||
.guac-keyboard-key:not(.guac-keyboard-uses-alt-gr):not(.guac-keyboard-key-alt-gr) {
|
||||
opacity: 0.5;
|
||||
}
|
@ -112,6 +112,7 @@ html {
|
||||
display: inline-flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
background-color: rgb(40, 40, 40);
|
||||
}
|
||||
#noVNC_screen > #VNC_canvas{
|
||||
flex-grow: 1;
|
||||
@ -392,6 +393,28 @@ html {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.remote_logo{
|
||||
border-radius: 50%;
|
||||
background-color: #282828;
|
||||
padding: 0.4em;
|
||||
margin-right: 1em;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
#noVNC_status_msg{
|
||||
white-space: nowrap;
|
||||
max-width: 20em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#showExtraKeysButton { display: none; }
|
||||
#toggleCtrlButton { display: inline; }
|
||||
#toggleAltButton { display: inline; }
|
||||
|
@ -55,3 +55,29 @@ main {
|
||||
.spice-message-error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#VMRC_buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.remote_logo{
|
||||
border-radius: 50%;
|
||||
background-color: #282828;
|
||||
padding: 0.4em;
|
||||
margin-right: 1em;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
#spice_status_msg{
|
||||
white-space: nowrap;
|
||||
max-width: 20em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
503
src/sunstone/public/css/vmrc-custom.css
Normal file
503
src/sunstone/public/css/vmrc-custom.css
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Based on the VMRC base CSS
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
||||
* VMRC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-family: Helvetica;
|
||||
/*Background image with light grey curve.*/
|
||||
height:100%;
|
||||
}
|
||||
|
||||
html {
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#VMRC_controls ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#VMRC_controls li {
|
||||
padding-bottom:8px;
|
||||
}
|
||||
|
||||
#VMRC_host {
|
||||
width:150px;
|
||||
}
|
||||
#VMRC_port {
|
||||
width: 80px;
|
||||
}
|
||||
#VMRC_password {
|
||||
width: 150px;
|
||||
}
|
||||
#VMRC_path {
|
||||
width: 100px;
|
||||
}
|
||||
#VMRC_connect_button {
|
||||
width: 110px;
|
||||
float:right;
|
||||
}
|
||||
|
||||
#VMRC_buttons {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#VMRC_view_drag_button {
|
||||
display: none;
|
||||
}
|
||||
#VMRC_xvp_buttons {
|
||||
display: none;
|
||||
}
|
||||
#VMRC_mobile_buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#VMRC_extra_keys {
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.VMRC-buttons-left {
|
||||
float: left;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.VMRC-buttons-right {
|
||||
float:right;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#VMRC_status {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#VMRC_settings_menu {
|
||||
margin: 3px;
|
||||
text-align: left;
|
||||
}
|
||||
#VMRC_settings_menu ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#VMRC_apply {
|
||||
float:right;
|
||||
}
|
||||
|
||||
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect
|
||||
* scaling will occur. Canvas resizes to remote VNC settings */
|
||||
#VMRC_screen_pad {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 36px;
|
||||
}
|
||||
#VMRC_screen {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
background-color: rgb(40, 40, 40);
|
||||
}
|
||||
#VMRC_screen > #VNC_canvas{
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#VMRC_container, #VMRC_canvas {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#VMRC_canvas {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#VNC_clipboard_clear_button {
|
||||
float:right;
|
||||
}
|
||||
#VNC_clipboard_text {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#VMRC_clipboard_clear_button {
|
||||
float:right;
|
||||
}
|
||||
|
||||
/*Bubble contents divs*/
|
||||
#VMRC_settings {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:20px;
|
||||
position:fixed;
|
||||
}
|
||||
|
||||
#VMRC_controls {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:12px;
|
||||
position:fixed;
|
||||
}
|
||||
#VMRC_controls.top:after {
|
||||
right:15px;
|
||||
}
|
||||
|
||||
#VMRC_description {
|
||||
display:none;
|
||||
position:fixed;
|
||||
|
||||
margin-top:73px;
|
||||
right:20px;
|
||||
left:20px;
|
||||
padding:15px;
|
||||
color:#000;
|
||||
|
||||
border:2px solid #E0E0E0;
|
||||
}
|
||||
|
||||
#VMRC_popup_status_panel {
|
||||
display:none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
|
||||
margin:15px;
|
||||
margin-top:60px;
|
||||
padding:15px;
|
||||
width:auto;
|
||||
|
||||
text-align:center;
|
||||
font-weight:bold;
|
||||
word-wrap:break-word;
|
||||
background:rgba(0,0,0,0.65);
|
||||
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
}
|
||||
|
||||
#VMRC_xvp {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:30px;
|
||||
position:fixed;
|
||||
}
|
||||
#VMRC_xvp.top:after {
|
||||
right:125px;
|
||||
}
|
||||
|
||||
#VMRC_clipboard {
|
||||
display:none;
|
||||
margin-top:73px;
|
||||
right:30px;
|
||||
position:fixed;
|
||||
}
|
||||
#VMRC_clipboard.top:after {
|
||||
right:85px;
|
||||
}
|
||||
|
||||
#keyboardinput {
|
||||
width:1px;
|
||||
height:1px;
|
||||
background-color:#fff;
|
||||
color:#fff;
|
||||
border:0;
|
||||
position: relative;
|
||||
left: -40px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Advanced Styling
|
||||
*/
|
||||
|
||||
.VMRC_status_normal {
|
||||
color: #b2bdcd; /* Old browsers */
|
||||
}
|
||||
.VMRC_status_error {
|
||||
color: #f04040; /* Old browsers */
|
||||
}
|
||||
.VMRC_status_warn {
|
||||
color: #f0f040; /* Old browsers */
|
||||
}
|
||||
|
||||
/* Control bar */
|
||||
#VMRC-control-bar {
|
||||
position:fixed;
|
||||
|
||||
display:block;
|
||||
height:36px;
|
||||
left:0;
|
||||
top:0;
|
||||
width:100%;
|
||||
z-index:200;
|
||||
}
|
||||
|
||||
.VMRC_status_button {
|
||||
padding: 4px 4px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #869dbc;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
background: #b2bdcd; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
|
||||
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||
}
|
||||
|
||||
.VMRC_status_button_selected {
|
||||
padding: 4px 4px;
|
||||
vertical-align: middle;
|
||||
border:1px solid #4366a9;
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
background: #779ced; /* Old browsers */
|
||||
background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */
|
||||
background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */
|
||||
background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */
|
||||
background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */
|
||||
background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */
|
||||
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
|
||||
}
|
||||
|
||||
|
||||
/*Settings Bubble*/
|
||||
.triangle-right {
|
||||
position:relative;
|
||||
padding:15px;
|
||||
margin:1em 0 3em;
|
||||
color:#fff;
|
||||
background:#fff; /* default background for browsers without gradient support */
|
||||
/* css3 */
|
||||
/*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698));
|
||||
background:-moz-linear-gradient(#2e88c4, #075698);
|
||||
background:-o-linear-gradient(#2e88c4, #075698);
|
||||
background:linear-gradient(#2e88c4, #075698);*/
|
||||
-webkit-border-radius:10px;
|
||||
-moz-border-radius:10px;
|
||||
border-radius:10px;
|
||||
color:#000;
|
||||
border:2px solid #E0E0E0;
|
||||
}
|
||||
|
||||
.triangle-right.top:after {
|
||||
border-color: transparent #E0E0E0;
|
||||
border-width: 20px 20px 0 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: 50px;
|
||||
top: -20px;
|
||||
}
|
||||
|
||||
.triangle-right:after {
|
||||
content:"";
|
||||
position:absolute;
|
||||
bottom:-20px; /* value = - border-top-width - border-bottom-width */
|
||||
left:50px; /* controls horizontal position */
|
||||
border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */
|
||||
border-style:solid;
|
||||
border-color:#E0E0E0 transparent;
|
||||
/* reduce the damage in FF3.0 */
|
||||
display:block;
|
||||
width:0;
|
||||
}
|
||||
|
||||
.triangle-right.top:after {
|
||||
top:-40px; /* value = - border-top-width - border-bottom-width */
|
||||
right:50px; /* controls horizontal position */
|
||||
bottom:auto;
|
||||
left:auto;
|
||||
border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */
|
||||
border-color:transparent #E0E0E0;
|
||||
}
|
||||
|
||||
/*Default VMRC logo.*/
|
||||
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
|
||||
@font-face {
|
||||
font-family: 'Orbitron';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('?'), url('Orbitron700.woff') format('woff'),
|
||||
url('Orbitron700.ttf') format('truetype');
|
||||
}
|
||||
|
||||
#VMRC_logo {
|
||||
margin-top: 170px;
|
||||
margin-left: 10px;
|
||||
color:yellow;
|
||||
text-align:left;
|
||||
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
|
||||
line-height:90%;
|
||||
text-shadow:
|
||||
5px 5px 0 #000,
|
||||
-1px -1px 0 #000,
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
}
|
||||
|
||||
|
||||
#VMRC_logo span{
|
||||
color:green;
|
||||
}
|
||||
|
||||
/* ----------------------------------------
|
||||
* Media sizing
|
||||
* ----------------------------------------
|
||||
*/
|
||||
|
||||
|
||||
.VMRC_status_button {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#VMRC_clipboard_text {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
#VMRC_logo {
|
||||
font-size: 180px;
|
||||
}
|
||||
|
||||
.VMRC-buttons-left {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.VMRC-buttons-right {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#VMRC_status {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#wmksContainer {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
.remote_logo{
|
||||
border-radius: 50%;
|
||||
background-color: #282828;
|
||||
padding: 0.4em;
|
||||
margin-right: 1em;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
#VMRC_status_msg{
|
||||
white-space: nowrap;
|
||||
max-width: 20em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.container{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#showExtraKeysButton { display: none; }
|
||||
#toggleCtrlButton { display: inline; }
|
||||
#toggleAltButton { display: inline; }
|
||||
#sendTabButton { display: inline; }
|
||||
#sendEscButton { display: inline; }
|
||||
|
||||
/* left-align the status text on lower resolutions */
|
||||
@media screen and (max-width: 800px){
|
||||
#VMRC_status {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
width: auto;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px){
|
||||
#VMRC_clipboard_text {
|
||||
width: 410px;
|
||||
}
|
||||
#VMRC_logo {
|
||||
font-size: 150px;
|
||||
}
|
||||
.VMRC_status_button {
|
||||
font-size: 10px;
|
||||
}
|
||||
.VMRC-buttons-left {
|
||||
padding-left: 0;
|
||||
}
|
||||
.VMRC-buttons-right {
|
||||
padding-right: 0;
|
||||
}
|
||||
/* collapse the extra keys on lower resolutions */
|
||||
#showExtraKeysButton {
|
||||
display: inline;
|
||||
}
|
||||
#toggleCtrlButton {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 0;
|
||||
}
|
||||
#toggleAltButton {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 65px;
|
||||
left: 0;
|
||||
}
|
||||
#sendTabButton {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 0;
|
||||
}
|
||||
#sendEscButton {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 135px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 321px) and (max-width: 480px) {
|
||||
#VMRC_clipboard_text {
|
||||
width: 250px;
|
||||
}
|
||||
#VMRC_logo {
|
||||
font-size: 110px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
.VMRC_status_button {
|
||||
font-size: 9px;
|
||||
}
|
||||
#VMRC_clipboard_text {
|
||||
width: 220px;
|
||||
}
|
||||
#VMRC_logo {
|
||||
font-size: 90px;
|
||||
}
|
||||
}
|
BIN
src/sunstone/public/images/remote_console/guacamole.png
Normal file
BIN
src/sunstone/public/images/remote_console/guacamole.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
BIN
src/sunstone/public/images/remote_console/noVNC.png
Normal file
BIN
src/sunstone/public/images/remote_console/noVNC.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
src/sunstone/public/images/remote_console/spice.png
Normal file
BIN
src/sunstone/public/images/remote_console/spice.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src/sunstone/public/images/remote_console/vmrc.png
Normal file
BIN
src/sunstone/public/images/remote_console/vmrc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.0 KiB |
@ -159,263 +159,4 @@ progress{
|
||||
background-color: $busy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.guacamole-main {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
place-content: center;
|
||||
background-color: #282828;
|
||||
& > div {
|
||||
z-index: 1;
|
||||
}
|
||||
& .guacamole-display {
|
||||
cursor: none;
|
||||
}
|
||||
}
|
||||
|
||||
#guacVMDialog {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.osk-container {
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.59);
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
left: 0;
|
||||
display: none;
|
||||
border: 1px solid #acacac;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 0 20px #acacac;
|
||||
& > .osk-container-header {
|
||||
background: linear-gradient(to top, #ebebeb, #d5d5d5);
|
||||
color: #4d494d;
|
||||
font-size: 11pt;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
border-top: 1px solid #f3f1f3;
|
||||
border-bottom: 1px solid #b1aeb1;
|
||||
border-top-left-radius: 6px;
|
||||
border-top-right-radius: 6px;
|
||||
& .buttons {
|
||||
padding-left: 8px;
|
||||
padding-top: 3px;
|
||||
float: left;
|
||||
line-height: 0;
|
||||
& .close {
|
||||
background: #ff5c5c;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
& .layouts {
|
||||
padding-right: 8px;
|
||||
padding-top: 3px;
|
||||
float: right;
|
||||
line-height: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.guac-keyboard {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
cursor: default;
|
||||
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.guac-keyboard,
|
||||
.guac-keyboard * {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key-container {
|
||||
display: inline-block;
|
||||
margin: 0.05em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key {
|
||||
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
background: #444;
|
||||
|
||||
border: 0.125em solid #666;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
color: white;
|
||||
font-size: 40%;
|
||||
font-weight: lighter;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
|
||||
text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.25),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.25),
|
||||
-1px 1px 0 rgba(0, 0, 0, 0.25),
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.25);
|
||||
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.highlight {
|
||||
background: #666;
|
||||
border-color: #666;
|
||||
}
|
||||
|
||||
/* Align some keys to the left */
|
||||
.guac-keyboard .guac-keyboard-key-caps,
|
||||
.guac-keyboard .guac-keyboard-key-enter,
|
||||
.guac-keyboard .guac-keyboard-key-tab,
|
||||
.guac-keyboard .guac-keyboard-key-lalt,
|
||||
.guac-keyboard .guac-keyboard-key-ralt,
|
||||
.guac-keyboard .guac-keyboard-key-alt-gr,
|
||||
.guac-keyboard .guac-keyboard-key-lctrl,
|
||||
.guac-keyboard .guac-keyboard-key-rctrl,
|
||||
.guac-keyboard .guac-keyboard-key-lshift,
|
||||
.guac-keyboard .guac-keyboard-key-rshift {
|
||||
text-align: left;
|
||||
padding-left: 0.75em;
|
||||
}
|
||||
|
||||
/* Active shift */
|
||||
.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key-rshift,
|
||||
.guac-keyboard.guac-keyboard-modifier-shift .guac-keyboard-key-lshift,
|
||||
|
||||
/* Active ctrl */
|
||||
.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key-rctrl,
|
||||
.guac-keyboard.guac-keyboard-modifier-control .guac-keyboard-key-lctrl,
|
||||
|
||||
/* Active alt */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key-ralt,
|
||||
.guac-keyboard.guac-keyboard-modifier-alt .guac-keyboard-key-lalt,
|
||||
|
||||
/* Active alt-gr */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr .guac-keyboard-key-alt-gr,
|
||||
|
||||
/* Active caps */
|
||||
.guac-keyboard.guac-keyboard-modifier-caps .guac-keyboard-key-caps,
|
||||
|
||||
/* Active super */
|
||||
.guac-keyboard.guac-keyboard-modifier-super .guac-keyboard-key-super {
|
||||
background: #882;
|
||||
border-color: #DD4;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-key.guac-keyboard-pressed {
|
||||
background: #822;
|
||||
border-color: #D44;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group {
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-alpha,
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-movement {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-main {
|
||||
|
||||
/* IE10 */
|
||||
display: -ms-flexbox;
|
||||
-ms-flex-align: stretch;
|
||||
-ms-flex-direction: row;
|
||||
|
||||
/* Ancient Mozilla */
|
||||
display: -moz-box;
|
||||
-moz-box-align: stretch;
|
||||
-moz-box-orient: horizontal;
|
||||
|
||||
/* Ancient WebKit */
|
||||
display: -webkit-box;
|
||||
-webkit-box-align: stretch;
|
||||
-webkit-box-orient: horizontal;
|
||||
|
||||
/* Old WebKit */
|
||||
display: -webkit-flex;
|
||||
-webkit-align-items: stretch;
|
||||
-webkit-flex-direction: row;
|
||||
|
||||
/* W3C */
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-direction: row;
|
||||
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-group.guac-keyboard-movement {
|
||||
-ms-flex: 1 1 auto;
|
||||
-moz-box-flex: 1;
|
||||
-webkit-box-flex: 1;
|
||||
-webkit-flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.guac-keyboard .guac-keyboard-gap {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Hide keycaps requiring modifiers which are NOT currently active. */
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-caps)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-caps,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-shift)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-shift,
|
||||
|
||||
.guac-keyboard:not(.guac-keyboard-modifier-alt-gr)
|
||||
.guac-keyboard-cap.guac-keyboard-requires-alt-gr,
|
||||
|
||||
/* Hide keycaps NOT requiring modifiers which ARE currently active, where that
|
||||
modifier is used to determine which cap is displayed for the current key. */
|
||||
.guac-keyboard.guac-keyboard-modifier-shift
|
||||
.guac-keyboard-key.guac-keyboard-uses-shift
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-shift),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-caps
|
||||
.guac-keyboard-key.guac-keyboard-uses-caps
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-caps),
|
||||
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr
|
||||
.guac-keyboard-key.guac-keyboard-uses-alt-gr
|
||||
.guac-keyboard-cap:not(.guac-keyboard-requires-alt-gr) {
|
||||
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
/* Fade out keys which do not use AltGr if AltGr is active */
|
||||
.guac-keyboard.guac-keyboard-modifier-alt-gr
|
||||
.guac-keyboard-key:not(.guac-keyboard-uses-alt-gr):not(.guac-keyboard-key-alt-gr) {
|
||||
opacity: 0.5;
|
||||
}
|
@ -550,7 +550,7 @@ before do
|
||||
@request_body = request.body.read
|
||||
request.body.rewind
|
||||
|
||||
unless %w(/ /login /vnc /spice /version /webauthn_options_for_get /ws /vmrc).include?(request.path)
|
||||
unless %w(/ /login /vnc /spice /version /webauthn_options_for_get /ws /vmrc /guac).include?(request.path)
|
||||
halt [401, "csrftoken"] unless authorized? && valid_csrftoken?
|
||||
end
|
||||
|
||||
@ -747,6 +747,20 @@ get '/vnc' do
|
||||
end
|
||||
end
|
||||
|
||||
get '/guac' do
|
||||
content_type 'text/html', :charset => 'utf-8'
|
||||
if !authorized?
|
||||
erb :login
|
||||
else
|
||||
erb :guac, :locals => {
|
||||
:logos_conf => $conf[:locals][:logos_conf],
|
||||
:oned_conf => $conf[:locals][:oned_conf],
|
||||
:support => $conf[:locals][:support],
|
||||
:upgrade => $conf[:locals][:upgrade]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
get '/vmrc' do
|
||||
content_type 'text/html', :charset => 'utf-8'
|
||||
if !authorized?
|
||||
|
119
src/sunstone/views/guac.erb
Normal file
119
src/sunstone/views/guac.erb
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<link rel="apple-touch-startup-image" href="images/screen_320x460.png" />
|
||||
<link rel="apple-touch-icon" href="images/screen_57x57.png">
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" type="text/css" href="css/app.css" title="plain">
|
||||
<link rel="stylesheet" type="text/css" href="css/guac-custom.css" title="plain">
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<div style="background: #f7f7f7; padding: 1.5em 1.5em 0.8em;">
|
||||
<div style="max-width: 1250px; margin: 0 auto;">
|
||||
<div style="display: flex; align-items: center;">
|
||||
<img src="images/one_small_logo.png" style="height:40px;">
|
||||
<h5 class="guacamole-status">
|
||||
<div class="container">
|
||||
<div class="remote-logo">
|
||||
<img src="images/remote_console/guacamole.png">
|
||||
</div>
|
||||
<div id="guacamole-state" class="guacamole-state">
|
||||
<span id="guacamole-loading">
|
||||
<i class="fas fa-spinner fa-spin"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</h5>
|
||||
<div class="remote-buttons">
|
||||
<button class="button alert" id="sendCtrlAltDelButton">
|
||||
Send CtrlAltDel
|
||||
</button>
|
||||
<button class="button primary" id="oskButton" style="display: none !important;">
|
||||
<i class="fas fa-keyboard fa-fw"></i>
|
||||
</button>
|
||||
<button class="button primary" id="mouseButton">
|
||||
<i class="fas fa-mouse-pointer fa-fw"></i>
|
||||
</button>
|
||||
<button class="button primary" id="takeScreenshot">
|
||||
<i class="fas fa-camera fa-fw" title="Take screenshot"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="guacVNC_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="guacamole-main" class="guacamole-main">
|
||||
<div id="guacamole-display" class="guacamole-display"></div>
|
||||
</div>
|
||||
<!-- On-screen keyboard -->
|
||||
<div class="osk-container" id="osk-container">
|
||||
<div class="osk-container-header" id="osk-container-header">
|
||||
<div class="buttons">
|
||||
<button class="close" id="osk-close">x</button>
|
||||
</div>
|
||||
<div class="layouts">
|
||||
<select id="osk-qwerty"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="osk" id="osk"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="dist/console/guacamole.js?v=<%= OpenNebula::VERSION %>"></script>
|
||||
<% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %>
|
||||
<script type="text/javascript">
|
||||
var csrftoken = '<%= session[:csrftoken] %>';
|
||||
var view = JSON.parse('<%= view.to_json %>')
|
||||
var available_views = JSON.parse('["<%=
|
||||
$views_config.available_views(session[:user], session[:user_gname]).join('","')
|
||||
%>"]')
|
||||
var all_labels = JSON.parse('["<%=
|
||||
$views_config.get_all_labels(session[:user_gname]).join('","')
|
||||
%>"]')
|
||||
var all_views = JSON.parse('["<%=
|
||||
$views_config.get_all_views.join('","')
|
||||
%>"]')
|
||||
if ('<%= $conf[:addons] %>'){
|
||||
var addons = JSON.parse('<%= $conf[:addons].to_json %>');
|
||||
}
|
||||
var config = {
|
||||
'user_config' : {
|
||||
'lang' : '<%= session[:lang] %>',
|
||||
'vnc_wss' : '<%= session[:vnc_wss] %>',
|
||||
'table_order' : '<%= session[:table_order] %>',
|
||||
'default_view' : '<%= session[:default_view] %>',
|
||||
'page_length' : '<%= session[:page_length] %>'
|
||||
},
|
||||
'system_config' : {
|
||||
'marketplace_url' : '<%= $conf[:marketplace_url] %>',
|
||||
'vnc_request_password' : <%= $conf[:vnc_request_password] || false %>,
|
||||
'vnc_proxy_port' : '<%= $vnc.proxy_port %>',
|
||||
'vnc_client_port' : '<%= $conf[:vnc_client_port] %>',
|
||||
'max_upload_file_size' : <%= $conf[:max_upload_file_size] ? $conf[:max_upload_file_size] : "undefined" %>,
|
||||
'public_fireedge_endpoint': '<%= $conf[:public_fireedge_endpoint] %>'
|
||||
},
|
||||
'view' : view,
|
||||
'available_views' : available_views,
|
||||
'all_labels' : all_labels,
|
||||
'all_views' : all_views,
|
||||
'user_id' : '<%= session[:user_id] %>',
|
||||
'user_gid' : '<%= session[:user_gid] %>',
|
||||
'display_name' : '<%= session[:display_name] %>',
|
||||
'zone_name' : '<%= session[:zone_name] %>',
|
||||
'zone_id' : '<%= session[:zone_id] %>',
|
||||
'federation_mode' : '<%= session[:federation_mode] %>',
|
||||
'vm_logos' : <%= logos_conf.to_json %>,
|
||||
'oned_conf' : <%= oned_conf.to_json %>,
|
||||
'support' : <%= support.to_json %>,
|
||||
'upgrade' : <%= upgrade.to_json %>,
|
||||
'mode' : '<%= session[:mode] %>'
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -10,7 +10,8 @@
|
||||
<link rel="apple-touch-startup-image" href="images/screen_320x460.png" />
|
||||
<link rel="apple-touch-icon" href="images/screen_57x57.png">
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" type="text/css" href="css/novnc-custom.css" title="plain">
|
||||
<link rel="stylesheet" type="text/css" href="css/app.css" title="plain">
|
||||
<link rel="stylesheet" type="text/css" href="css/vmrc-custom.css" title="plain">
|
||||
<% view = $views_config.view(session[:user], session[:user_gname], session[:default_view]) %>
|
||||
<!-- JQuery -->
|
||||
<script src="bower_components/jquery/dist/jquery.min.js"></script>
|
||||
@ -68,27 +69,45 @@
|
||||
</head>
|
||||
<body style="margin: 0px;">
|
||||
<div id="VMRC_screen">
|
||||
<div style="background: #f7f7f7; border-bottom: 1px solid #dfdfdf; padding: 10px 0px 15px 0px">
|
||||
<div id="VMRC_status_bar" class="VMRC_status_bar" style="margin-top: 0px;">
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
<td width="1%" >
|
||||
<img src="images/one_small_logo.png" style="height:40px; vertical-align:top; margin-left: 30px"></td>
|
||||
<td>
|
||||
<div id="VMRC_status" style="position: relative; height: auto; color: #000; text-align:center;">Loading</div>
|
||||
</td>
|
||||
<td width="1%" >
|
||||
<div id="VMRC_buttons" style="margin-right: 30px">
|
||||
<input type=button value="Send CtrlAltDel"
|
||||
id="sendCtrlAltDelButton">
|
||||
<div style="background: #f7f7f7; padding: 1.5em 1.5em 0.8em;">
|
||||
<div style="max-width: 1250px; margin: 0 auto;">
|
||||
<div id="VMRC_status_bar" class="noVNC_status_bar" style="display: flex; align-items: center;">
|
||||
<img src="images/one_small_logo.png" style="height:40px;">
|
||||
|
||||
<h5 id="VMRC_status" style="position: relative;">
|
||||
<div class="container">
|
||||
<div class="remote_logo">
|
||||
<img src="images/remote_console/vmrc.png">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="VMRC_status_msg">
|
||||
Loading
|
||||
</div>
|
||||
</div>
|
||||
</h5>
|
||||
|
||||
<div id="VMRC_buttons">
|
||||
<button class="button alert" id="sendCtrlAltDelButton">Send CtrlAltDel</button>
|
||||
<button class="button info" id="fullScreenButton"><i class="fas fa-expand"></i></button>
|
||||
<button class="button info" id="keyboardSelector"><i class="fas fa-keyboard"></i></button>
|
||||
<select style="width: auto;" id="selectLanguage" hidden>
|
||||
<option value="en-US">English</option>
|
||||
<option value="ja-JP_106/109">Japanese</option>
|
||||
<option value="de-DE">German</option>
|
||||
<option value="it-IT">Italian</option>
|
||||
<option value="es-ES">Spanish</option>
|
||||
<option value="pt-PT">Portuguese</option>
|
||||
<option value="fr-FR">French</option>
|
||||
<option value="fr-CH">Swiss-French</option>
|
||||
<option value="de-CH">Swiss-German</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="VMRC_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="wmksContainer" style="position:absolute;width:100%;height:100%; left:0em"></div>
|
||||
<div id="VMRC_canvas" width="640px" height="20px">
|
||||
|
||||
<div id="VMRC_canvas">
|
||||
<div id="wmksContainer" class="wmksContainer"></div>
|
||||
<div class="VMRC_message"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,7 +18,16 @@
|
||||
<div style="max-width: 1250px; margin: 0 auto;">
|
||||
<div id="noVNC_status_bar" class="noVNC_status_bar" style="display: flex; align-items: center;">
|
||||
<img src="images/one_small_logo.png" style="height:40px;">
|
||||
<h5 id="noVNC_status" style="position: relative;">Loading</h5>
|
||||
<h5 id="noVNC_status" style="position: relative;">
|
||||
<div class="container">
|
||||
<div class="remote_logo">
|
||||
<img src="images/remote_console/noVNC.png">
|
||||
</div>
|
||||
<div id="noVNC_status_msg">
|
||||
Loading
|
||||
</div>
|
||||
</div>
|
||||
</h5>
|
||||
<div id="noVNC_buttons">
|
||||
<button class="button alert" id="sendCtrlAltDelButton">Send CtrlAltDel</button>
|
||||
<span id="noVNC_xvp_buttons">
|
||||
|
Loading…
x
Reference in New Issue
Block a user