rvc 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +120 -0
  3. data/Rakefile +39 -0
  4. data/TODO +4 -0
  5. data/VERSION +1 -0
  6. data/bin/rvc +104 -0
  7. data/lib/rvc.rb +31 -0
  8. data/lib/rvc/completion.rb +110 -0
  9. data/lib/rvc/extensions/ClusterComputeResource.rb +42 -0
  10. data/lib/rvc/extensions/ComputeResource.rb +47 -0
  11. data/lib/rvc/extensions/Datacenter.rb +36 -0
  12. data/lib/rvc/extensions/Datastore.rb +188 -0
  13. data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +38 -0
  14. data/lib/rvc/extensions/DistributedVirtualSwitch.rb +40 -0
  15. data/lib/rvc/extensions/Folder.rb +33 -0
  16. data/lib/rvc/extensions/HostSystem.rb +48 -0
  17. data/lib/rvc/extensions/ManagedEntity.rb +28 -0
  18. data/lib/rvc/extensions/Network.rb +28 -0
  19. data/lib/rvc/extensions/ResourcePool.rb +52 -0
  20. data/lib/rvc/extensions/VirtualMachine.rb +72 -0
  21. data/lib/rvc/fs.rb +123 -0
  22. data/lib/rvc/inventory.rb +125 -0
  23. data/lib/rvc/modules.rb +74 -0
  24. data/lib/rvc/modules/basic.rb +276 -0
  25. data/lib/rvc/modules/datastore.rb +63 -0
  26. data/lib/rvc/modules/host.rb +29 -0
  27. data/lib/rvc/modules/resource_pool.rb +95 -0
  28. data/lib/rvc/modules/vim.rb +128 -0
  29. data/lib/rvc/modules/vm.rb +607 -0
  30. data/lib/rvc/modules/vmrc.rb +72 -0
  31. data/lib/rvc/modules/vnc.rb +111 -0
  32. data/lib/rvc/option_parser.rb +114 -0
  33. data/lib/rvc/path.rb +37 -0
  34. data/lib/rvc/readline-ffi.rb +41 -0
  35. data/lib/rvc/shell.rb +220 -0
  36. data/lib/rvc/ttl_cache.rb +44 -0
  37. data/lib/rvc/util.rb +168 -0
  38. data/test/_test_completion.rb +27 -0
  39. data/test/_test_option_parser.rb +6 -0
  40. data/test/inventory_fixtures.rb +15 -0
  41. data/test/test_fs.rb +113 -0
  42. data/test/test_parse_path.rb +41 -0
  43. metadata +146 -0
@@ -0,0 +1,72 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'tmpdir'
22
+
23
+ VMRC_NAME = "vmware-vmrc-linux-x86-3.0.0"
24
+ VMRC_PKGVER = 1
25
+ VMRC_URL = "https://github.com/downloads/vmware/rvc/#{VMRC_NAME}.#{VMRC_PKGVER}.tar.bz2"
26
+
27
+ def find_local_vmrc
28
+ path = File.join(Dir.tmpdir, VMRC_NAME, 'plugins', 'vmware-vmrc')
29
+ File.exists?(path) && path
30
+ end
31
+
32
+ def find_vmrc
33
+ find_local_vmrc || search_path('vmrc')
34
+ end
35
+
36
+
37
+ opts :view do
38
+ summary "Spawn a VMRC"
39
+ arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
40
+ end
41
+
42
+ rvc_alias :view
43
+ rvc_alias :view, :vmrc
44
+ rvc_alias :view, :v
45
+
46
+ def view vms
47
+ err "VMRC not found" unless vmrc = find_vmrc
48
+ vms.each do |vm|
49
+ moref = vm._ref
50
+ ticket = vm._connection.serviceInstance.content.sessionManager.AcquireCloneTicket
51
+ host = vm._connection._host
52
+ fork do
53
+ ENV['https_proxy'] = ENV['HTTPS_PROXY'] = ''
54
+ $stderr.reopen("#{ENV['HOME']||'.'}/.rvc-vmrc.log", "w")
55
+ $stderr.puts Time.now
56
+ Process.setpgrp
57
+ exec vmrc, '-M', moref,
58
+ '-h', host,
59
+ '-p', ticket
60
+ end
61
+ end
62
+ end
63
+
64
+ opts :install do
65
+ summary "Install VMRC"
66
+ end
67
+
68
+ def install
69
+ puts "Installing VMRC..."
70
+ system "curl -L #{VMRC_URL} | tar -xj -C #{Dir.tmpdir}" or err("VMRC installation failed")
71
+ puts "VMRC was installed successfully."
72
+ end
@@ -0,0 +1,111 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ VNC = ENV['VNC'] || search_path('vinagre') || search_path('tightvnc')
22
+
23
+ opts :view do
24
+ summary "Spawn a VNC client"
25
+ arg :vm, nil, :lookup => VIM::VirtualMachine
26
+ end
27
+
28
+ rvc_alias :view, :vnc
29
+ rvc_alias :view, :V
30
+
31
+ def view vm
32
+ ip = reachable_ip vm.runtime.host
33
+ extraConfig = vm.config.extraConfig
34
+ already_enabled = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.enabled' && x.value.downcase == 'true' }
35
+ if already_enabled
36
+ puts "VNC already enabled"
37
+ port = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.port' }.value
38
+ password = extraConfig.find { |x| x.key == 'RemoteDisplay.vnc.password' }.value
39
+ else
40
+ port = unused_vnc_port ip
41
+ password = vnc_password
42
+ vm.ReconfigVM_Task(:spec => {
43
+ :extraConfig => [
44
+ { :key => 'RemoteDisplay.vnc.enabled', :value => 'true' },
45
+ { :key => 'RemoteDisplay.vnc.password', :value => password },
46
+ { :key => 'RemoteDisplay.vnc.port', :value => port.to_s }
47
+ ]
48
+ }).wait_for_completion
49
+ end
50
+ vnc_client ip, port, password
51
+ end
52
+
53
+
54
+ opts :off do
55
+ summary "Close a VM's VNC port"
56
+ arg :vm, nil, :lookup => VIM::VirtualMachine
57
+ end
58
+
59
+ def off vm
60
+ vm.ReconfigVM_Task(:spec => {
61
+ :extraConfig => [
62
+ { :key => 'RemoteDisplay.vnc.enabled', :value => 'false' },
63
+ { :key => 'RemoteDisplay.vnc.password', :value => '' },
64
+ { :key => 'RemoteDisplay.vnc.port', :value => '' }
65
+ ]
66
+ }).wait_for_completion
67
+ end
68
+
69
+
70
+ def reachable_ip host
71
+ ips = host.config.network.vnic.map { |x| x.spec.ip.ipAddress } # TODO optimize
72
+ ips.find do |x|
73
+ begin
74
+ Timeout.timeout(1) { TCPSocket.new(x, 443).close; true }
75
+ rescue
76
+ false
77
+ end
78
+ end or err("could not find IP for server #{host.name}")
79
+ end
80
+
81
+ def unused_vnc_port ip
82
+ 10.times do
83
+ port = 5901 + rand(64)
84
+ unused = (TCPSocket.connect(ip, port).close rescue true)
85
+ return port if unused
86
+ end
87
+ err "no unused port found"
88
+ end
89
+
90
+ # Override this if you don't want a random password
91
+ def vnc_password
92
+ n = 8
93
+ chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
94
+ (0...n).map { chars[rand(chars.length)].chr }.join
95
+ end
96
+
97
+ # Override this to spawn a VNC client differently
98
+ def vnc_client ip, port, password
99
+ if VNC
100
+ fork do
101
+ $stderr.reopen("#{ENV['HOME']||'.'}/.rvc-vmrc.log", "w")
102
+ Process.setpgrp
103
+ exec VNC, "#{ip}:#{port}"
104
+ end
105
+ puts "spawning #{VNC}"
106
+ puts "#{ip}:#{port} password: #{password}"
107
+ else
108
+ puts "no VNC client configured"
109
+ puts "#{ip}:#{port} password: #{password}"
110
+ end
111
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'trollop'
22
+
23
+ module RVC
24
+
25
+ class OptionParser < Trollop::Parser
26
+ attr_reader :applicable
27
+
28
+ def initialize cmd, &b
29
+ @cmd = cmd
30
+ @summary = nil
31
+ @args = []
32
+ @has_options = false
33
+ @seen_not_required = false
34
+ @seen_multi = false
35
+ @applicable = Set.new
36
+ super &b
37
+ end
38
+
39
+ def summary str
40
+ @summary = str
41
+ text str
42
+ end
43
+
44
+ def summary?
45
+ @summary
46
+ end
47
+
48
+ def opt name, *a
49
+ super
50
+ @applicable << @specs[name][:lookup] if @specs[name][:lookup]
51
+ @has_options = true unless name == :help
52
+ end
53
+
54
+ def has_options?
55
+ @has_options
56
+ end
57
+
58
+ def arg name, description, opts={}
59
+ opts = {
60
+ :required => true,
61
+ :default => nil,
62
+ :multi => false,
63
+ }.merge opts
64
+ opts[:default] = [] if opts[:multi] and opts[:default].nil?
65
+ fail "Multi argument must be the last one" if @seen_multi
66
+ fail "Can't have required argument after optional ones" if opts[:required] and @seen_not_required
67
+ @applicable << opts[:lookup] if opts[:lookup]
68
+ @args << [name, description, opts[:required], opts[:default], opts[:multi], opts[:lookup]]
69
+ text " #{name}: " + [description, opts[:lookup]].compact.join(' ')
70
+ end
71
+
72
+ def parse argv
73
+ opts = super argv
74
+
75
+ @specs.each do |name,spec|
76
+ next unless klass = spec[:lookup] and path = opts[name]
77
+ opts[name] = lookup! path, klass
78
+ end
79
+
80
+ argv = leftovers
81
+ args = []
82
+ @args.each do |name,desc,required,default,multi,lookup_klass|
83
+ if multi
84
+ err "missing argument '#{name}'" if required and argv.empty?
85
+ a = (argv.empty? ? default : argv.dup)
86
+ a.map! { |x| lookup! x, lookup_klass } if lookup_klass
87
+ args << a
88
+ argv.clear
89
+ else
90
+ x = argv.shift
91
+ err "missing argument '#{name}'" if required and x.nil?
92
+ x = default if x.nil?
93
+ x = lookup! x, lookup_klass if lookup_klass
94
+ args << x
95
+ end
96
+ end
97
+ err "too many arguments" unless argv.empty?
98
+ return args, opts
99
+ end
100
+
101
+ def educate
102
+ arg_texts = @args.map do |name,desc,required,default,multi,lookup_klass|
103
+ text = name
104
+ text = "[#{text}]" if not required
105
+ text = "#{text}..." if multi
106
+ text
107
+ end
108
+ arg_texts.unshift "[opts]" if has_options?
109
+ puts "usage: #{@cmd} #{arg_texts*' '}"
110
+ super
111
+ end
112
+ end
113
+
114
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module RVC
22
+ module Path
23
+ def self.parse path
24
+ if path.empty?
25
+ return [[], false, false]
26
+ elsif path == '/'
27
+ return [[], true, true]
28
+ else
29
+ els = path.split '/'
30
+ trailing_slash = path[-1..-1] == '/'
31
+ absolute = !els[0].nil? && els[0].empty?
32
+ els.shift if absolute
33
+ [els, absolute, trailing_slash]
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'ffi'
22
+
23
+ module RVC::ReadlineFFI
24
+ extend FFI::Library
25
+ ffi_lib "readline.so"
26
+ callback :rl_linebuf_func_t, [ :string, :int ], :bool
27
+ attach_variable :rl_char_is_quoted_p, :rl_char_is_quoted_p, :rl_linebuf_func_t
28
+ attach_variable :rl_line_buffer, :rl_line_buffer, :string
29
+ end
30
+
31
+ unless Readline.respond_to? :line_buffer
32
+ def Readline.line_buffer
33
+ RVC::ReadlineFFI.rl_line_buffer
34
+ end
35
+ end
36
+
37
+ unless Readline.respond_to? :char_is_quoted=
38
+ def Readline.char_is_quoted= fn
39
+ RVC::ReadlineFFI.rl_char_is_quoted_p = fn
40
+ end
41
+ end
@@ -0,0 +1,220 @@
1
+ # Copyright (c) 2011 VMware, Inc. All Rights Reserved.
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ module RVC
22
+
23
+ class Shell
24
+ attr_reader :fs, :connections
25
+ attr_accessor :debug
26
+
27
+ def initialize
28
+ @persist_ruby = false
29
+ @fs = RVC::FS.new RVC::RootNode.new
30
+ @ruby_evaluator = RubyEvaluator.new @fs
31
+ @connections = {}
32
+ @debug = false
33
+ end
34
+
35
+ def eval_input input
36
+ if input == '//'
37
+ @persist_ruby = !@persist_ruby
38
+ return
39
+ end
40
+
41
+ if input[0..0] == '!'
42
+ system_fg input[1..-1]
43
+ return
44
+ end
45
+
46
+ ruby = @persist_ruby
47
+ if input =~ /^\//
48
+ input = $'
49
+ ruby = !ruby
50
+ end
51
+
52
+ begin
53
+ if ruby
54
+ eval_ruby input
55
+ else
56
+ eval_command input
57
+ end
58
+ rescue SystemExit, IOError
59
+ raise
60
+ rescue UserError, RuntimeError, RbVmomi::Fault
61
+ if ruby or debug
62
+ puts "#{$!.class}: #{$!.message}"
63
+ puts $!.backtrace * "\n"
64
+ else
65
+ case $!
66
+ when RbVmomi::Fault, UserError
67
+ puts $!.message
68
+ else
69
+ puts "#{$!.class}: #{$!.message}"
70
+ end
71
+ end
72
+ rescue Exception
73
+ puts "#{$!.class}: #{$!.message}"
74
+ puts $!.backtrace * "\n"
75
+ end
76
+ end
77
+
78
+ def eval_command input
79
+ cmd, *args = Shellwords.shellwords(input)
80
+ return unless cmd
81
+ err "invalid command" unless cmd.is_a? String
82
+ case cmd
83
+ when RVC::FS::MARK_REGEX
84
+ CMD.basic.cd cmd
85
+ else
86
+ if cmd.include? '.'
87
+ module_name, cmd, = cmd.split '.'
88
+ elsif ALIASES.member? cmd
89
+ module_name, cmd, = ALIASES[cmd].split '.'
90
+ else
91
+ err "unknown alias #{cmd}"
92
+ end
93
+
94
+ m = MODULES[module_name] or err("unknown module #{module_name}")
95
+
96
+ opts_block = m.opts_for(cmd.to_sym)
97
+ parser = RVC::OptionParser.new cmd, &opts_block
98
+
99
+ begin
100
+ args, opts = parser.parse args
101
+ rescue Trollop::HelpNeeded
102
+ parser.educate
103
+ return
104
+ end
105
+
106
+ if parser.has_options?
107
+ m.send cmd.to_sym, *(args + [opts])
108
+ else
109
+ m.send cmd.to_sym, *args
110
+ end
111
+ end
112
+ nil
113
+ end
114
+
115
+ def eval_ruby input
116
+ result = @ruby_evaluator.do_eval input
117
+ if input =~ /\#$/
118
+ introspect_object result
119
+ else
120
+ pp result
121
+ end
122
+ nil
123
+ end
124
+
125
+ def prompt
126
+ "#{@fs.display_path}#{@persist_ruby ? '~' : '>'} "
127
+ end
128
+
129
+ def introspect_object obj
130
+ case obj
131
+ when RbVmomi::VIM::DataObject, RbVmomi::VIM::ManagedObject
132
+ introspect_class obj.class
133
+ when Array
134
+ klasses = obj.map(&:class).uniq
135
+ if klasses.size == 0
136
+ puts "Array"
137
+ elsif klasses.size == 1
138
+ $stdout.write "Array of "
139
+ introspect_class klasses[0]
140
+ else
141
+ counts = Hash.new 0
142
+ obj.each { |o| counts[o.class] += 1 }
143
+ puts "Array of:"
144
+ counts.each { |k,c| puts " #{k}: #{c}" }
145
+ puts
146
+ $stdout.write "Common ancestor: "
147
+ introspect_class klasses.map(&:ancestors).inject(&:&)[0]
148
+ end
149
+ else
150
+ puts obj.class
151
+ end
152
+ end
153
+
154
+ def introspect_class klass
155
+ q = lambda { |x| x =~ /^xsd:/ ? $' : x }
156
+ if klass < RbVmomi::VIM::DataObject
157
+ puts "Data Object #{klass}"
158
+ klass.full_props_desc.each do |desc|
159
+ puts " #{desc['name']}: #{q[desc['wsdl_type']]}#{desc['is-array'] ? '[]' : ''}"
160
+ end
161
+ elsif klass < RbVmomi::VIM::ManagedObject
162
+ puts "Managed Object #{klass}"
163
+ puts
164
+ puts "Properties:"
165
+ klass.full_props_desc.each do |desc|
166
+ puts " #{desc['name']}: #{q[desc['wsdl_type']]}#{desc['is-array'] ? '[]' : ''}"
167
+ end
168
+ puts
169
+ puts "Methods:"
170
+ klass.full_methods_desc.sort_by(&:first).each do |name,desc|
171
+ params = desc['params']
172
+ puts " #{name}(#{params.map { |x| "#{x['name']} : #{q[x['wsdl_type'] || 'void']}#{x['is-array'] ? '[]' : ''}" } * ', '}) : #{q[desc['result']['wsdl_type'] || 'void']}"
173
+ end
174
+ else
175
+ puts klass
176
+ end
177
+ end
178
+ end
179
+
180
+ class RubyEvaluator
181
+ def initialize fs
182
+ @binding = binding
183
+ @fs = fs
184
+ end
185
+
186
+ def do_eval input
187
+ eval input, @binding
188
+ end
189
+
190
+ def this
191
+ @fs.cur
192
+ end
193
+
194
+ def dc
195
+ @fs.lookup("~")
196
+ end
197
+
198
+ def conn
199
+ @fs.lookup("~@")
200
+ end
201
+
202
+ def method_missing sym, *a
203
+ str = sym.to_s
204
+ if a.empty?
205
+ if MODULES.member? str
206
+ MODULES[str]
207
+ elsif @fs.marks.member?(str)
208
+ @fs.marks[str].obj
209
+ elsif str[0..0] == '_' && @fs.marks.member?(str[1..-1])
210
+ @fs.marks[str[1..-1]].obj
211
+ else
212
+ super
213
+ end
214
+ else
215
+ super
216
+ end
217
+ end
218
+ end
219
+
220
+ end