mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-11 05:17:41 +03:00
* Linting Guacamole * Guacamole with fireedge * Guacd start with fireedge
This commit is contained in:
parent
313b74e651
commit
01653bdcf1
@ -904,7 +904,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/opennebula_guac.rb \
|
||||
src/sunstone/opennebula_vmrc.rb \
|
||||
src/sunstone/OpenNebulaAddons.rb \
|
||||
src/vmm_mad/remotes/vcenter/vcenter_driver.rb \
|
||||
|
@ -237,7 +237,6 @@ AllCops:
|
||||
- src/authm_mad/remotes/x509/x509_auth.rb
|
||||
- src/authm_mad/remotes/ssh/ssh_auth.rb
|
||||
- src/sunstone/sunstone-server.rb
|
||||
- src/sunstone/OpenNebulaGuac.rb
|
||||
- src/sunstone/test/spec/spec_helper.rb
|
||||
- src/sunstone/test/spec/vnet_spec.rb
|
||||
- src/sunstone/test/spec/image_spec.rb
|
||||
|
@ -22,14 +22,34 @@ if [ -z "$ONE_LOCATION" ]; then
|
||||
FIREEDGE_LOCK_FILE=/var/lock/one/.fireedge.lock
|
||||
FIREEDGE_LOG=/var/log/one/fireedge.log
|
||||
FIREEDGE_LOG_ERROR=/var/log/one/fireedge.error
|
||||
GUACD_PID=/var/run/one/guacd.pid
|
||||
else
|
||||
FIREEDGE_PID=$ONE_LOCATION/var/fireedge.pid
|
||||
FIREEDGE_SERVER=$ONE_LOCATION/lib/fireedge/dist/index.js
|
||||
FIREEDGE_LOCK_FILE=$ONE_LOCATION/var/.fireedge.lock
|
||||
FIREEDGE_LOG=$ONE_LOCATION/var/fireedge.log
|
||||
FIREEDGE_LOG_ERROR=$ONE_LOCATION/var/fireedge.error
|
||||
GUACD_PID=$ONE_LOCATION/var/guacd.pid
|
||||
fi
|
||||
|
||||
GUACD_BIN=guacd
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
setup()
|
||||
{
|
||||
if [ -f $FIREEDGE_LOCK_FILE ]; then
|
||||
@ -56,7 +76,7 @@ start()
|
||||
|
||||
touch $FIREEDGE_LOCK_FILE
|
||||
|
||||
# Start the oneflow-server daemon
|
||||
# Start the fireedge-server daemon
|
||||
node $FIREEDGE_SERVER >$FIREEDGE_LOG 2>$FIREEDGE_LOG_ERROR &
|
||||
|
||||
LASTRC=$?
|
||||
@ -74,8 +94,8 @@ start()
|
||||
ps $LASTPID > /dev/null 2>&1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error executing fireedge-server."
|
||||
echo "Check $FIREEDGE_LOG_ERROR and $FIREEDGE_LOG for more information"
|
||||
echo "Error executing fireedge-server."
|
||||
echo "Check $FIREEDGE_LOG_ERROR and $FIREEDGE_LOG for more information"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@ -96,18 +116,71 @@ stop()
|
||||
rm -f $FIREEDGE_LOCK_FILE &> /dev/null
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start_guacd
|
||||
setup
|
||||
start
|
||||
echo "fireedge-server started"
|
||||
;;
|
||||
stop)
|
||||
stop_guacd
|
||||
stop
|
||||
echo "fireedge-server stopped"
|
||||
;;
|
||||
restart)
|
||||
stop_guacd 2> /dev/null
|
||||
stop 2> /dev/null
|
||||
start_guacd
|
||||
setup
|
||||
start
|
||||
echo "fireedge-server restarted"
|
||||
|
@ -1,325 +0,0 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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
|
||||
|
@ -1,196 +0,0 @@
|
||||
#!/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
|
@ -158,6 +158,9 @@
|
||||
:vnc_request_password: false
|
||||
:allow_vnc_federation: no
|
||||
|
||||
# guacamole daemon port
|
||||
:guacd_port: 4822
|
||||
|
||||
# Login Session Length in seconds, defaults to 1 hour.
|
||||
#:session_expire_time: 3600
|
||||
|
||||
@ -271,6 +274,3 @@
|
||||
|
||||
# Number of tries when connecting to fireedge_endpoint
|
||||
:max_waiting_tries: 5
|
||||
|
||||
:guac_port: 29877
|
||||
:guacd_port: 4822
|
@ -20,7 +20,7 @@ require 'OpenNebulaJSON'
|
||||
include OpenNebulaJSON
|
||||
|
||||
require 'OpenNebulaVNC'
|
||||
require 'OpenNebulaGuac'
|
||||
require 'opennebula_guac'
|
||||
require 'opennebula_vmrc'
|
||||
require 'OpenNebulaAddons'
|
||||
require 'OpenNebulaJSON/JSONUtils'
|
||||
|
362
src/sunstone/opennebula_guac.rb
Normal file
362
src/sunstone/opennebula_guac.rb
Normal file
@ -0,0 +1,362 @@
|
||||
# -------------------------------------------------------------------------- #
|
||||
# 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 for Guacamole connection configuration
|
||||
class OpenNebulaGuac
|
||||
|
||||
attr_reader :proxy_port
|
||||
|
||||
def initialize(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 lockfile?
|
||||
return error(400, 'Guacamole server is not running')
|
||||
end
|
||||
|
||||
unless running?
|
||||
return error(400, 'Guacamole server is not running')
|
||||
end
|
||||
|
||||
unless 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'])
|
||||
error_message = "Wrong state (#{vm_resource['LCM_STATE']})
|
||||
to open a Guacamole session"
|
||||
return error(400, error_message)
|
||||
end
|
||||
|
||||
unless %w[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 = nil
|
||||
case type_connection.downcase
|
||||
when 'vnc' then settings = get_config_vnc(vm_resource)
|
||||
when 'rdp' then settings = get_config_rdp(vm_resource)
|
||||
when 'ssh' then settings = 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 running?
|
||||
STDOUT.puts 'Guacamole server is running'
|
||||
true
|
||||
else
|
||||
STDOUT.puts 'Guacamole server is NOT running'
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def error(code, msg)
|
||||
unless @options[:json_error]
|
||||
return [code, msg]
|
||||
end
|
||||
|
||||
[code, OpenNebula::Error.new(msg).to_json]
|
||||
end
|
||||
|
||||
def 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 get_guac_pid running?
|
||||
|
||||
def 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 get_guacd_pid guacd_running?
|
||||
|
||||
def 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 do
|
||||
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
|
||||
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
|
||||
error_message = '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'
|
||||
return error(400, error_message)
|
||||
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"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[RDP='YES'][1]/IP"]
|
||||
|
||||
if hostname.nil?
|
||||
error_message = 'Wrong configuration. Cannot find a NIC with RDP'
|
||||
return { :error => error(400, error_message) }
|
||||
end
|
||||
|
||||
{
|
||||
'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"] ||
|
||||
vm_resource["TEMPLATE/NIC_ALIAS[SSH='YES'][1]/IP"]
|
||||
|
||||
if hostname.nil?
|
||||
error_message = 'Wrong configuration. Cannot find a NIC with SSH'
|
||||
return { :error => error(400, error_message) }
|
||||
end
|
||||
|
||||
{
|
||||
'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)
|
||||
|
||||
# OpenSSL::Random.random_bytes(32)
|
||||
key = 'LSIOGCKYLSIOGCKYLSIOGCKYLSIOGCKY'
|
||||
|
||||
cipher = OpenSSL::Cipher.new('aes-256-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
|
||||
}
|
||||
|
||||
Base64.strict_encode64(token.to_json).encode('utf-8').strip
|
||||
end
|
||||
|
||||
end
|
@ -169,7 +169,6 @@ 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"]);
|
||||
|
@ -63,9 +63,13 @@ define(function(require) {
|
||||
}
|
||||
else setLoading(true);
|
||||
|
||||
var port = Config.guacPort || '29877';
|
||||
var host = window.location.hostname || 'localhost';
|
||||
var wsprotocol = (window.location.protocol == 'https:') ? 'wss:' : 'ws:';
|
||||
var fireedge_protocol = Config.fireedgeEndpoint.split("//")[0];
|
||||
var fireedge_host = Config.fireedgeEndpoint.split("//")[1].split(":")[0];
|
||||
var fireedge_port = Config.fireedgeEndpoint.split("//")[1].split(":")[1];
|
||||
|
||||
var port = fireedge_port || '2616';
|
||||
var host = fireedge_host || 'localhost';
|
||||
var wsprotocol = (fireedge_protocol == 'https:') ? 'wss:' : 'ws:';
|
||||
|
||||
var tunnel = new Guacamole.WebSocketTunnel(wsprotocol + '//' + host + ':' + port + '/guacamole')
|
||||
var guac = this._client = new Guacamole.Client(tunnel);
|
||||
|
@ -261,7 +261,7 @@ end
|
||||
$vnc = OpenNebulaVNC.new($conf, logger)
|
||||
|
||||
#init Guacamole server
|
||||
$guac = OpenNebulaGuac.new($conf, logger)
|
||||
$guac = OpenNebulaGuac.new(logger)
|
||||
|
||||
#init VMRC server
|
||||
$vmrc = OpenNebulaVMRC.new(logger)
|
||||
|
@ -41,7 +41,6 @@
|
||||
'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] %>',
|
||||
|
Loading…
Reference in New Issue
Block a user