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.
- data/LICENSE +19 -0
- data/README.rdoc +120 -0
- data/Rakefile +39 -0
- data/TODO +4 -0
- data/VERSION +1 -0
- data/bin/rvc +104 -0
- data/lib/rvc.rb +31 -0
- data/lib/rvc/completion.rb +110 -0
- data/lib/rvc/extensions/ClusterComputeResource.rb +42 -0
- data/lib/rvc/extensions/ComputeResource.rb +47 -0
- data/lib/rvc/extensions/Datacenter.rb +36 -0
- data/lib/rvc/extensions/Datastore.rb +188 -0
- data/lib/rvc/extensions/DistributedVirtualPortgroup.rb +38 -0
- data/lib/rvc/extensions/DistributedVirtualSwitch.rb +40 -0
- data/lib/rvc/extensions/Folder.rb +33 -0
- data/lib/rvc/extensions/HostSystem.rb +48 -0
- data/lib/rvc/extensions/ManagedEntity.rb +28 -0
- data/lib/rvc/extensions/Network.rb +28 -0
- data/lib/rvc/extensions/ResourcePool.rb +52 -0
- data/lib/rvc/extensions/VirtualMachine.rb +72 -0
- data/lib/rvc/fs.rb +123 -0
- data/lib/rvc/inventory.rb +125 -0
- data/lib/rvc/modules.rb +74 -0
- data/lib/rvc/modules/basic.rb +276 -0
- data/lib/rvc/modules/datastore.rb +63 -0
- data/lib/rvc/modules/host.rb +29 -0
- data/lib/rvc/modules/resource_pool.rb +95 -0
- data/lib/rvc/modules/vim.rb +128 -0
- data/lib/rvc/modules/vm.rb +607 -0
- data/lib/rvc/modules/vmrc.rb +72 -0
- data/lib/rvc/modules/vnc.rb +111 -0
- data/lib/rvc/option_parser.rb +114 -0
- data/lib/rvc/path.rb +37 -0
- data/lib/rvc/readline-ffi.rb +41 -0
- data/lib/rvc/shell.rb +220 -0
- data/lib/rvc/ttl_cache.rb +44 -0
- data/lib/rvc/util.rb +168 -0
- data/test/_test_completion.rb +27 -0
- data/test/_test_option_parser.rb +6 -0
- data/test/inventory_fixtures.rb +15 -0
- data/test/test_fs.rb +113 -0
- data/test/test_parse_path.rb +41 -0
- 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
|
data/lib/rvc/path.rb
ADDED
@@ -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
|
data/lib/rvc/shell.rb
ADDED
@@ -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
|