mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
337 lines
9 KiB
Ruby
337 lines
9 KiB
Ruby
|
# -------------------------------------------------------------------------- #
|
||
|
# Copyright 2002-2014, OpenNebula Project (OpenNebula.org), C12G Labs #
|
||
|
# #
|
||
|
# 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'
|
||
|
|
||
|
|
||
|
#if !ONE_LOCATION
|
||
|
NOVNC_LOCK_FILE = "/var/lock/.novnc.lock"
|
||
|
#else
|
||
|
# NOVNC_LOCK_FILE= ONE_LOCATION + "/var/.novnc.lock"
|
||
|
#end
|
||
|
|
||
|
TOKEN_EXPIRE_SECONDS = 4
|
||
|
|
||
|
VNC_STATES = [
|
||
|
#0, #LCM_INIT
|
||
|
#1, #PROLOG
|
||
|
#2, #BOOT
|
||
|
"3", #RUNNING
|
||
|
"4", #MIGRATE
|
||
|
#5, #SAVE_STOP
|
||
|
#6, #SAVE_SUSPEND
|
||
|
#7, #SAVE_MIGRATE
|
||
|
#8, #PROLOG_MIGRATE
|
||
|
#9, #PROLOG_RESUME
|
||
|
#10, #EPILOG_STOP
|
||
|
#11, #EPILOG
|
||
|
"12", #SHUTDOWN
|
||
|
"13", #CANCEL
|
||
|
#14, #FAILURE
|
||
|
#15, #CLEANUP_RESUBMIT
|
||
|
"16", #UNKNOWN
|
||
|
"17", #HOTPLUG
|
||
|
"18", #SHUTDOWN_POWEROFF
|
||
|
#19, #BOOT_UNKNOWN
|
||
|
#20, #BOOT_POWEROFF
|
||
|
#21, #BOOT_SUSPENDED
|
||
|
#22, #BOOT_STOPPED
|
||
|
#23, #CLEANUP_DELETE
|
||
|
"24", #HOTPLUG_SNAPSHOT
|
||
|
"25", #HOTPLUG_NIC
|
||
|
"26", #HOTPLUG_SAVEAS
|
||
|
"27", #HOTPLUG_SAVEAS_POWEROFF
|
||
|
"28", #HOTPLUG_SAVEAS_SUSPENDED
|
||
|
"29" #SHUTDOWN_UNDEPLOY
|
||
|
#30, #EPILOG_UNDEPLOY
|
||
|
#31, #PROLOG_UNDEPLOY
|
||
|
#32 #BOOT_UNDEPLOY
|
||
|
]
|
||
|
|
||
|
VAR_LOCATION = Dir.pwd + "/extras/noVNC"
|
||
|
SHARE_LOCATION = Dir.pwd + "/extras/noVNC"
|
||
|
class OpenNebulaVNC
|
||
|
|
||
|
attr_reader :proxy_port
|
||
|
|
||
|
def initialize(config, logger, options = {})
|
||
|
opts={ :json_errors => true,
|
||
|
:token_folder_name => 'sunstone_vnc_tokens'}.merge(options)
|
||
|
|
||
|
@pipe = nil
|
||
|
@token_folder = File.join(VAR_LOCATION, opts[:token_folder_name])
|
||
|
@proxy_path = File.join(SHARE_LOCATION, "websockify/websocketproxy.py")
|
||
|
@proxy_port = config[:vnc_proxy_port]
|
||
|
|
||
|
@proxy_ipv6 = config[:vnc_proxy_ipv6]
|
||
|
|
||
|
@wss = config[:vnc_proxy_support_wss]
|
||
|
|
||
|
@lock_file = NOVNC_LOCK_FILE
|
||
|
|
||
|
if (@wss == "yes") || (@wss == "only") || (@wss == true)
|
||
|
@enable_wss = true
|
||
|
@cert = config[:vnc_proxy_cert]
|
||
|
@key = config[:vnc_proxy_key]
|
||
|
else
|
||
|
@enable_wss = false
|
||
|
end
|
||
|
@options = opts
|
||
|
@logger = logger
|
||
|
end
|
||
|
|
||
|
def start
|
||
|
if is_running?
|
||
|
message="VNC server already running"
|
||
|
STDERR.puts message
|
||
|
@logger.info message
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
create_token_dir
|
||
|
|
||
|
proxy_options = "--target-config=#{@token_folder} "
|
||
|
|
||
|
if @enable_wss
|
||
|
proxy_options << " --cert #{@cert}"
|
||
|
proxy_options << " --key #{@key}" if @key && @key.size > 0
|
||
|
proxy_options << " --ssl-only" if @wss == "only"
|
||
|
end
|
||
|
|
||
|
if @proxy_ipv6
|
||
|
proxy_options << " -6"
|
||
|
end
|
||
|
|
||
|
cmd ="python #{@proxy_path} #{proxy_options} #{@proxy_port}"
|
||
|
|
||
|
begin
|
||
|
@logger.info { "Starting VNC proxy: #{cmd}" }
|
||
|
pid=start_daemon(cmd, VNC_LOG)
|
||
|
rescue Exception => e
|
||
|
@logger.error e.message
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
File.open(@lock_file, "w") do |f|
|
||
|
f.write(pid.to_s)
|
||
|
end
|
||
|
|
||
|
sleep 1
|
||
|
|
||
|
if !is_running?
|
||
|
message="Error starting VNC proxy"
|
||
|
STDERR.puts message
|
||
|
@logger.error message
|
||
|
File.delete(@lock_file) if File.exist?(@lock_file)
|
||
|
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
STDOUT.puts "VNC proxy started"
|
||
|
|
||
|
true
|
||
|
end
|
||
|
|
||
|
def proxy(vm_resource)
|
||
|
# Check configurations and VM attributes
|
||
|
#if !is_running?
|
||
|
# return error(400, "VNC Proxy is not running")
|
||
|
#end
|
||
|
|
||
|
if !VNC_STATES.include?(vm_resource['LCM_STATE'])
|
||
|
return error(400,"Wrong state (#{vm_resource['LCM_STATE']}) to open a VNC session")
|
||
|
end
|
||
|
|
||
|
if vm_resource['TEMPLATE/GRAPHICS/TYPE'].nil? ||
|
||
|
vm_resource['TEMPLATE/GRAPHICS/TYPE'].downcase != "vnc"
|
||
|
return error(400,"VM has no VNC configured")
|
||
|
end
|
||
|
|
||
|
# Proxy data
|
||
|
host = vm_resource['HISTORY_RECORDS/HISTORY[last()]/HOSTNAME']
|
||
|
vnc_port = vm_resource['TEMPLATE/GRAPHICS/PORT']
|
||
|
vnc_pw = vm_resource['TEMPLATE/GRAPHICS/PASSWD']
|
||
|
|
||
|
# Generate token random_str: host:port
|
||
|
random_str = rand(36**20).to_s(36) #random string a-z0-9 length 20
|
||
|
token = "#{random_str}: #{host}:#{vnc_port}"
|
||
|
token_file = 'one-'+vm_resource['ID']
|
||
|
|
||
|
# Create token file
|
||
|
|
||
|
begin
|
||
|
f = File.open(File.join(@token_folder, token_file), 'w')
|
||
|
f.write(token)
|
||
|
f.close
|
||
|
rescue Exception => e
|
||
|
# @logger.error e.message
|
||
|
return error(500, "Cannot create VNC proxy token")
|
||
|
end
|
||
|
|
||
|
info = {
|
||
|
:proxy_port => "29876",
|
||
|
:password => vnc_pw,
|
||
|
:token => random_str,
|
||
|
:vm_name => vm_resource['NAME']
|
||
|
}
|
||
|
|
||
|
return [200, info]
|
||
|
end
|
||
|
|
||
|
# Delete proxy token file
|
||
|
def delete_token(filename)
|
||
|
begin
|
||
|
File.delete(File.join(@token_folder, filename))
|
||
|
rescue => e
|
||
|
@logger.error "Error deleting token file for VM #{vm_id}"
|
||
|
@logger.error e.message
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def stop(force=false)
|
||
|
pid=get_pid
|
||
|
|
||
|
if pid
|
||
|
@logger.info "Killing VNC proxy"
|
||
|
|
||
|
signal=(force ? 'KILL' : 'TERM')
|
||
|
Process.kill(signal ,pid)
|
||
|
|
||
|
sleep 1
|
||
|
|
||
|
begin
|
||
|
Process.getpgid(pid)
|
||
|
|
||
|
Process.kill('KILL', pid)
|
||
|
rescue
|
||
|
end
|
||
|
|
||
|
if is_running?
|
||
|
message="VNC server is still running"
|
||
|
STDERR.puts message
|
||
|
logger.error message
|
||
|
return false
|
||
|
end
|
||
|
|
||
|
delete_token_dir
|
||
|
else
|
||
|
message="VNC server is not running"
|
||
|
@logger.info message
|
||
|
STDERR.puts message
|
||
|
end
|
||
|
true
|
||
|
end
|
||
|
|
||
|
def status
|
||
|
if is_running?
|
||
|
STDOUT.puts "VNC is running"
|
||
|
true
|
||
|
else
|
||
|
STDOUT.puts "VNC is NOT running"
|
||
|
false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def error(code, msg)
|
||
|
if @options[:json_errors]
|
||
|
return [code,OpenNebula::Error.new(msg).to_json]
|
||
|
else
|
||
|
return [code,msg]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def create_token_dir
|
||
|
delete_token_dir
|
||
|
begin
|
||
|
Dir.mkdir(@token_folder) if !File.exist?(@token_folder)
|
||
|
rescue Exception => e
|
||
|
@logger.error "Cannot create token folder"
|
||
|
@logger.error e.message
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def delete_token_dir
|
||
|
if File.exist?(@token_folder)
|
||
|
begin
|
||
|
Dir.glob("#{@token_folder}/*").each do |file|
|
||
|
File.delete(file)
|
||
|
end
|
||
|
rescue => e
|
||
|
@logger.error "Error deleting token folder"
|
||
|
@logger.error e.message
|
||
|
end
|
||
|
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_pid, :is_running?
|
||
|
|
||
|
if RUBY_VERSION<'1.9'
|
||
|
def spawn(*args)
|
||
|
fork {
|
||
|
command=args[0..-2]
|
||
|
|
||
|
# Close stdin and point out and err to log file
|
||
|
$stdin.close
|
||
|
$stdout.reopen(VNC_LOG, "a")
|
||
|
$stderr.reopen(VNC_LOG, "a")
|
||
|
|
||
|
# Detach process from the parent
|
||
|
Process.setsid
|
||
|
|
||
|
exec(*command)
|
||
|
}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def start_daemon(cmd, log)
|
||
|
options={
|
||
|
:pgroup => true,
|
||
|
:in => :close,
|
||
|
[:out, :err] => [log, "a"],
|
||
|
:close_others => true }
|
||
|
|
||
|
params=cmd.split(" ")+[options]
|
||
|
pid=spawn( *params )
|
||
|
|
||
|
Process.detach(pid)
|
||
|
|
||
|
pid
|
||
|
end
|
||
|
end
|
||
|
|