lib/rvc/modules/vim.rb in rvc-1.3.6 vs lib/rvc/modules/vim.rb in rvc-1.4.0
- old
+ new
@@ -35,11 +35,10 @@
}x
opts :connect do
summary 'Open a connection to ESX/VC'
arg :uri, "Host to connect to"
- opt :insecure, "don't verify ssl certificate", :short => 'k', :default => (ENV['RBVMOMI_INSECURE'] == '1')
opt :rev, "Override protocol revision", :type => :string
end
rvc_alias :connect
@@ -49,52 +48,38 @@
username = match[1] || ENV['RBVMOMI_USER']
password = match[2] || ENV['RBVMOMI_PASSWORD']
host = match[3]
path = match[4]
- insecure = opts[:insecure]
+ bad_cert = false
vim = nil
loop do
begin
vim = RbVmomi::VIM.new :host => host,
- :port => 443,
- :path => '/sdk',
- :ns => 'urn:vim25',
- :rev => (opts[:rev]||'4.0'),
- :ssl => true,
- :insecure => insecure
+ :port => 443,
+ :path => '/sdk',
+ :ns => 'urn:vim25',
+ :rev => (opts[:rev]||'4.0'),
+ :ssl => true,
+ :insecure => bad_cert
break
rescue OpenSSL::SSL::SSLError
- err "Connection failed" unless prompt_cert_insecure
- insecure = true
+ # We'll check known_hosts next
+ raise if bad_cert
+ bad_cert = true
rescue Errno::EHOSTUNREACH, SocketError
err $!.message
end
end
- if opts[:really_insecure]
- result = :ok
- else
+ if bad_cert
+ # Fall back to SSH-style known_hosts
peer_public_key = vim.http.peer_cert.public_key
- known_hosts = RVC::KnownHosts.new
- result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s
+ check_known_hosts(host, peer_public_key)
end
- if result == :not_found
- puts "The authenticity of host '#{host}' can't be established."
- puts "Public key fingerprint is #{arg}."
- err "Connection failed" unless prompt_cert_unknown
- puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
- known_hosts.add 'vim', host, peer_public_key.to_s
- elsif result == :mismatch
- err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
- elsif result == :ok
- else
- err "Unexpected result from known_hosts check"
- end
-
unless opts[:rev]
# negotiate API version
rev = vim.serviceContent.about.apiVersion
vim.rev = [rev, '4.1'].min
end
@@ -105,10 +90,19 @@
if username == nil
username = isVC ? 'Administrator' : 'root'
puts "Using default username #{username.inspect}."
end
+ # If we already have a password, then don't bother querying if we have an OSX
+ # keychain entry for it. If we have either of them, use it.
+ # So will use command line first, then ENV, then keychain on OSX, then prompt.
+ loaded_from_keychain = nil
+ password = keychain_password( username , host ) if password.nil?
+ if not password.nil?
+ loaded_from_keychain = password
+ end
+
password_given = password != nil
loop do
begin
password = prompt_password unless password_given
vim.serviceContent.sessionManager.Login :userName => username,
@@ -124,26 +118,149 @@
sleep 600
vim.serviceInstance.CurrentTime
end
end
+ # if we got to here, save the password, unless we loaded it from keychain
+ save_keychain_password( username , password , host ) unless loaded_from_keychain == password
+
# Stash the address we used to connect so VMRC can use it.
vim.define_singleton_method(:_host) { host }
conn_name = host.dup
conn_name = "#{conn_name}:1" if $shell.connections.member? conn_name
conn_name.succ! while $shell.connections.member? conn_name
$shell.connections[conn_name] = vim
+ $shell.session.set_connection conn_name,
+ 'host' => host,
+ 'username' => username,
+ 'rev' => opts[:rev]
end
def prompt_password
ask("password: ") { |q| q.echo = false }
end
-def prompt_cert_insecure
- agree("SSL certificate verification failed. Connect anyway (y/n)? ", true)
+def keychain_password username , hostname
+ return nil unless RbConfig::CONFIG['host_os'] =~ /^darwin10/
+
+ begin
+ require 'osx_keychain'
+ rescue LoadError
+ return nil
+ end
+
+ keychain = OSXKeychain.new
+ return keychain["rvc", "#{username}@#{hostname}" ]
+
end
-def prompt_cert_unknown
- agree("Are you sure you want to continue connecting (y/n)? ", true)
+def save_keychain_password username , password , hostname
+ # only works for OSX at the minute.
+ return false unless RbConfig::CONFIG['host_os'] =~ /^darwin10/
+
+ # check we already managed to load that gem.
+ if defined? OSXKeychain::VERSION
+
+ if agree("Save password for connection (y/n)? ", true)
+ keychain = OSXKeychain.new
+
+ # update the keychain, unless it's already set to that.
+ keychain.set("rvc", "#{username}@#{hostname}" , password ) unless
+ keychain["rvc", "#{username}@#{hostname}" ] == password
+ end
+ else
+ return false
+ end
+end
+
+
+def check_known_hosts host, peer_public_key
+ known_hosts = RVC::KnownHosts.new
+ result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s
+
+ if result == :not_found
+ puts "The authenticity of host '#{host}' can't be established."
+ puts "Public key fingerprint is #{arg}."
+ err "Connection failed" unless agree("Are you sure you want to continue connecting (y/n)? ", true)
+ puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
+ known_hosts.add 'vim', host, peer_public_key.to_s
+ elsif result == :mismatch
+ err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
+ elsif result == :ok
+ else
+ err "Unexpected result from known_hosts check"
+ end
+end
+
+class RbVmomi::VIM
+ def display_info
+ puts serviceContent.about.fullName
+ end
+
+ def _connection
+ self
+ end
+end
+
+
+opts :tasks do
+ summary "Watch tasks in progress"
+end
+
+def tasks
+ conn = single_connection [$shell.fs.cur]
+
+ begin
+ view = conn.serviceContent.viewManager.CreateListView
+
+ collector = conn.serviceContent.taskManager.CreateCollectorForTasks(:filter => {
+ :time => {
+ :beginTime => conn.serviceInstance.CurrentTime.to_datetime, # XXX
+ :timeType => :queuedTime
+ }
+ })
+ collector.SetCollectorPageSize :maxCount => 1
+
+ filter_spec = {
+ :objectSet => [
+ {
+ :obj => view,
+ :skip => true,
+ :selectSet => [
+ VIM::TraversalSpec(:path => 'view', :type => view.class.wsdl_name)
+ ]
+ },
+ { :obj => collector },
+ ],
+ :propSet => [
+ { :type => 'Task', :pathSet => %w(info.state) },
+ { :type => 'TaskHistoryCollector', :pathSet => %w(latestPage) },
+ ]
+ }
+ filter = conn.propertyCollector.CreateFilter(:partialUpdates => false, :spec => filter_spec)
+
+ ver = ''
+ loop do
+ result = conn.propertyCollector.WaitForUpdates(:version => ver)
+ ver = result.version
+ result.filterSet[0].objectSet.each do |r|
+ remove = []
+ case r.obj
+ when VIM::TaskHistoryCollector
+ infos = collector.ReadNextTasks :maxCount => 100
+ view.ModifyListView :add => infos.map(&:task)
+ when VIM::Task
+ puts "#{Time.now} #{r.obj.info.name} #{r.obj.info.entityName} #{r['info.state']}" unless r['info.state'] == nil
+ remove << r.obj if %w(error success).member? r['info.state']
+ end
+ view.ModifyListView :remove => remove unless remove.empty?
+ end
+ end
+ rescue Interrupt
+ ensure
+ filter.DestroyPropertyFilter if filter
+ collector.DestroyCollector if collector
+ view.DestroyView if view
+ end
end