Skip to content

Instantly share code, notes, and snippets.

@oogali
Created March 1, 2011 17:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oogali/849458 to your computer and use it in GitHub Desktop.
Save oogali/849458 to your computer and use it in GitHub Desktop.
Connect to Manaviz KVM without opening a browser (tested on OS X)
#!/usr/bin/env ruby
#
# I hate ManaWiz KVMs. That is all.
#
# Create a file in your home directory called ".kvmcfg" with the following:
# :kvm_user: yourusername
# :kvm_passwd: yourpassword
#
# Then run this marvelous script:
# $ ./kvm.rb servername-oob.whatever.com
#
# - @oogali
#
#
require 'rubygems'
require 'thread'
require 'net/ssh'
require 'curb'
require 'yaml'
require 'nokogiri'
require 'socket'
include Socket::Constants
class CurbWrapper
attr_reader :base
attr_reader :curb
attr_reader :channel_number
attr_reader :session_id
def initialize(base_url = nil, verbose = false)
@base = base_url
raise "No URL specified" unless @base
@curb = Curl::Easy.new
@curb.enable_cookies = true
@curb.cookies = 'SessionCookie=LOGGED_OUT'
@curb.verbose = verbose
@curb.url = @base
reset false
end
def cookies
@curb.cookies
end
def cookies=(jar)
@curb.cookies = jar
end
def channel_number=(chan)
@channel_number = chan
@curb.cookies += "ChannelNumber=#{chan};"
end
def session_id=(sid)
@session_id = sid
@curb.cookies = "SessionCookie=#{sid};"
end
def url(path = nil)
@curb.url = File.join @base, path if path
@curb.url
end
def get(path)
url path
begin
@curb.perform
rescue Curl::Err::PartialFileError
end
body
end
def post(path, params)
url path
@curb.http_post params.map { |k,v| "#{k}=#{v}" }.join('&')
@curb.perform
body
end
def body
@curb.body_str
end
def headers
@curb.headers
end
def reset(hard_reset = true)
if hard_reset
old_url = @curb.url
@curb.reset
@curb.url = old_url
end
headers['User-Agent'] = 'Mozilla/5.0'
@curb.cookies = "SessionCookie=#{@session_id}; " if @session_id
@curb.cookies += "ChannelNumber=#{@channel_number}; " if @channel_number
@curb.version = Curl::HTTP_1_1
@curb.use_ssl = Curl::CURL_USESSL_ALL
@curb.ssl_verify_host = false
@curb.ssl_verify_peer = false
end
end
# install sigint signal handler
interrupted = false
trap('INT') { interrupted = true }
# print usage if we get incorrect amount of args
if ARGV.length <= 0
$stderr.puts "#{$0} <kvm ip>"
exit
end
# grab kvm host from argv stack
kvm_host = ARGV.shift
# set our default options
config = Hash.new
config[:gw_user] = ENV['USER']
config[:gw_host] = nil
config[:kvm_user] = 'admin'
config[:kvm_passwd] = nil
config[:forward_port] = 8443
config[:jv_port] = 7578
# merge config file with our options
config.update YAML.load_file(ENV['HOME'] + '/.kvmcfg')
Thread.abort_on_exception = true
started = 0
new_forwards = Array.new
# set destination web host and port
web_host = config[:gw_host] ? 'localhost' : kvm_host
web_port = config[:gw_host] ? config[:forward_port] : 443
# if we're given a gateway host, then we should connect to it first
if config[:gw_host]
# spin up new ssh thread, and forward port
ssh_thread = Thread.new do
Thread.current[:connected] = false
Net::SSH.start(config[:gw_host], config[:gw_user]) do |session|
session.forward.local(web_port, kvm_host, 443)
session.forward.local(config[:jv_port], kvm_host, 7578)
Thread.current[:connected] = true
session.loop { true }
end
end
# check every 125ms if we're connected
until ssh_thread[:connected] do
sleep 0.125
end
end
# wait 500ms for thread to settle. why? i dunno, but it works for now.
sleep 0.5
# initialize our wrapper and ping manaviz to make sure we're alive
c = CurbWrapper.new "https://#{web_host}:#{web_port}", true
c.get '/login.asp'
c.get '/page/login.html'
# validate session
c.get '/rpc/WEBSES/validate.asp'
c.reset
# create an authenticated session
session = c.post '/rpc/WEBSES/create.asp', { :WEBVAR_USERNAME => config[:kvm_user], :WEBVAR_PASSWORD => config[:kvm_passwd] }
c.session_id = session.match(/'SESSION_COOKIE'\s+:\s+'(\S+)'\s*,/)[1]
c.channel_number = session.match(/'CHANNEL_NUMBER'\s+:\s+(\d+)/)[1]
c.reset
# download jviewer jar if we don't have it on disk
#
# why am i caching?
# * because this stupid kvm goes unresponsive from time to time on a large download like say, the jar file you need for kvm
# ** leading to you being locked out of your kvm for minutes, hours, or days; or until you yank power from the server
# ** do you know how frustrating it is to not be able to access your server's kvm when you need to?
# **** yes, i am really fucking angry.
# ***** yes, i really do mean YANK power from the server. as long as the server has power, manawiz remains hung and useless.
# ****** no, i will not do 1-3 RMAs/week of motherboards in my datacenter footprint.
# ******* have you ever RMA'd a motherboard? i'd much rather buy dedicated kvm hardware. and stop buying motherboards featuring 'onboard kvm' from flextronics/supermicro/etc.
if not File.exists? '/tmp/JViewer.jar'
jar = File.open '/tmp/JViewer.jar', 'w+'
jar.write c.get '/Java/release/JViewer.jar'
jar.close
end
# send http request for jnlp
jnlp = c.get '/Java/jviewer.jnlp'
p jnlp
# parse jnlp for jviewer args
doc = Nokogiri::XML jnlp
args = doc.xpath('//jnlp/application-desc/argument')
# assign args to var, use curl ip for host to deal with forwarding magic
jviewer_host = c.curb.primary_ip
jviewer_remote_port = args[1].text.to_i
jviewer_auth = args[2].text
# if we're forwarding connections, then replace hosts in jnlp file
if kvm_host != jviewer_host
jnlp.sub! "https://#{kvm_host}", "https://#{web_host}:#{web_port}"
jnlp.sub! kvm_host, jviewer_host
end
# write jnlp to temp file
j = File.new("/tmp/kvm.#{$$}.jnlp", "w+")
j.write(jnlp)
j.close
# sleep 500ms and then launch jnlp
sleep 0.5
system('java', "-Xdock:name=Manawiz KVM", '-jar', '/tmp/JViewer.jar', jviewer_host, jviewer_remote_port.to_s, jviewer_auth + "\002")
# hold session open for 30 minutes, or until we get a ctrl+c
ts = Time.now.to_i
while Time.now.to_i < ts + 1800
break if interrupted
sleep 0.125
end
p c.get '/rpc/WEBSES/logout.asp'
# kill ssh thread
ssh_thread.kill if ssh_thread
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment