mirror of
https://github.com/OpenNebula/one.git
synced 2026-11-09 05:27:20 +09:00
1 parent
4a88858350
commit
c3fcbcae55
49 files changed
+1651
-181
No files matched your search
@@ -45,6 +45,9 @@ src/sunstone/public/css/app.min.css
|
||||
src/sunstone/public/dist/
|
||||
src/sunstone/public/locale/languages/*.js
|
||||
src/sunstone/public/package-lock.json
|
||||
src/sunstone/guac/node_modules
|
||||
src/sunstone/guac/dist
|
||||
src/sunstone/guac/package-lock.json
|
||||
.tx/config
|
||||
|
||||
src/fireedge/node_modules
|
||||
|
||||
@@ -739,6 +739,7 @@ INSTALL_SUNSTONE_FILES=(
|
||||
|
||||
INSTALL_SUNSTONE_PUBLIC_MINIFIED_FILES=(
|
||||
SUNSTONE_PUBLIC_JS_FILES:$SUNSTONE_LOCATION/public/dist
|
||||
SUNSTONE_PUBLIC_JS_GUAC_FILES:$SUNSTONE_LOCATION/guac/dist
|
||||
SUNSTONE_PUBLIC_JS_CONSOLE_FILES:$SUNSTONE_LOCATION/public/dist/console
|
||||
SUNSTONE_PUBLIC_FONT_AWSOME:$SUNSTONE_LOCATION/public/bower_components/fontawesome/web-fonts-with-css/webfonts
|
||||
SUNSTONE_PUBLIC_CSS_FILES:$SUNSTONE_LOCATION/public/css
|
||||
@@ -767,6 +768,7 @@ INSTALL_SUNSTONE_PUBLIC_MINIFIED_FILES=(
|
||||
|
||||
INSTALL_SUNSTONE_PUBLIC_DEV_DIR=(
|
||||
SUNSTONE_PUBLIC_DEV_DIR:$SUNSTONE_LOCATION
|
||||
SUNSTONE_GUAC_DEV_DIR:$SUNSTONE_LOCATION
|
||||
)
|
||||
|
||||
INSTALL_SUNSTONE_ETC_FILES=(
|
||||
@@ -895,6 +897,7 @@ RUBY_LIB_FILES="src/mad/ruby/ActionManager.rb \
|
||||
src/vnm_mad/one_vnm.rb \
|
||||
src/oca/ruby/opennebula.rb \
|
||||
src/sunstone/OpenNebulaVNC.rb \
|
||||
src/sunstone/OpenNebulaGuac.rb \
|
||||
src/sunstone/OpenNebulaAddons.rb \
|
||||
src/vmm_mad/remotes/vcenter/vcenter_driver.rb \
|
||||
src/vmm_mad/remotes/nsx/nsx_driver.rb \
|
||||
@@ -2373,6 +2376,7 @@ SUNSTONE_FILES="src/sunstone/sunstone-server.rb \
|
||||
src/sunstone/config.ru"
|
||||
|
||||
SUNSTONE_BIN_FILES="src/sunstone/bin/sunstone-server \
|
||||
src/sunstone/bin/guac-server \
|
||||
src/sunstone/bin/novnc-server"
|
||||
|
||||
SUNSTONE_ETC_FILES="src/sunstone/etc/sunstone-server.conf \
|
||||
@@ -2436,6 +2440,10 @@ SUNSTONE_PUBLIC_JS_FILES="src/sunstone/public/dist/login.js \
|
||||
src/sunstone/public/dist/main.js.map \
|
||||
src/sunstone/public/dist/main-dist.js"
|
||||
|
||||
SUNSTONE_PUBLIC_JS_GUAC_FILES="src/sunstone/guac/dist/guac.js"
|
||||
|
||||
SUNSTONE_GUAC_DEV_DIR="src/sunstone/guac"
|
||||
|
||||
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 \
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
||||
# not use this file except in compliance with the License. You may obtain #
|
||||
# a copy of the License at #
|
||||
# #
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 #
|
||||
# #
|
||||
# Unless required by applicable law or agreed to in writing, software #
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, #
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
||||
# See the License for the specific language governing permissions and #
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
#
|
||||
# This class provides support for launching and stopping a websockify proxy
|
||||
#
|
||||
|
||||
require 'rubygems'
|
||||
require 'json'
|
||||
require 'opennebula'
|
||||
require 'base64'
|
||||
require 'openssl'
|
||||
|
||||
|
||||
if !ONE_LOCATION
|
||||
GUAC_LOCK_FILE = "/var/lock/one/.guac.lock"
|
||||
GUACD_PID_FILE = "/var/run/one/guacd.pid"
|
||||
else
|
||||
GUAC_LOCK_FILE= ONE_LOCATION + "/var/.guac.lock"
|
||||
GUACD_PID_FILE= ONE_LOCATION + "/var/guacd.pid"
|
||||
end
|
||||
|
||||
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
|
||||
]
|
||||
|
||||
class OpenNebulaGuac
|
||||
|
||||
attr_reader :proxy_port
|
||||
|
||||
def initialize(config, logger, options = {})
|
||||
opts={ :json_errors => true }.merge(options)
|
||||
|
||||
@lock_file = GUAC_LOCK_FILE
|
||||
@guacd_pid_file = GUACD_PID_FILE
|
||||
|
||||
@options = opts
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def proxy(vm_resource, type_connection = "vnc")
|
||||
# Check configurations and VM attributes
|
||||
unless is_lockfile?
|
||||
return error(400, "Guacamole server is not running")
|
||||
end
|
||||
|
||||
if !is_running?
|
||||
return error(400, "Guacamole server is not running")
|
||||
end
|
||||
|
||||
if !is_guacd_running?
|
||||
return error(400, "Guacamole proxy daemon is not running")
|
||||
end
|
||||
|
||||
# Check configurations and VM attributes
|
||||
if !GUAC_STATES.include?(vm_resource['LCM_STATE'])
|
||||
return error(400, "Wrong state (#{vm_resource['LCM_STATE']}) to open a Guacamole session")
|
||||
end
|
||||
|
||||
if !(["vnc", "rdp", "ssh"].include?(type_connection.downcase))
|
||||
return error(400, "Type connection not supported by Guacamole")
|
||||
end
|
||||
|
||||
# TODO (* : optional) ===============================
|
||||
# =======================================================
|
||||
# type : telnet
|
||||
# hostname : vm_resource["TEMPLATE/NIC[1]/IP"]
|
||||
# port* : 23 by default
|
||||
# username* : vm_resource['TEMPLATE/CONTEXT/USERNAME']
|
||||
# password* : vm_resource['TEMPLATE/CONTEXT/PASSWORD']
|
||||
|
||||
settings = case type_connection.downcase
|
||||
when "vnc" then get_config_vnc(vm_resource)
|
||||
when "rdp" then get_config_rdp(vm_resource)
|
||||
when "ssh" then get_config_ssh(vm_resource)
|
||||
else {:error => error(400, "Type connection not supported by Guacamole")}
|
||||
end
|
||||
|
||||
return settings[:error] if settings.key?(:error)
|
||||
|
||||
data = encrypt_data({
|
||||
"connection": {
|
||||
"type": type_connection.downcase,
|
||||
"settings": {
|
||||
"security": "any",
|
||||
"ignore-cert": "true",
|
||||
"enable-drive": "true",
|
||||
"enable-audio": "true",
|
||||
"create-drive-path": "true",
|
||||
}.merge!(settings)
|
||||
}
|
||||
})
|
||||
|
||||
[200, { :token => data }.to_json]
|
||||
end
|
||||
|
||||
def status
|
||||
if is_running?
|
||||
STDOUT.puts "Guacamole server is running"
|
||||
true
|
||||
else
|
||||
STDOUT.puts "Guacamole server 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 is_running?
|
||||
if File.exist?(@lock_file)
|
||||
pid=File.read(@lock_file).strip
|
||||
|
||||
if system("ps #{pid} 1> /dev/null")
|
||||
return pid.to_i
|
||||
end
|
||||
|
||||
@logger.info "Deleting stale lock file"
|
||||
File.delete(@lock_file)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
alias_method :get_guac_pid, :is_running?
|
||||
|
||||
def is_guacd_running?
|
||||
if File.exist?(@guacd_pid_file)
|
||||
pid=File.read(@guacd_pid_file).strip
|
||||
|
||||
if system("ps #{pid} 1> /dev/null")
|
||||
return pid.to_i
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
alias_method :get_guacd_pid, :is_guacd_running?
|
||||
|
||||
def is_lockfile?
|
||||
dn = File.dirname(@lock_file)
|
||||
bn = File.basename(@lock_file)
|
||||
|
||||
# Due to restrictions which doesn't allow to stat the lock file
|
||||
# when Sunstone is running under Apache with SELinux enabled,
|
||||
# as a workaround we read content of the whole lock directory.
|
||||
begin
|
||||
Dir.entries(dn).include?(bn)
|
||||
rescue StandardError
|
||||
false
|
||||
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(GUACAMOLE_LOG, "a")
|
||||
$stderr.reopen(GUACAMOLE_LOG, "a")
|
||||
$stdin.close
|
||||
|
||||
# Detach process from the parent
|
||||
Process.setsid
|
||||
|
||||
exec(*command)
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def get_config_vnc(vm_resource)
|
||||
# If it is a vCenter VM
|
||||
if vm_resource['USER_TEMPLATE/HYPERVISOR'] == "vcenter"
|
||||
if vm_resource['MONITORING/VCENTER_ESX_HOST']
|
||||
hostname = 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")
|
||||
end
|
||||
else
|
||||
hostname = vm_resource['/VM/HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']
|
||||
end
|
||||
|
||||
{ "hostname": "localhost", "port": "5900", "password": nil }.merge({
|
||||
"hostname": hostname,
|
||||
"port": vm_resource['TEMPLATE/GRAPHICS/PORT'],
|
||||
"password": vm_resource['TEMPLATE/GRAPHICS/PASSWD']
|
||||
})
|
||||
end
|
||||
|
||||
def get_config_rdp(vm_resource)
|
||||
hostname = vm_resource["TEMPLATE/NIC[RDP='YES'][1]/IP | TEMPLATE/NIC_ALIAS[RDP='YES'][1]/IP"]
|
||||
|
||||
return {
|
||||
:error => error(400, "Wrong configuration. Cannot find a NIC with RDP")
|
||||
} if hostname.nil?
|
||||
|
||||
{ "hostname": "localhost", "port": "3389", "username": nil, "password": nil }.merge({
|
||||
"hostname": hostname,
|
||||
"port": vm_resource['TEMPLATE/CONTEXT/RDP_PORT'],
|
||||
"username": vm_resource['TEMPLATE/CONTEXT/USERNAME'],
|
||||
"password": vm_resource['TEMPLATE/CONTEXT/PASSWORD']
|
||||
})
|
||||
end
|
||||
|
||||
def get_config_ssh(vm_resource)
|
||||
hostname = vm_resource["TEMPLATE/NIC[SSH='YES'][1]/IP | TEMPLATE/NIC_ALIAS[SSH='YES'][1]/IP"]
|
||||
|
||||
return {
|
||||
:error => error(400, "Wrong configuration. Cannot find a NIC with SSH")
|
||||
} if hostname.nil?
|
||||
|
||||
{ "hostname": "localhost", "port": "22", "username": nil, "password": nil }.merge({
|
||||
"hostname": hostname,
|
||||
"port": vm_resource['TEMPLATE/CONTEXT/SSH_PORT'],
|
||||
"username": vm_resource['TEMPLATE/CONTEXT/USERNAME'],
|
||||
"password": vm_resource['TEMPLATE/CONTEXT/PASSWORD']
|
||||
})
|
||||
end
|
||||
|
||||
def encrypt_data(data)
|
||||
iv = OpenSSL::Random.random_bytes(16)
|
||||
key = 'LSIOGCKYLSIOGCKYLSIOGCKYLSIOGCKY' #OpenSSL::Random.random_bytes(32)
|
||||
|
||||
cipher = OpenSSL::Cipher::AES256.new(:CBC)
|
||||
cipher.encrypt
|
||||
cipher.key = key
|
||||
cipher.iv = iv
|
||||
|
||||
crypted = cipher.update(data.to_json)
|
||||
crypted << cipher.final
|
||||
token = {
|
||||
iv: Base64.encode64(iv).strip,
|
||||
value: Base64.strict_encode64(crypted).strip
|
||||
}
|
||||
|
||||
return Base64.strict_encode64(token.to_json).encode('utf-8').strip
|
||||
end
|
||||
end
|
||||
|
||||
@@ -285,6 +285,8 @@ class OpenNebulaVNC
|
||||
end
|
||||
|
||||
delete_token_dir
|
||||
|
||||
STDOUT.puts "VNC proxy stopped"
|
||||
else
|
||||
message="VNC server is not running"
|
||||
@logger.info message
|
||||
|
||||
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/bin/bash
|
||||
|
||||
# -------------------------------------------------------------------------- #
|
||||
# Copyright 2002-2020, OpenNebula Project, OpenNebula Systems #
|
||||
# #
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may #
|
||||
# not use this file except in compliance with the License. You may obtain #
|
||||
# a copy of the License at #
|
||||
# #
|
||||
# http://www.apache.org/licenses/LICENSE-2.0 #
|
||||
# #
|
||||
# Unless required by applicable law or agreed to in writing, software #
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, #
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
|
||||
# See the License for the specific language governing permissions and #
|
||||
# limitations under the License. #
|
||||
#--------------------------------------------------------------------------- #
|
||||
|
||||
GUACD_BIN=/usr/local/sbin/guacd
|
||||
|
||||
if [ -z "$ONE_LOCATION" ]; then
|
||||
GUAC_SERVER_DIR=/usr/lib/one/sunstone/guac
|
||||
GUAC_LOCK_FILE=/var/lock/one/.guac.lock
|
||||
GUAC_LOG=/var/log/one/guac.log
|
||||
GUAC_LOG_ERROR=/var/log/one/guac.error
|
||||
GUAC_PID=/var/run/one/guac.pid
|
||||
GUACD_PID=/var/run/one/guacd.pid
|
||||
SUNSTONE_CONF=/etc/one/sunstone-server.conf
|
||||
else
|
||||
GUAC_LOCK_FILE=$ONE_LOCATION/var/.guac.lock
|
||||
GUAC_LOG=$ONE_LOCATION/var/guac.log
|
||||
GUAC_LOG_ERROR=$ONE_LOCATION/var/guac.error
|
||||
GUAC_PID=$ONE_LOCATION/var/guac.pid
|
||||
GUACD_PID=$ONE_LOCATION/var/guacd.pid
|
||||
SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf
|
||||
fi
|
||||
|
||||
get_pid()
|
||||
{
|
||||
if [ -f "$1" ]; then
|
||||
read PID < "$1"
|
||||
|
||||
# if pidfile contains PID and PID is valid
|
||||
if [ -n "$PID" ] && ps "$PID" > /dev/null 2>&1; then
|
||||
echo "$PID"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# pidfile/pid not found, or process is dead
|
||||
return 1
|
||||
}
|
||||
|
||||
start_guacd()
|
||||
{
|
||||
if [ ! -f "$GUACD_BIN" ]; then
|
||||
echo "Cannot find $GUACD_BIN."
|
||||
return 1
|
||||
fi
|
||||
|
||||
PID=`get_pid $GUACD_PID`
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "Guacamole proxy daemon already running"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -n "Starting guacd: "
|
||||
$GUACD_BIN -p "$GUACD_PID" > /dev/null 2>&1
|
||||
|
||||
case "$?" in
|
||||
0)
|
||||
echo "SUCCESS"
|
||||
;;
|
||||
*)
|
||||
echo "FAIL"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
stop_guacd()
|
||||
{
|
||||
echo -n "Stopping guacd: "
|
||||
|
||||
PID=`get_pid $GUACD_PID`
|
||||
case "$?" in
|
||||
0)
|
||||
if kill -9 $PID > /dev/null 2>&1
|
||||
then
|
||||
echo "SUCCESS"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "FAIL"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
echo "SUCCESS (not running)"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
setup()
|
||||
{
|
||||
if [ -f $GUAC_LOCK_FILE ]; then
|
||||
PID=`get_pid $GUAC_PID`
|
||||
if [ $? -eq 0 ]; then
|
||||
echo -n "Guacamole Server is still running (PID:$PID). Please "
|
||||
echo "try 'guac-server stop' first."
|
||||
exit 1
|
||||
fi
|
||||
echo "Stale .lock detected. Erasing it."
|
||||
rm $GUAC_LOCK_FILE
|
||||
fi
|
||||
}
|
||||
|
||||
start()
|
||||
{
|
||||
ENV_MODE=$(grep ^:env: $SUNSTONE_CONF|awk '{print $2}')
|
||||
if [ "$ENV_MODE" = "dev" ]; then
|
||||
GUAC_SERVER=$GUAC_SERVER_DIR/app.js
|
||||
else
|
||||
GUAC_SERVER=$GUAC_SERVER_DIR/dist/guac.js
|
||||
fi
|
||||
|
||||
if [ ! -f "$GUAC_SERVER" ]; then
|
||||
echo "Cannot find $GUAC_SERVER."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
touch $GUAC_LOCK_FILE
|
||||
|
||||
# Start the guac-server daemon
|
||||
GUAC_PORT=$(grep ^:guac_port: $SUNSTONE_CONF|awk '{print $2}')
|
||||
GUACD_PORT=$(grep ^:guacd_port: $SUNSTONE_CONF|awk '{print $2}')
|
||||
PORT="$GUAC_PORT" GUACD_PORT="$GUACD_PORT" node "$GUAC_SERVER" \
|
||||
> $GUAC_LOG 2>$GUAC_LOG_ERROR &
|
||||
|
||||
LASTRC=$?
|
||||
LASTPID=$!
|
||||
|
||||
if [ $LASTRC -ne 0 ]; then
|
||||
echo "Error executing guac-server."
|
||||
echo "Check $GUAC_LOG_ERROR and $GUAC_LOG for more information"
|
||||
exit 1
|
||||
else
|
||||
echo $LASTPID > $GUAC_PID
|
||||
fi
|
||||
|
||||
sleep 2
|
||||
ps $LASTPID > /dev/null 2>&1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error executing guac-server."
|
||||
echo "Check $GUAC_LOG_ERROR and $GUAC_LOG for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "guac-server started"
|
||||
}
|
||||
|
||||
#
|
||||
# Function that stops the daemon/service
|
||||
#
|
||||
stop()
|
||||
{
|
||||
if [ ! -f $GUAC_PID ]; then
|
||||
echo "Couldn't find guac-server process pid."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Kill the guac-server daemon
|
||||
kill -9 `cat $GUAC_PID` > /dev/null 2>&1
|
||||
|
||||
# Remove pid files
|
||||
rm -f $GUAC_PID > /dev/null 2>&1
|
||||
rm -f $GUAC_LOCK_FILE &> /dev/null
|
||||
|
||||
echo "guac-server stopped"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_guacd
|
||||
setup
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop_guacd
|
||||
stop
|
||||
;;
|
||||
*)
|
||||
echo "Usage: guac-server {start|stop}" >&2
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
@@ -25,6 +25,7 @@ if [ -z "$ONE_LOCATION" ]; then
|
||||
SUNSTONE_LOG_ERROR=/var/log/one/sunstone.error
|
||||
SUNSTONE_CONF=/etc/one/sunstone-server.conf
|
||||
NOVNC_SERVER=/usr/bin/novnc-server
|
||||
GUACAMOLE_SERVER=/usr/bin/guac-server
|
||||
else
|
||||
SUNSTONE_PID=$ONE_LOCATION/var/sunstone.pid
|
||||
SUNSTONE_SERVER=$ONE_LOCATION/lib/sunstone/sunstone-server.rb
|
||||
@@ -33,6 +34,7 @@ else
|
||||
SUNSTONE_LOG_ERROR=$ONE_LOCATION/var/sunstone.error
|
||||
SUNSTONE_CONF=$ONE_LOCATION/etc/sunstone-server.conf
|
||||
NOVNC_SERVER=$ONE_LOCATION/bin/novnc-server
|
||||
GUACAMOLE_SERVER=$ONE_LOCATION/bin/guac-server
|
||||
fi
|
||||
|
||||
setup()
|
||||
@@ -59,12 +61,15 @@ setup()
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
start()
|
||||
{
|
||||
if [ "$1" = "-n" ]; then
|
||||
novnc=1
|
||||
fi
|
||||
while getopts "ng" opt; do
|
||||
case "${opt}" in
|
||||
n) novnc=1 ;;
|
||||
g) guacamole=1 ;;
|
||||
*) echo "Invalid option: $OPTARG" 1>&2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -f "$SUNSTONE_SERVER" ]; then
|
||||
echo "Cannot find $SUNSTONE_SERVER."
|
||||
@@ -80,6 +85,15 @@ start()
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$guacamole" = "1" ]; then
|
||||
# Start guacamole server
|
||||
$GUACAMOLE_SERVER start
|
||||
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Could not start Guacamole server" 1>&2
|
||||
fi
|
||||
fi
|
||||
|
||||
# Start the sunstone daemon
|
||||
touch $SUNSTONE_LOCK_FILE
|
||||
|
||||
@@ -87,6 +101,7 @@ start()
|
||||
echo "Error creating lock file" 1>&2
|
||||
|
||||
$NOVNC_SERVER stop
|
||||
$GUACAMOLE_SERVER stop
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -97,6 +112,7 @@ start()
|
||||
echo "Error executing sunstone-server."
|
||||
echo "Check $SUNSTONE_LOG_ERROR and $SUNSTONE_LOG for more information"
|
||||
[ "$novnc" = "1" ] && $NOVNC_SERVER stop
|
||||
[ "$guacamole" = "1" ] && $GUACAMOLE_SERVER stop
|
||||
exit 1
|
||||
else
|
||||
echo $LASTPID > $SUNSTONE_PID
|
||||
@@ -109,6 +125,7 @@ start()
|
||||
echo "Error executing sunstone-server."
|
||||
echo "Check $SUNSTONE_LOG_ERROR and $SUNSTONE_LOG for more information"
|
||||
[ "$novnc" = "1" ] && $NOVNC_SERVER stop
|
||||
[ "$guacamole" = "1" ] && $GUACAMOLE_SERVER stop
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -120,13 +137,20 @@ start()
|
||||
#
|
||||
stop()
|
||||
{
|
||||
if [ "$1" = "-n" ]; then
|
||||
novnc=1
|
||||
fi
|
||||
while getopts "ng" opt; do
|
||||
case "${opt}" in
|
||||
n) novnc=1 ;;
|
||||
g) guacamole=1 ;;
|
||||
*) echo "Invalid option: $OPTARG" 1>&2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Stop novnc server
|
||||
[ "$novnc" = "1" ] && $NOVNC_SERVER stop
|
||||
|
||||
# Stop guacamole server
|
||||
[ "$guacamole" = "1" ] && $GUACAMOLE_SERVER stop
|
||||
|
||||
if [ ! -f $SUNSTONE_PID ]; then
|
||||
echo "Couldn't find sunstone-server process pid." >&2
|
||||
return 1
|
||||
@@ -146,16 +170,16 @@ stop()
|
||||
case "$1" in
|
||||
start)
|
||||
setup
|
||||
start -n
|
||||
start -ng
|
||||
;;
|
||||
stop)
|
||||
stop -n
|
||||
stop -ng
|
||||
exit $?
|
||||
;;
|
||||
restart)
|
||||
stop -n 2> /dev/null
|
||||
stop -ng 2> /dev/null
|
||||
setup
|
||||
start -n
|
||||
start -ng
|
||||
;;
|
||||
start-sunstone)
|
||||
setup
|
||||
@@ -165,7 +189,7 @@ case "$1" in
|
||||
stop
|
||||
;;
|
||||
restart-sunstone)
|
||||
stop -n 2> /dev/null
|
||||
stop -ng 2> /dev/null
|
||||
setup
|
||||
start
|
||||
;;
|
||||
|
||||
@@ -158,6 +158,9 @@
|
||||
:vnc_request_password: false
|
||||
:allow_vnc_federation: no
|
||||
|
||||
:guac_port: 29877
|
||||
:guacd_port: 4822
|
||||
|
||||
# Login Session Length in seconds, defaults to 1 hour.
|
||||
#:session_expire_time: 3600
|
||||
|
||||
|
||||
@@ -420,6 +420,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -93,6 +93,9 @@ tabs:
|
||||
VM.unlock: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
dashboard:
|
||||
|
||||
@@ -419,6 +419,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -410,10 +410,13 @@ tabs:
|
||||
VM.unlock: true
|
||||
VM.edit_labels: true
|
||||
VM.menu_labels: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
panel_tabs:
|
||||
service_info_tab: true
|
||||
|
||||
@@ -420,6 +420,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -93,6 +93,9 @@ tabs:
|
||||
VM.unlock: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
dashboard:
|
||||
|
||||
@@ -419,6 +419,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -412,6 +412,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -418,6 +418,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -94,6 +94,9 @@ tabs:
|
||||
VM.unlock: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
dashboard:
|
||||
|
||||
@@ -419,6 +419,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -412,6 +412,9 @@ tabs:
|
||||
VM.menu_labels: true
|
||||
VM.startvnc: true
|
||||
VM.startspice: true
|
||||
VM.vnc: true
|
||||
VM.ssh: true
|
||||
VM.rdp: true
|
||||
VM.save_rdp: true
|
||||
VM.save_virt_viewer: true
|
||||
oneflow-services-tab:
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// Guacamole Client
|
||||
|
||||
//// Application Variables ////
|
||||
var helmet = require('helmet');
|
||||
var app = require('express')();
|
||||
var http = require('http').Server(app);
|
||||
var GuacamoleLite = require('guacamole-lite');
|
||||
|
||||
var { clientOptions, clientCallbacks } = require('./options');
|
||||
|
||||
/**
|
||||
* CONSTANTS
|
||||
* */
|
||||
GUAC_PORT = process.env.PORT || 29877
|
||||
GUACD_PORT = process.env.GUACD || 4822
|
||||
|
||||
/**
|
||||
* Spinup the Guac websocket proxy on port 29877 if guacd is running
|
||||
* */
|
||||
var guacServer = new GuacamoleLite(
|
||||
{ server: http, path: '/guacamole' },
|
||||
{ host: '127.0.0.1', port: GUACD_PORT },
|
||||
clientOptions,
|
||||
clientCallbacks
|
||||
);
|
||||
|
||||
/**
|
||||
* Helmet helps you secure your Express apps
|
||||
* */
|
||||
app.use(helmet());
|
||||
|
||||
// Spin up application on port 29877
|
||||
http.listen(GUAC_PORT, function(){
|
||||
console.log('listening on *:' + GUAC_PORT);
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* CLIENT OPTIONS
|
||||
*/
|
||||
var clientOptions = {
|
||||
crypt: {
|
||||
cypher: 'AES-256-CBC',
|
||||
key: 'LSIOGCKYLSIOGCKYLSIOGCKYLSIOGCKY'
|
||||
},
|
||||
allowedUnencryptedConnectionSettings: {
|
||||
rdp: [
|
||||
'width',
|
||||
'height',
|
||||
'dpi'
|
||||
],
|
||||
vnc: [
|
||||
'width',
|
||||
'height',
|
||||
'dpi'
|
||||
],
|
||||
ssh: [
|
||||
'color-scheme',
|
||||
'font-name',
|
||||
'font-size',
|
||||
'width',
|
||||
'height',
|
||||
'dpi'
|
||||
],
|
||||
telnet: [
|
||||
'color-scheme',
|
||||
'font-name',
|
||||
'font-size',
|
||||
'width',
|
||||
'height',
|
||||
'dpi'
|
||||
]
|
||||
},
|
||||
log: { verbose: false }
|
||||
};
|
||||
|
||||
/**
|
||||
* CALLBACKS
|
||||
*/
|
||||
var callbacks = {
|
||||
processConnectionSettings: function (settings, callback) {
|
||||
if (settings.expiration && settings.expiration < Date.now()) {
|
||||
return callback(new Error('Token expired'));
|
||||
}
|
||||
|
||||
//settings.connection['drive-path'] = '/tmp/guacamole_'
|
||||
//settings.connection['enable-drive'] = false
|
||||
//settings.connection['create-drive-path'] = false
|
||||
|
||||
callback(null, settings);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
clientOptions,
|
||||
callbacks
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "one-guac",
|
||||
"version": "1.0.0",
|
||||
"description": "OpenNebula Guacamole Client",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "webpack --mode=production app.js"
|
||||
},
|
||||
"author": "Sergio Betanzos",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"express": "^4.17.1",
|
||||
"guacamole-lite": "^0.6.2",
|
||||
"helmet": "^3.23.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"webpack": "^4.43.0",
|
||||
"webpack-cli": "^3.3.12"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: './app.js',
|
||||
target: 'node',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'guac.js'
|
||||
},
|
||||
stats: {
|
||||
warningsFilter: /^(?!CriticalDependenciesWarning$)/
|
||||
}
|
||||
};
|
||||
@@ -20,6 +20,7 @@ require 'OpenNebulaJSON'
|
||||
include OpenNebulaJSON
|
||||
|
||||
require 'OpenNebulaVNC'
|
||||
require 'OpenNebulaGuac'
|
||||
require 'OpenNebulaAddons'
|
||||
require 'OpenNebulaJSON/JSONUtils'
|
||||
#include JSONUtils
|
||||
@@ -301,6 +302,34 @@ class SunstoneServer < CloudServer
|
||||
return vnc.proxy(resource)
|
||||
end
|
||||
|
||||
########################################################################
|
||||
# Guacamole
|
||||
########################################################################
|
||||
def startguac(id, type_connection, guac)
|
||||
resource = retrieve_resource("vm", id)
|
||||
if OpenNebula.is_error?(resource)
|
||||
return [404, resource.to_json]
|
||||
end
|
||||
|
||||
client = @client
|
||||
vm_pool = VirtualMachinePool.new(client, -1)
|
||||
user_pool = UserPool.new(client)
|
||||
|
||||
rc = user_pool.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
puts rc.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
rc = vm_pool.info
|
||||
if OpenNebula.is_error?(rc)
|
||||
puts rc.message
|
||||
exit -1
|
||||
end
|
||||
|
||||
return guac.proxy(resource, type_connection)
|
||||
end
|
||||
|
||||
########################################################################
|
||||
# Accounting & Monitoring
|
||||
########################################################################
|
||||
|
||||
@@ -29,6 +29,9 @@ require.config({
|
||||
/** Wickedpicker */
|
||||
"wickedpicker": "../bower_components/wickedpicker/dist/wickedpicker.min",
|
||||
|
||||
/** Guacamole-common-js */
|
||||
"guacamole-common-js": "../bower_components/guacamole-common-js/dist/guacamole-common.min",
|
||||
|
||||
/* DataTables */
|
||||
"datatables.net": "../bower_components/datatables/media/js/jquery.dataTables",
|
||||
"datatables.foundation": "../bower_components/datatables/media/js/dataTables.foundation",
|
||||
|
||||
@@ -512,6 +512,27 @@ define(function(require) {
|
||||
}
|
||||
});
|
||||
},
|
||||
"guac" : function(params) {
|
||||
var callback = params.success;
|
||||
var callback_error = params.error;
|
||||
var id = params.data.id;
|
||||
var typeConnection = params.data.extra_param;
|
||||
var resource = RESOURCE;
|
||||
|
||||
var request = OpenNebulaHelper.request(resource, null, params.data);
|
||||
$.ajax({
|
||||
url: "vm/" + id + "/guac/" + typeConnection,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
return callback ? callback(request, response) : null;
|
||||
},
|
||||
error: function(response) {
|
||||
return callback_error ?
|
||||
callback_error(request, OpenNebulaError(response)) : null;
|
||||
}
|
||||
});
|
||||
},
|
||||
"append": function(params) {
|
||||
var action_obj = {"template_raw" : params.data.extra_param, append : true};
|
||||
OpenNebulaAction.simple_action(params, RESOURCE, "update", action_obj);
|
||||
@@ -666,13 +687,14 @@ define(function(require) {
|
||||
"isDiskGraphsSupported": isDiskGraphsSupported,
|
||||
"isNICAttachSupported": isNICAttachSupported,
|
||||
"isVNCSupported": isVNCSupported,
|
||||
"isRDPSupported": isRDPSupported,
|
||||
"isConnectionSupported": isConnectionSupported,
|
||||
"isSPICESupported": isSPICESupported,
|
||||
"isWFileSupported": isWFileSupported,
|
||||
"buttonVnc": buttonVnc,
|
||||
"buttonSpice": buttonSpice,
|
||||
"buttonWFile": buttonWFile,
|
||||
"buttonRDP": buttonRDP,
|
||||
"dropdownRDP": dropdownRDP,
|
||||
"buttonSSH": buttonSSH,
|
||||
"getName": function(id){
|
||||
return OpenNebulaAction.getName(id, RESOURCE);
|
||||
}
|
||||
@@ -925,7 +947,7 @@ define(function(require) {
|
||||
|
||||
// returns true if the vnc button should be enabled
|
||||
function isVNCSupported(element) {
|
||||
return (Config.isTabActionEnabled("vms-tab", "VM.startvnc") && graphicSupported(element, "vnc"))
|
||||
return (Config.isTabActionEnabled("vms-tab", "VM.vnc") && graphicSupported(element, "vnc"))
|
||||
? true : false;
|
||||
}
|
||||
|
||||
@@ -948,34 +970,43 @@ define(function(require) {
|
||||
: false;
|
||||
}
|
||||
|
||||
// returns true if the RDP button should be enabled
|
||||
function isRDPSupported(element) {
|
||||
var hasRdp = false;
|
||||
/**
|
||||
* returns NIC object if the SSH/RDP should be enabled
|
||||
* @param {Object} element
|
||||
* @param {String} typeConnection
|
||||
*/
|
||||
function isConnectionSupported(element, typeConnection) {
|
||||
var isEnabled = false;
|
||||
if (
|
||||
Config.isTabActionEnabled("vms-tab", "VM.save_rdp") &&
|
||||
$.inArray(String(typeConnection).toLowerCase(), ['rdp', 'ssh']) > -1 &&
|
||||
Config.isTabActionEnabled("vms-tab", "VM."+ String(typeConnection).toLowerCase()) &&
|
||||
element && element.TEMPLATE && element.TEMPLATE.GRAPHICS && element.LCM_STATE
|
||||
) {
|
||||
var template = element.TEMPLATE;
|
||||
var state = parseInt(element.LCM_STATE);
|
||||
|
||||
if ($.inArray(state, RDP_STATES) != -1 && template.NIC) {
|
||||
hasRdp = hasRDP(template.NIC);
|
||||
if ($.inArray(state, RDP_STATES) > -1 && template.NIC) {
|
||||
isEnabled = hasConnection(template.NIC, typeConnection);
|
||||
|
||||
if (!hasRdp && template.NIC_ALIAS) {
|
||||
hasRdp = hasRDP(template.NIC_ALIAS);
|
||||
if (!isEnabled && template.NIC_ALIAS) {
|
||||
isEnabled = hasConnection(template.NIC_ALIAS, typeConnection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasRdp;
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
function hasRDP(nics) {
|
||||
function hasConnection(nics, typeConnection) {
|
||||
typeConnection = String(typeConnection).toUpperCase();
|
||||
var activated = false;
|
||||
nics = Array.isArray(nics) ? nics : [nics];
|
||||
|
||||
$.each(nics, function(_, nic) {
|
||||
if (nic.RDP && String(nic.RDP).toLowerCase() === "yes") {
|
||||
if (
|
||||
nic[typeConnection] &&
|
||||
String(nic[typeConnection]).toLowerCase() === "yes"
|
||||
) {
|
||||
activated = nic;
|
||||
}
|
||||
});
|
||||
@@ -997,23 +1028,48 @@ define(function(require) {
|
||||
}
|
||||
|
||||
function buttonVnc(id = "") {
|
||||
return '<button class="vnc remote_vm" data-id="' + id + '">\
|
||||
return '<button class="vnc remote-vm" data-id="' + id + '">\
|
||||
<i class="fas fa-desktop"></i></button>';
|
||||
}
|
||||
|
||||
function buttonSSH(id = "") {
|
||||
return '<button class="ssh remote-vm" data-id="' + id + '">\
|
||||
<i class="fas fa-terminal"></i></button>';
|
||||
}
|
||||
|
||||
function buttonSpice(id = "") {
|
||||
return '<button class="spice remote_vm" data-id="' + id + '">\
|
||||
return '<button class="spice remote-vm" data-id="' + id + '">\
|
||||
<i class="fas fa-desktop"></i></button>';
|
||||
}
|
||||
|
||||
function buttonWFile(id = "", data = {}) {
|
||||
return '<button class="w_file remote_vm" data-id="' + id + '"\
|
||||
return '<button class="w-file remote-vm" data-id="' + id + '"\
|
||||
data-type="' + data.type + '" data-port="' + data.port + '" data-hostname="' + data.hostname + '">\
|
||||
<i class="fas fa-external-link-square-alt"></i></button>';
|
||||
}
|
||||
|
||||
function buttonRDP(ip = "", vm = {}) {
|
||||
var username, password;
|
||||
function dropdownRDP(id = "", ip = "", vm = {}) {
|
||||
return '<ul class="dropdown menu" id="rdp-buttons" data-dropdown-menu> \
|
||||
<li>\
|
||||
<button type="button" class="remote-vm"> \
|
||||
<i class="fab fa-windows"></i> \
|
||||
</button>\
|
||||
<ul class="menu">\
|
||||
<li>' + buttonRDP(id) + '</li> \
|
||||
<li>' + buttonSaveRDP(ip, vm) + '</li> \
|
||||
</ul>\
|
||||
</li>\
|
||||
</ul>';
|
||||
}
|
||||
|
||||
function buttonRDP(id = "") {
|
||||
var styleIcon = 'style="width: 2em;text-align: center;"';
|
||||
return '<a class="rdp" data-id="' + id + '"> \
|
||||
<i class="fas fa-desktop" ' + styleIcon + '></i><span>' + Locale.tr("HTML") + '</span></a>';
|
||||
}
|
||||
|
||||
function buttonSaveRDP(ip = "", vm = {}) {
|
||||
var username, password, styleIcon = 'style="width: 2em;text-align: center;"';
|
||||
|
||||
if (vm && vm.TEMPLATE && vm.TEMPLATE.CONTEXT) {
|
||||
var context = vm.TEMPLATE.CONTEXT;
|
||||
@@ -1023,13 +1079,14 @@ define(function(require) {
|
||||
(propUpperCase === "PASSWORD") && (password = context[prop]);
|
||||
}
|
||||
}
|
||||
var button = '<button class="rdp remote_vm" data-name="' + vm.NAME + '" data-ip="' + ip + '"';
|
||||
var button = '<a class="save-rdp" data-name="' + vm.NAME + '" data-ip="' + ip + '"';
|
||||
username && (button += ' data-username="' + username + '"');
|
||||
password && (button += ' data-password="' + password + '"');
|
||||
button += '><i class="fab fa-windows"></i></button>';
|
||||
button += '><i class="fas fa-file" ' + styleIcon + '></i><span>' + Locale.tr("RDP Client") + '</span></a>';
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
return VM;
|
||||
});
|
||||
@@ -169,6 +169,7 @@ define(function(require) {
|
||||
"scaleFactor": _config["view"]["features"]["instantiate_cpu_factor"],
|
||||
"filterView": _config["view"]["filter-view"],
|
||||
"doCountAnimation": _config["view"]["do_count_animation"],
|
||||
"guacPort": _config["user_config"]["guac_port"],
|
||||
|
||||
"allTabs": function() {
|
||||
return Object.keys(_config["view"]["tabs"]);
|
||||
|
||||
@@ -227,8 +227,11 @@ define(function(require) {
|
||||
var wFile = OpenNebulaVM.isWFileSupported(data.VM);
|
||||
actions += wFile ? OpenNebulaVM.buttonWFile(id, wFile) : "";
|
||||
|
||||
var rdp = OpenNebulaVM.isRDPSupported(data.VM);
|
||||
actions += rdp ? OpenNebulaVM.buttonRDP(rdp.IP, data.VM) : "";
|
||||
var rdp = OpenNebulaVM.isConnectionSupported(data.VM, 'rdp');
|
||||
actions += rdp ? OpenNebulaVM.dropdownRDP(id, rdp.ID, data.VM) : "";
|
||||
|
||||
var ssh = OpenNebulaVM.isConnectionSupported(data.VM, 'ssh');
|
||||
actions += ssh ? OpenNebulaVM.buttonSSH(id) : "";
|
||||
}
|
||||
|
||||
roleVms[index] = rowInfoRoleVm(id, name, uname, gname, ips, actions);
|
||||
|
||||
@@ -71,6 +71,7 @@ define(function(require) {
|
||||
WizardTab.prototype.addNicTab = _addNicTab;
|
||||
WizardTab.prototype.notify = _notify;
|
||||
WizardTab.prototype.enableRDP = _enableRDP;
|
||||
WizardTab.prototype.enableSSH = _enableSSH;
|
||||
|
||||
return WizardTab;
|
||||
|
||||
@@ -210,6 +211,7 @@ define(function(require) {
|
||||
|
||||
that.renameTabLinks(context);
|
||||
that.enableRDP(context);
|
||||
that.enableSSH(context);
|
||||
|
||||
if (templateJSON.NIC) {
|
||||
delete templateJSON.NIC;
|
||||
@@ -282,6 +284,7 @@ define(function(require) {
|
||||
|
||||
that.renameTabLinks(context);
|
||||
that.enableRDP(context);
|
||||
that.enableSSH(context);
|
||||
that.nicTabObjects[that.numberOfNics] = nicTab;
|
||||
|
||||
// close icon: removing the tab on click
|
||||
@@ -333,6 +336,13 @@ define(function(require) {
|
||||
else $("fieldset#rdp_connection", that.context).hide();
|
||||
}
|
||||
|
||||
function _enableSSH(context) {
|
||||
const canSSH = $("fieldset#ssh_connection input[type='checkbox']:not(#" + that.nicTabId + "_ssh):checked", context).length === 0;
|
||||
|
||||
if (canSSH) $("fieldset#ssh_connection").show();
|
||||
else $("fieldset#ssh_connection", that.context).hide();
|
||||
}
|
||||
|
||||
function _notify(context, templateJSON) {
|
||||
if (templateJSON.VROUTER == "YES"){
|
||||
while($("i.remove-tab", context).length > 0){
|
||||
|
||||
+24
-4
@@ -317,7 +317,12 @@ define(function(require) {
|
||||
|
||||
context.on("change", "#" + that.nicTabId + "_rdp", function() {
|
||||
const isRDPActivated = $(this).prop('checked');
|
||||
_hide_rdp(that.nicTabId, isRDPActivated, context);
|
||||
_hide_connection(that.nicTabId, isRDPActivated, context, 'rdp');
|
||||
});
|
||||
|
||||
context.on("change", "#" + that.nicTabId + "_ssh", function() {
|
||||
const isSSHActivated = $(this).prop('checked');
|
||||
_hide_connection(that.nicTabId, isSSHActivated, context, 'ssh');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -372,6 +377,10 @@ define(function(require) {
|
||||
nicJSON["RDP"] = "YES";
|
||||
}
|
||||
|
||||
if($("input#" + this.nicTabId + "_ssh", context).prop("checked")) {
|
||||
nicJSON["SSH"] = "YES";
|
||||
}
|
||||
|
||||
return nicJSON;
|
||||
}
|
||||
|
||||
@@ -488,6 +497,14 @@ define(function(require) {
|
||||
|
||||
$("input#" + this.nicTabId + "_rdp", context).prop("checked", isRDPActivated);
|
||||
|
||||
const isSSHActivated = (
|
||||
templateJSON["SSH"] &&
|
||||
templateJSON["SSH"] === "YES" &&
|
||||
$("fieldset#ssh_connection input:not(#" + that.nicTabId + "_ssh):checked", context).length === 0
|
||||
) ? true : false;
|
||||
|
||||
$("input#" + this.nicTabId + "_ssh", context).prop("checked", isSSHActivated);
|
||||
|
||||
WizardFields.fill(context, templateJSON);
|
||||
}
|
||||
|
||||
@@ -526,9 +543,12 @@ define(function(require) {
|
||||
});
|
||||
}
|
||||
|
||||
function _hide_rdp(nicTabId, isRDPActivated, context) {
|
||||
$("#template_create_network_tabs_content > div:not(#" + nicTabId + ") fieldset#rdp_connection", context).each(function() {
|
||||
if (isRDPActivated) {
|
||||
function _hide_connection(nicTabId, isActivated, context, typeConnection) {
|
||||
tabSelector = "#template_create_network_tabs_content";
|
||||
fieldsetSelector = "fieldset#" + typeConnection + "_connection";
|
||||
|
||||
$(tabSelector + " > div:not(#" + nicTabId + ") " + fieldsetSelector, context).each(function() {
|
||||
if (isActivated) {
|
||||
$(this).hide();
|
||||
} else {
|
||||
$(this).show();
|
||||
|
||||
+75
-62
@@ -16,75 +16,88 @@
|
||||
{{!-- {{#isFeatureEnabled "automatic_selection_vnet"}} --}}
|
||||
<div class="only_create">
|
||||
<div class="row autoContainer">
|
||||
<div class="large-12 large-centered columns" style="display:inline-flex;justify-content:start;">
|
||||
<fieldset id="{{nicTabId}}interface_type" class="small-12 large-4 columns">
|
||||
<legend style="margin:0px;">{{tr "Interface type"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="{{nicTabId}}_interface_type" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_interface_type">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Alias"}}
|
||||
{{{tip (tr "The NIC will be attached as an alias")}}}
|
||||
<fieldset id="{{nicTabId}}interface_type" class="small-12 large-6 columns">
|
||||
<legend style="margin:0px;">{{tr "Interface type"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="{{nicTabId}}_interface_type" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_interface_type">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Alias"}}
|
||||
{{{tip (tr "The NIC will be attached as an alias")}}}
|
||||
</label>
|
||||
|
||||
<span id="{{nicTabId}}_no_alias" class="radius warning label">
|
||||
<i class="fas fa-exclamation-triangle"/> {{tr "NIC has alias"}}
|
||||
</span>
|
||||
<select id="{{nicTabId}}_alias_parent" style="width: 200px; margin-left: 5px"></select>
|
||||
</fieldset>
|
||||
<fieldset class="small-12 large-6 columns">
|
||||
<legend style="margin:0px;">{{tr "Network selection"}}</legend>
|
||||
<div class="switch left network_selection">
|
||||
<input class="switch-input" id="{{nicTabId}}_network_mode" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_network_mode">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left network_selection">
|
||||
|
||||
{{tr "Automatic selection"}}
|
||||
{{{tip (tr "The Schedule will decide which is the best virtual network")}}}
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="row autoContainer">
|
||||
<fieldset id="rdp_connection" class="small-12 large-6 columns">
|
||||
<legend style="margin:0px;">{{tr "RDP connection"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="{{nicTabId}}_rdp" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_rdp">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
<span id="{{nicTabId}}_no_alias" class="radius warning label">
|
||||
<i class="fas fa-exclamation-triangle"/> {{tr "NIC has alias"}}
|
||||
</span>
|
||||
<select id="{{nicTabId}}_alias_parent" style="width: 200px; margin-left: 5px"></select>
|
||||
</fieldset>
|
||||
<fieldset class="small-12 large-4 columns">
|
||||
<legend style="margin:0px;">{{tr "Network selection"}}</legend>
|
||||
<div class="switch left network_selection">
|
||||
<input class="switch-input" id="{{nicTabId}}_network_mode" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_network_mode">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left network_selection">
|
||||
|
||||
{{tr "Automatic selection"}}
|
||||
{{{tip (tr "The Schedule will decide which is the best virtual network")}}}
|
||||
</label>
|
||||
</fieldset class="small-12 large-4 columns">
|
||||
<fieldset id="rdp_connection" class="small-12 large-4 columns">
|
||||
<legend style="margin:0px;">{{tr "RDP connection"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="{{nicTabId}}_rdp" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_rdp">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "RDP will be activate in this network")}}}
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "RDP will be activate in this network")}}}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset id="ssh_connection" class="small-12 large-6 columns">
|
||||
<legend style="margin:0px;">{{tr "SSH connection"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="{{nicTabId}}_ssh" type="checkbox">
|
||||
<label class="switch-paddle" for="{{nicTabId}}_ssh">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "SSH will be enable in this network")}}}
|
||||
</label>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="auto">
|
||||
<fieldset>
|
||||
<legend>{{tr "Network Requirements"}}</legend>
|
||||
<div class="row">
|
||||
<div class="large-12 columns text-center">
|
||||
<input type="radio" id="vnet_req" name="{{nicTabId}}_req_select" value="vnet_select">
|
||||
<label for="vnet_req">{{tr "Select Vnets "}}</label>
|
||||
<input type="radio" id="custom_req" name="{{nicTabId}}_req_select" value="custom" checked>
|
||||
<label for="custom_req">{{tr "Custom expression "}}</label>
|
||||
</div>
|
||||
<legend>{{tr "Network Requirements"}}</legend>
|
||||
<div class="row">
|
||||
<div class="large-12 columns text-center">
|
||||
<input type="radio" id="vnet_req" name="{{nicTabId}}_req_select" value="vnet_select">
|
||||
<label for="vnet_req">{{tr "Select Vnets "}}</label>
|
||||
<input type="radio" id="custom_req" name="{{nicTabId}}_req_select" value="custom" checked>
|
||||
<label for="custom_req">{{tr "Custom expression "}}</label>
|
||||
</div>
|
||||
<br>
|
||||
<div id="{{nicTabId}}_vnetTable" class="net_select" hidden>{{{vnetsTableAutoSelectHTML}}}</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<label for="{{nicTabId}}_SCHED_REQUIREMENTS">
|
||||
{{tr "Expression"}}
|
||||
{{{tip (tr "Boolean expression that rules out entries from the pool of datastores suitable to run this VM.")}}}
|
||||
</label>
|
||||
<input type="text" id="{{nicTabId}}_SCHED_REQUIREMENTS" name="requirements" />
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div id="{{nicTabId}}_vnetTable" class="net_select" hidden>{{{vnetsTableAutoSelectHTML}}}</div>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<label for="{{nicTabId}}_SCHED_REQUIREMENTS">
|
||||
{{tr "Expression"}}
|
||||
{{{tip (tr "Boolean expression that rules out entries from the pool of datastores suitable to run this VM.")}}}
|
||||
</label>
|
||||
<input type="text" id="{{nicTabId}}_SCHED_REQUIREMENTS" name="requirements" />
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div class="row autoContainer">
|
||||
<fieldset class="nic_rank">
|
||||
|
||||
@@ -37,6 +37,7 @@ define(function(require) {
|
||||
require("./vms-tab/dialogs/revert"),
|
||||
require("./vms-tab/dialogs/vnc"),
|
||||
require("./vms-tab/dialogs/spice"),
|
||||
require("./vms-tab/dialogs/guac"),
|
||||
require("./vms-tab/dialogs/saveas-template")
|
||||
];
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ define(function(require) {
|
||||
var MIGRATE_DIALOG_ID = require('./dialogs/migrate/dialogId');
|
||||
var VNC_DIALOG_ID = require('./dialogs/vnc/dialogId');
|
||||
var SPICE_DIALOG_ID = require('./dialogs/spice/dialogId');
|
||||
var GUAC_DIALOG_ID = require('./dialogs/guac/dialogId');
|
||||
var SAVE_AS_TEMPLATE_DIALOG_ID = require('./dialogs/saveas-template/dialogId');
|
||||
var UPDATECONF_FORM_ID = require('./form-panels/updateconf/formPanelId');
|
||||
|
||||
@@ -310,6 +311,52 @@ define(function(require) {
|
||||
},
|
||||
notify: true
|
||||
},
|
||||
"VM.guac_vnc" : {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
Sunstone.runAction("VM.startguac_action", elem, 'vnc');
|
||||
});
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
},
|
||||
},
|
||||
"VM.guac_rdp" : {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
Sunstone.runAction("VM.startguac_action", elem, 'rdp');
|
||||
});
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
},
|
||||
},
|
||||
"VM.guac_ssh" : {
|
||||
type: "custom",
|
||||
call: function() {
|
||||
$.each(Sunstone.getDataTable(TAB_ID).elements(), function(index, elem) {
|
||||
Sunstone.runAction("VM.startguac_action", elem, 'ssh');
|
||||
});
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
},
|
||||
},
|
||||
"VM.startguac_action" : {
|
||||
type: "single",
|
||||
call: OpenNebulaVM.guac,
|
||||
callback: function(request, response) {
|
||||
var dialog = Sunstone.getDialog(GUAC_DIALOG_ID);
|
||||
dialog.setElement(response);
|
||||
dialog.show();
|
||||
},
|
||||
error: function(req, resp) {
|
||||
Notifier.onError(req, resp);
|
||||
},
|
||||
notify: true
|
||||
},
|
||||
"VM.save_as_template" : {
|
||||
type: "single",
|
||||
call: function() {
|
||||
|
||||
@@ -212,9 +212,27 @@ define(function(require) {
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info vv-sunstone-info"
|
||||
},
|
||||
"VM.guac_vnc" : {
|
||||
type: "action",
|
||||
text: Locale.tr("GUAC VNC"),
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info vnc-sunstone-info"
|
||||
},
|
||||
"VM.guac_ssh" : {
|
||||
type: "action",
|
||||
text: Locale.tr("GUAC SSH"),
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info ssh-sunstone-info"
|
||||
},
|
||||
"VM.guac_rdp" : {
|
||||
type: "action",
|
||||
text: Locale.tr("GUAC RDP"),
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info rdp-sunstone-info"
|
||||
},
|
||||
"VM.save_rdp" : {
|
||||
type: "action",
|
||||
text: Locale.tr("RDP"),
|
||||
text: Locale.tr("RDP Client"),
|
||||
layout: "vmsremote_buttons",
|
||||
custom_classes: "only-sunstone-info rdp-sunstone-info"
|
||||
},
|
||||
|
||||
@@ -167,6 +167,8 @@ define(function(require) {
|
||||
$(".off_vms").text(this.offVms);
|
||||
|
||||
VMsTableUtils.tooltipCharters()
|
||||
|
||||
$("#rdp-buttons").foundation();
|
||||
}
|
||||
|
||||
function _initialize(opts) {
|
||||
@@ -175,7 +177,7 @@ define(function(require) {
|
||||
TabDataTable.prototype.initialize.call(this, opts);
|
||||
|
||||
//download virt-viewer file
|
||||
$('#' + this.dataTableId).on("click", '.w_file', function(){
|
||||
$('#' + this.dataTableId).on("click", '.w-file', function(){
|
||||
var data = $(this).data();
|
||||
|
||||
(data.hasOwnProperty("id") && data.hasOwnProperty("hostname") && data.hasOwnProperty("type") && data.hasOwnProperty("port"))
|
||||
@@ -190,7 +192,7 @@ define(function(require) {
|
||||
});
|
||||
|
||||
//download RDP file
|
||||
$('#' + this.dataTableId).on("click", '.rdp', function() {
|
||||
$('#' + this.dataTableId).on("click", '.save-rdp', function() {
|
||||
var data = $(this).data();
|
||||
|
||||
(data.hasOwnProperty("ip") && data.hasOwnProperty("name"))
|
||||
@@ -200,6 +202,36 @@ define(function(require) {
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#' + this.dataTableId).on("click", ".rdp", function() {
|
||||
var data = $(this).data();
|
||||
|
||||
(data.hasOwnProperty("id"))
|
||||
? Sunstone.runAction("VM.startguac_action", String(data.id), 'rdp')
|
||||
: Notifier.notifyError(Locale.tr("RDP - Invalid action"));
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#' + this.dataTableId).on("click", ".ssh", function() {
|
||||
var data = $(this).data();
|
||||
|
||||
(data.hasOwnProperty("id"))
|
||||
? Sunstone.runAction("VM.startguac_action", String(data.id), 'ssh')
|
||||
: Notifier.notifyError(Locale.tr("SSH - Invalid action"));
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#' + this.dataTableId).on("click", ".guac-vnc", function() {
|
||||
var data = $(this).data();
|
||||
|
||||
(data.hasOwnProperty("id"))
|
||||
? Sunstone.runAction("VM.startguac_action", String(data.id), 'vnc')
|
||||
: Notifier.notifyError(Locale.tr("VNC - Invalid action"));
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#' + this.dataTableId).on("click", '.vnc', function() {
|
||||
var data = $(this).data();
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
define(function(require) {
|
||||
/*
|
||||
DEPENDENCIES
|
||||
*/
|
||||
|
||||
var BaseDialog = require('utils/dialogs/dialog');
|
||||
var TemplateHTML = require('hbs!./guac/html');
|
||||
var Sunstone = require('sunstone');
|
||||
var GClient = require('utils/gclient');
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
var DIALOG_ID = require('./guac/dialogId');
|
||||
var TAB_ID = require('../tabId')
|
||||
|
||||
/*
|
||||
CONSTRUCTOR
|
||||
*/
|
||||
|
||||
function Dialog() {
|
||||
this.dialogId = DIALOG_ID;
|
||||
this.gClient = new GClient();
|
||||
|
||||
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(context) {
|
||||
this.gClient.connect(this.element);
|
||||
this.gClient.mouse(true);
|
||||
this.gClient.keyboard(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _onClose(context) {
|
||||
this.gClient.disconnect();
|
||||
this.gClient.mouse(false);
|
||||
this.gClient.keyboard(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
function _setElement(element) {
|
||||
this.element = element
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
define(function(require) {
|
||||
return 'guacVMDialog';
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
{{! Copyright 2002-2020, OpenNebula Project, OpenNebula Systems }}
|
||||
{{! }}
|
||||
{{! Licensed under the Apache License, Version 2.0 (the "License"); you may }}
|
||||
{{! not use this file except in compliance with the License. You may obtain }}
|
||||
{{! a copy of the License at }}
|
||||
{{! }}
|
||||
{{! http://www.apache.org/licenses/LICENSE-2.0 }}
|
||||
{{! }}
|
||||
{{! Unless required by applicable law or agreed to in writing, software }}
|
||||
{{! distributed under the License is distributed on an "AS IS" BASIS, }}
|
||||
{{! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }}
|
||||
{{! See the License for the specific language governing permissions and }}
|
||||
{{! limitations under the License. }}
|
||||
{{! -------------------------------------------------------------------------- }}
|
||||
|
||||
<div id="{{dialogId}}" class="reveal full" data-reveal>
|
||||
<div class="row">
|
||||
<div class="large-12 columns">
|
||||
<h5 class="subheader" id="guacamole_dialog">
|
||||
<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 secondary" data-close aria-label="{{tr "Close modal"}}" type="button" title="Close Guacamole">
|
||||
<i class="fas fa-times-circle"></i>
|
||||
</button>
|
||||
</span>
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div id="guacamole-display" class="guacamole-display"></div>
|
||||
</div>
|
||||
@@ -41,13 +41,20 @@ define(function(require) {
|
||||
StateActions.disableAllStateActions();
|
||||
StateActions.enableStateActions(element.STATE, element.LCM_STATE);
|
||||
|
||||
var isVNCSupported = OpenNebulaVM.isVNCSupported(element),
|
||||
isSPICESupported = OpenNebulaVM.isSPICESupported(element),
|
||||
isWFileSupported = OpenNebulaVM.isWFileSupported(element),
|
||||
isRDPSupported = OpenNebulaVM.isRDPSupported(element);
|
||||
var isVNCSupported = Boolean(OpenNebulaVM.isVNCSupported(element)),
|
||||
isSPICESupported = Boolean(OpenNebulaVM.isSPICESupported(element)),
|
||||
isWFileSupported = Boolean(OpenNebulaVM.isWFileSupported(element)),
|
||||
isRDPSupported = Boolean(OpenNebulaVM.isConnectionSupported(element, 'rdp')),
|
||||
isSSHSupported = Boolean(OpenNebulaVM.isConnectionSupported(element, 'ssh'));
|
||||
|
||||
// All remote buttons are disabled
|
||||
var allDisabled = (!isVNCSupported && !isSPICESupported && !isWFileSupported && !isRDPSupported);
|
||||
var allDisabled = (
|
||||
!isVNCSupported &&
|
||||
!isSPICESupported &&
|
||||
!isWFileSupported &&
|
||||
!isRDPSupported &&
|
||||
!isSSHSupported
|
||||
);
|
||||
$("#vmsremote_buttons").toggle(!allDisabled);
|
||||
|
||||
if (isVNCSupported) {
|
||||
@@ -64,10 +71,13 @@ define(function(require) {
|
||||
}
|
||||
|
||||
// Show / hide virt-viewer button
|
||||
$(".vv-sunstone-info").toggle(Boolean(isWFileSupported));
|
||||
$(".vv-sunstone-info").toggle(!!isWFileSupported);
|
||||
|
||||
// Show / hide rdp button
|
||||
$(".rdp-sunstone-info").toggle(Boolean(isRDPSupported));
|
||||
$(".rdp-sunstone-info").toggle(!!isRDPSupported);
|
||||
|
||||
// Show / hide ssh button
|
||||
$(".ssh-sunstone-info").toggle(!!isSSHSupported);
|
||||
|
||||
if(config &&
|
||||
config["system_config"] &&
|
||||
|
||||
@@ -218,9 +218,13 @@ define(function(require) {
|
||||
actions = '';
|
||||
}
|
||||
|
||||
// RDP icon
|
||||
var rdp = OpenNebulaVM.isRDPSupported(element);
|
||||
rdp && (actions += OpenNebulaVM.buttonRDP(rdp.IP, element));
|
||||
// RDP icons
|
||||
var rdp = OpenNebulaVM.isConnectionSupported(element, 'rdp');
|
||||
rdp && (actions += OpenNebulaVM.dropdownRDP(element.ID, rdp.IP, element));
|
||||
|
||||
// SSH icon
|
||||
var ssh = OpenNebulaVM.isConnectionSupported(element, 'ssh');
|
||||
ssh && (actions += OpenNebulaVM.buttonSSH(element.ID));
|
||||
|
||||
var cpuMonitoring = 0, memoryMonitoring = 0;
|
||||
if (element.MONITORING) {
|
||||
@@ -275,7 +279,7 @@ define(function(require) {
|
||||
hostname,
|
||||
OpenNebulaVM.ipsDropdown(element),
|
||||
Humanize.prettyTimeDatatable(element.STIME),
|
||||
actions && "<div style='display: inline-flex'>"+actions+"</div>",
|
||||
actions && "<div style='display: flex; align-items: end;'>"+actions+"</div>",
|
||||
TemplateUtils.htmlEncode(TemplateUtils.templateToString(element)),
|
||||
(LabelsUtils.labelsStr(element[TEMPLATE_ATTR])||''),
|
||||
btoa(unescape(encodeURIComponent(JSON.stringify(search)))),
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */
|
||||
/* */
|
||||
/* Licensed under the Apache License, Version 2.0 (the "License"); you may */
|
||||
/* not use this file except in compliance with the License. You may obtain */
|
||||
/* a copy of the License at */
|
||||
/* */
|
||||
/* http://www.apache.org/licenses/LICENSE-2.0 */
|
||||
/* */
|
||||
/* Unless required by applicable law or agreed to in writing, software */
|
||||
/* distributed under the License is distributed on an "AS IS" BASIS, */
|
||||
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
|
||||
/* See the License for the specific language governing permissions and */
|
||||
/* limitations under the License. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
define(function(require) {
|
||||
/**
|
||||
* DEPENDENCIES
|
||||
*/
|
||||
require("guacamole-common-js")
|
||||
var Config = require("sunstone-config");
|
||||
var Notifier = require("utils/notifier");
|
||||
var Locale = require("utils/locale");
|
||||
|
||||
/**
|
||||
* CONSTRUCTOR
|
||||
*/
|
||||
function GClient() {
|
||||
this._display = null;
|
||||
this._client = null;
|
||||
this._mouse = null;
|
||||
this._keyboard = null;
|
||||
this._clientErrorText = Locale.tr("The OpenNebula service for remote console is not running, please contact your administrator.")
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
GClient.prototype = {
|
||||
"connect": connect,
|
||||
"mouse": mouse,
|
||||
"keyboard": keyboard,
|
||||
"snapshot": getCanvas,
|
||||
"disconnect": disconnect,
|
||||
};
|
||||
|
||||
return GClient;
|
||||
|
||||
function setStatus(state = "") {
|
||||
$('#guacamole-state').text(state).animate();
|
||||
}
|
||||
|
||||
function setLoading(isLoading = false) {
|
||||
$('#guacamole-loading')[isLoading ? 'fadeIn' : 'fadeOut']('fast');
|
||||
}
|
||||
|
||||
function connect(response) {
|
||||
var that = this;
|
||||
|
||||
if (!response.token) {
|
||||
Notifier.notifyError(this._clientErrorText)
|
||||
return null;
|
||||
}
|
||||
else setLoading(true);
|
||||
|
||||
var port = Config.guacPort || '29877';
|
||||
var host = window.location.hostname || 'localhost';
|
||||
var wsprotocol = (window.location.protocol == 'https:') ? 'wss:' : 'ws:';
|
||||
|
||||
var tunnel = new Guacamole.WebSocketTunnel(wsprotocol + '//' + host + ':' + port + '/guacamole')
|
||||
var guac = this._client = new Guacamole.Client(tunnel);
|
||||
|
||||
// Client display
|
||||
this._display = $("#guacamole-display");
|
||||
this._display.html(this._client.getDisplay().getElement());
|
||||
|
||||
// client error handler
|
||||
guac.onerror = function() {
|
||||
Notifier.notifyError(that._clientErrorText)
|
||||
};
|
||||
|
||||
// websoket error handler
|
||||
tunnel.onerror = function() {
|
||||
disconnect();
|
||||
Notifier.notifyError(that._clientErrorText)
|
||||
};
|
||||
|
||||
guac.onstatechange = function(state) {
|
||||
switch (state) {
|
||||
case 0:
|
||||
setStatus("Client IDLE");
|
||||
setLoading(true);
|
||||
break;
|
||||
case 1:
|
||||
setStatus("Client CONNECTING");
|
||||
setLoading(true);
|
||||
break;
|
||||
case 2:
|
||||
setStatus("Client WAITING");
|
||||
setLoading(true);
|
||||
break;
|
||||
case 3:
|
||||
setStatus("Client CONNECTED");
|
||||
setLoading(false);
|
||||
setTimeout(function() {
|
||||
rescale(that);
|
||||
guac.getDisplay().showCursor(false);
|
||||
}, 100);
|
||||
break;
|
||||
case 4:
|
||||
setStatus("Client DISCONNECTING");
|
||||
setLoading(true);
|
||||
break;
|
||||
case 5:
|
||||
setStatus("Client DISCONNECTED");
|
||||
setLoading(false);
|
||||
break;
|
||||
default:
|
||||
setStatus("Client ERROR");
|
||||
setLoading(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Connect
|
||||
var params = [
|
||||
'token=' + response.token,
|
||||
'width=' + this._display.width()
|
||||
];
|
||||
|
||||
try {
|
||||
guac.connect(params.join('&'));
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
// Disconnect on close
|
||||
window.onunload = function () {
|
||||
disconnect();
|
||||
};
|
||||
|
||||
// Scale display when window resize
|
||||
$(window).resize(function () {
|
||||
rescale(that);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
GuacamoleWrapper.mouse
|
||||
- handles mouse interaction
|
||||
*/
|
||||
function mouse(enable = true) {
|
||||
var that = this;
|
||||
if (!this._client) return;
|
||||
|
||||
if (enable) {
|
||||
this._mouse = new Guacamole.Mouse(this._client.getDisplay().getElement());
|
||||
|
||||
// apply sendState function
|
||||
this._mouse.onmousedown =
|
||||
this._mouse.onmouseup = function(mouseState) {
|
||||
that._client.sendMouseState(mouseState);
|
||||
};
|
||||
|
||||
this._mouse.onmousemove = function(mouseState) {
|
||||
mouseState.y = mouseState.y / that._client.getDisplay().getScale();
|
||||
mouseState.x = mouseState.x / that._client.getDisplay().getScale();
|
||||
that._client.sendMouseState(mouseState);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GuacamoleWrapper.keyboard
|
||||
- handles keyboard interaction
|
||||
*/
|
||||
function keyboard(enable = true) {
|
||||
var that = this;
|
||||
if (!this._client) return;
|
||||
|
||||
if (enable) {
|
||||
this._keyboard = new Guacamole.Keyboard(document);
|
||||
this._keyboard.onkeydown = function(keysym) {
|
||||
that._client.sendKeyEvent(1, keysym);
|
||||
}
|
||||
this._keyboard.onkeyup = function(keysym) {
|
||||
that._client.sendKeyEvent(0, keysym);
|
||||
}
|
||||
} else {
|
||||
this._keyboard.onkeydown = null;
|
||||
this._keyboard.onkeyup = null;
|
||||
this._keyboard = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
GuacamoleWrapper.getCanvas
|
||||
- shortcut for returning default guac layer (active tunnel viewport)
|
||||
*/
|
||||
function getCanvas() {
|
||||
return (this._client) ? this._client.getDisplay().getDefaultLayer().getCanvas() : false;
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (this._client) {
|
||||
this._client.disconnect();
|
||||
this._scale = 1;
|
||||
$(window).off('resize');
|
||||
setStatus()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the scale of the attached Guacamole.Client based on current window
|
||||
* size and "auto-fit" setting.
|
||||
*/
|
||||
function rescale(thisContext) {
|
||||
var gclientDisplay = thisContext._client.getDisplay();
|
||||
|
||||
//Get screen resolution.
|
||||
var origHeigth = Math.max(gclientDisplay.getHeight(), 1);
|
||||
var origWidth = Math.max(gclientDisplay.getWidth(), 1);
|
||||
|
||||
var htmlWidth = thisContext._display.width();
|
||||
var htmlHeigth = thisContext._display.height();
|
||||
|
||||
var xscale = htmlWidth / origWidth;
|
||||
var yscale = htmlHeigth / origHeigth;
|
||||
|
||||
// This is done to handle both X and Y axis slacing
|
||||
var scale = Math.min(yscale, xscale);
|
||||
|
||||
// Limit to 1
|
||||
scale = Math.min(scale, 1);
|
||||
|
||||
if (scale !== 0) {
|
||||
gclientDisplay.scale(scale);
|
||||
|
||||
// Set minimum height container display
|
||||
thisContext._display.css('min-height', gclientDisplay.getHeight());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -297,6 +297,7 @@ define(function(require) {
|
||||
var displayType = true;
|
||||
var displaySelection = true;
|
||||
var displayRDP = true;
|
||||
var displaySSH = true;
|
||||
if(
|
||||
template_json &&
|
||||
template_json.VMTEMPLATE &&
|
||||
@@ -306,9 +307,11 @@ define(function(require) {
|
||||
var templateType = template_json.VMTEMPLATE.TEMPLATE.SUNSTONE.NETWORK_ALIAS;
|
||||
var templateSelection = template_json.VMTEMPLATE.TEMPLATE.SUNSTONE.NETWORK_AUTO;
|
||||
var templateRDP = template_json.VMTEMPLATE.TEMPLATE.SUNSTONE.NETWORK_RDP;
|
||||
var templateSSH = template_json.VMTEMPLATE.TEMPLATE.SUNSTONE.NETWORK_SSH;
|
||||
displayType = templateType && templateType.toUpperCase()==='NO'? false : true;
|
||||
displaySelection = templateSelection && templateSelection.toUpperCase()==='NO'? false : true;
|
||||
displayRDP = templateRDP && templateRDP.toUpperCase()==='NO'? false : true;
|
||||
displaySSH = templateSSH && templateSSH.toUpperCase()==='NO'? false : true;
|
||||
}
|
||||
var dd_context = $(TemplateDD({
|
||||
vnetsTableHTML: vnetsTable.dataTableHTML,
|
||||
@@ -318,7 +321,8 @@ define(function(require) {
|
||||
options: options,
|
||||
displayType: displayType,
|
||||
displaySelection: displaySelection,
|
||||
displayRDP: displayRDP
|
||||
displayRDP: displayRDP,
|
||||
displaySSH: displaySSH,
|
||||
})).appendTo(context);
|
||||
|
||||
dd_context["nic_id"] = nicId;
|
||||
@@ -468,7 +472,13 @@ define(function(require) {
|
||||
$("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_rdp", dd_context).on("change", function() {
|
||||
const isRDPActivated = $(this).prop('checked');
|
||||
const idAccordion = "#provision_accordion_dd_" + dd_context["dd_id"];
|
||||
_hide_rdp(idAccordion, isRDPActivated, context);
|
||||
_hide_connection(idAccordion, isRDPActivated, context, 'rdp');
|
||||
});
|
||||
|
||||
$("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_ssh", dd_context).on("change", function() {
|
||||
const isSSHActivated = $(this).prop('checked');
|
||||
const idAccordion = "#provision_accordion_dd_" + dd_context["dd_id"];
|
||||
_hide_connection(idAccordion, isSSHActivated, context, 'ssh');
|
||||
});
|
||||
|
||||
if ( options.nic && options.nic["NETWORK_MODE"] && options.nic["NETWORK_MODE"] === "auto" ) {
|
||||
@@ -535,6 +545,17 @@ define(function(require) {
|
||||
$("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_rdp", context).prop("checked", isRDPActivated);
|
||||
_enableRDP("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context)
|
||||
|
||||
// fill ssh connection
|
||||
const isSSHActivated = (
|
||||
options.nic &&
|
||||
options.nic["SSH"] &&
|
||||
options.nic["SSH"] === "YES" &&
|
||||
$("fieldset#ssh_connection input:not(#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_ssh):checked", context).length === 0
|
||||
) ? true : false;
|
||||
|
||||
$("input#provision_accordion_dd_" + provision_nic_accordion_dd_id + "_ssh", context).prop("checked", isSSHActivated);
|
||||
_enableSSH("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context)
|
||||
|
||||
provision_nic_accordion_dd_id += 1;
|
||||
|
||||
vnetsTable.initialize();
|
||||
@@ -584,6 +605,7 @@ define(function(require) {
|
||||
nicId --;
|
||||
|
||||
_enableRDP("#provision_accordion_dd_"+dd_context["nic_id"], context)
|
||||
_enableSSH("#provision_accordion_dd_"+dd_context["nic_id"], context)
|
||||
|
||||
return false;
|
||||
});
|
||||
@@ -637,6 +659,7 @@ define(function(require) {
|
||||
nicId ++;
|
||||
|
||||
_enableRDP("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context)
|
||||
_enableSSH("#provision_accordion_dd_" + provision_nic_accordion_dd_id, context)
|
||||
});
|
||||
|
||||
if (options.click_add_button == true){
|
||||
@@ -704,9 +727,11 @@ define(function(require) {
|
||||
});
|
||||
}
|
||||
|
||||
function _hide_rdp(idAccordion, isRDPActivated, context) {
|
||||
$(".accordion-item > div:not(" + idAccordion + ") fieldset#rdp_connection", context).each(function() {
|
||||
if (isRDPActivated) {
|
||||
function _hide_connection(idAccordion, isActivated, context, typeConnection) {
|
||||
fieldsetSelector = "fieldset#" + typeConnection + "_connection";
|
||||
|
||||
$(".accordion-item > div:not(" + idAccordion + ") " + fieldsetSelector, context).each(function() {
|
||||
if (isActivated) {
|
||||
$(this).hide();
|
||||
} else {
|
||||
$(this).show();
|
||||
@@ -720,4 +745,11 @@ define(function(require) {
|
||||
if (canRDP) $("fieldset#rdp_connection", context).has("input:not(:checked)").show();
|
||||
else $("fieldset#rdp_connection", context).has("input:not(:checked)").hide();
|
||||
}
|
||||
|
||||
function _enableSSH(idAccordion, context) {
|
||||
const canSSH = $("fieldset#ssh_connection input[type='checkbox']:not(" + idAccordion + "_ssh):checked", context).length === 0;
|
||||
|
||||
if (canSSH) $("fieldset#ssh_connection", context).has("input:not(:checked)").show();
|
||||
else $("fieldset#ssh_connection", context).has("input:not(:checked)").hide();
|
||||
}
|
||||
});
|
||||
@@ -23,61 +23,79 @@
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}" class="accordion-content nic-section-entry" data-tab-content>
|
||||
<div class="only_template">
|
||||
<div class="row autoContainer">
|
||||
<div class="large-12 large-centered columns" style="margin:0px;display:inline-flex;justify-content:start;">
|
||||
{{#if displayType}}
|
||||
<fieldset class="small-12 large-4 columns" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type_section">
|
||||
<legend style="margin:0px;">{{tr "Interface type"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Alias"}}
|
||||
{{{tip (tr "The NIC will be attached as an alias")}}}
|
||||
</label>
|
||||
|
||||
<span id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_no_alias" class="radius warning label">
|
||||
<i class="fas fa-exclamation-triangle"/> {{tr "NIC has alias"}}
|
||||
</span>
|
||||
<select id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_alias_parent" style="width: 200px; margin-left: 5px"></select>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if displaySelection}}
|
||||
<fieldset class="small-12 large-4 columns" style="margin:0px;">
|
||||
<legend style="margin:0px;">{{tr "Network selection"}}</legend>
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_selection">
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_mode" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_mode">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Automatic selection"}}
|
||||
{{{tip (tr "The Schedule will decide which is the best virtual network")}}}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if displayRDP}}
|
||||
<fieldset id="rdp_connection" class="small-12 large-4 columns" style="margin:0px;">
|
||||
<legend style="margin:0px;">{{tr "RDP connection"}}</legend>
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp_wrapper">
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "RDP will be enabled in this network")}}}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if displayType}}
|
||||
<fieldset class="small-12 large-6 columns" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type_section">
|
||||
<legend style="margin:0px;">{{tr "Interface type"}}</legend>
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_interface_type">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Alias"}}
|
||||
{{{tip (tr "The NIC will be attached as an alias")}}}
|
||||
</label>
|
||||
|
||||
<span id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_no_alias" class="radius warning label">
|
||||
<i class="fas fa-exclamation-triangle"/> {{tr "NIC has alias"}}
|
||||
</span>
|
||||
<select id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_alias_parent" style="width: 200px; margin-left: 5px"></select>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if displaySelection}}
|
||||
<fieldset class="small-12 large-6 columns" style="margin:0px;">
|
||||
<legend style="margin:0px;">{{tr "Network selection"}}</legend>
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_selection">
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_mode" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_network_mode">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Automatic selection"}}
|
||||
{{{tip (tr "The Schedule will decide which is the best virtual network")}}}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="row autoContainer">
|
||||
{{#if displayRDP}}
|
||||
<fieldset id="rdp_connection" class="small-12 large-6 columns" style="margin:0px;">
|
||||
<legend style="margin:0px;">{{tr "RDP connection"}}</legend>
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp_wrapper">
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_rdp">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "RDP will be enabled in this network")}}}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
{{#if displaySSH}}
|
||||
<fieldset id="ssh_connection" class="small-12 large-6 columns" style="margin:0px;">
|
||||
<legend style="margin:0px;">{{tr "SSH connection"}}</legend>
|
||||
<div id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_ssh_wrapper">
|
||||
<div class="switch left">
|
||||
<input class="switch-input" id="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_ssh" type="checkbox">
|
||||
<label class="switch-paddle" for="provision_accordion_dd_{{provision_nic_accordion_dd_id}}_ssh">
|
||||
</label>
|
||||
</div>
|
||||
<label class="left">
|
||||
|
||||
{{tr "Activate"}}
|
||||
{{{tip (tr "SSH will be enabled in this network")}}}
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="auto">
|
||||
<fieldset>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"sprintf": "~1.0.3",
|
||||
"jquery-ui": "~1.12.1",
|
||||
"wickedpicker": "https://github.com/OpenNebula/sunstone-deps.git#9398b3f",
|
||||
"guacamole-common-js": "https://github.com/OpenNebula/sunstone-deps.git#1633556e63",
|
||||
"webauthn-json": "https://registry.npmjs.org/@github/webauthn-json/-/webauthn-json-0.4.1.tgz"
|
||||
},
|
||||
"authors": [
|
||||
|
||||
@@ -16,6 +16,7 @@ usage() {
|
||||
|
||||
clean() {
|
||||
rm -rf dist node_modules bower_components
|
||||
rm -rf ../guac/dist ../guac/node_modules
|
||||
}
|
||||
|
||||
dependencies() {
|
||||
@@ -46,6 +47,13 @@ install_enterprise_patch() {
|
||||
done
|
||||
}
|
||||
|
||||
install_guac() {
|
||||
GUAC_DIR="../guac"
|
||||
|
||||
npm i --prefix $GUAC_DIR
|
||||
npm run build --prefix $GUAC_DIR
|
||||
}
|
||||
|
||||
install_patch() {
|
||||
|
||||
npm install
|
||||
@@ -78,6 +86,7 @@ install_patch() {
|
||||
mv ./main.js dist/main.js
|
||||
fi
|
||||
|
||||
install_guac
|
||||
}
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -159,4 +159,17 @@ progress{
|
||||
background-color: $busy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.guacamole-display {
|
||||
cursor: none;
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
place-content: center;
|
||||
background-color: #282828;
|
||||
& > div {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
@@ -136,14 +136,24 @@ button.search-dropdown {
|
||||
border-style: none !important;
|
||||
}
|
||||
|
||||
button.remote_vm {
|
||||
button.remote-vm {
|
||||
margin: 0 5px;
|
||||
font-size: 1.2em;
|
||||
font-size: 1.4em;
|
||||
|
||||
&.spice i {
|
||||
-webkit-text-stroke: 0.7px;
|
||||
}
|
||||
|
||||
&.ssh {
|
||||
border: .15em solid $anchor-color;
|
||||
padding: .16em .2em;
|
||||
font-size: .8em;
|
||||
|
||||
&:hover {
|
||||
border-color: $anchor-color-hover;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: $anchor-color;
|
||||
}
|
||||
@@ -159,3 +169,15 @@ button.remote_vm {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a.rdp,
|
||||
a.save-rdp {
|
||||
display: flex !important;
|
||||
align-items: baseline;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba($color: $anchor-color-hover, $alpha: .6);
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
@@ -251,9 +251,11 @@ if $conf[:webauthn_avail]
|
||||
end
|
||||
|
||||
#start VNC proxy
|
||||
|
||||
$vnc = OpenNebulaVNC.new($conf, logger)
|
||||
|
||||
#init Guacamole server
|
||||
$guac = OpenNebulaGuac.new($conf, logger)
|
||||
|
||||
configure do
|
||||
set :run, false
|
||||
set :vnc, $vnc
|
||||
@@ -1035,6 +1037,16 @@ post '/vm/:id/startvnc' do
|
||||
@SunstoneServer.startvnc(vm_id, $vnc)
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Start Guacamole Session for a target VM
|
||||
##############################################################################
|
||||
post '/vm/:id/guac/:type' do
|
||||
vm_id = params[:id]
|
||||
type_connection = params[:type]
|
||||
|
||||
@SunstoneServer.startguac(vm_id, type_connection, $guac)
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
# Perform an action on a Resource
|
||||
##############################################################################
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
'threshold_min' : '<%= $conf[:threshold_min] %>',
|
||||
'threshold_low' : '<%= $conf[:threshold_low] %>',
|
||||
'threshold_high' : '<%= $conf[:threshold_high] %>',
|
||||
'guac_port' : '<%= $conf[:guac_port] %>',
|
||||
},
|
||||
'system_config' : {
|
||||
'paginate': '<%= $conf[:paginate] %>',
|
||||
|
||||
Reference in new issue
Block a user