rvc 1.3.6 → 1.4.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/README.rdoc +8 -0
- data/Rakefile +12 -0
- data/TODO +5 -1
- data/VERSION +1 -1
- data/bin/rvc +34 -14
- data/lib/rvc/completion.rb +1 -1
- data/lib/rvc/extensions/VirtualMachine.rb +26 -1
- data/lib/rvc/filesystem_session.rb +81 -0
- data/lib/rvc/fs.rb +4 -10
- data/lib/rvc/memory_session.rb +39 -0
- data/lib/rvc/modules/basic.rb +35 -6
- data/lib/rvc/modules/datastore.rb +90 -14
- data/lib/rvc/modules/host.rb +21 -0
- data/lib/rvc/modules/mark.rb +11 -2
- data/lib/rvc/modules/permissions.rb +62 -0
- data/lib/rvc/modules/role.rb +116 -0
- data/lib/rvc/modules/vim.rb +149 -32
- data/lib/rvc/modules/vm.rb +12 -6
- data/lib/rvc/modules/vmrc.rb +16 -2
- data/lib/rvc/modules.rb +4 -0
- data/lib/rvc/option_parser.rb +13 -11
- data/lib/rvc/shell.rb +20 -11
- data/lib/rvc/util.rb +3 -0
- data/lib/rvc.rb +2 -0
- metadata +6 -2
@@ -0,0 +1,62 @@
|
|
1
|
+
opts :get do
|
2
|
+
summary "Display the permissions of a managed entity"
|
3
|
+
arg :obj, nil, :lookup => VIM::ManagedEntity, :multi => true
|
4
|
+
end
|
5
|
+
|
6
|
+
def get objs
|
7
|
+
conn = single_connection objs
|
8
|
+
authMgr = conn.serviceContent.authorizationManager
|
9
|
+
roles = Hash[authMgr.roleList.map { |x| [x.roleId, x] }]
|
10
|
+
objs.each do |obj|
|
11
|
+
puts "#{obj.name}:"
|
12
|
+
perms = authMgr.RetrieveEntityPermissions(:entity => obj, :inherited => true)
|
13
|
+
perms.each do |perm|
|
14
|
+
flags = []
|
15
|
+
flags << 'group' if perm[:group]
|
16
|
+
flags << 'propagate' if perm[:propagate]
|
17
|
+
puts " #{perm[:principal]}#{flags.empty? ? '' : " (#{flags * ', '})"}: #{roles[perm[:roleId]].name}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
opts :set do
|
24
|
+
summary "Set the permissions on a managed entity"
|
25
|
+
arg :obj, nil, :lookup => VIM::ManagedEntity, :multi => true
|
26
|
+
opt :role, "Role", :type => :string, :required => true
|
27
|
+
opt :principal, "Principal", :type => :string, :required => true
|
28
|
+
opt :group, "Does the principal refer to a group?"
|
29
|
+
opt :propagate, "Propagate?"
|
30
|
+
end
|
31
|
+
|
32
|
+
def set objs, opts
|
33
|
+
conn = single_connection objs
|
34
|
+
authMgr = conn.serviceContent.authorizationManager
|
35
|
+
role = authMgr.roleList.find { |x| x.name == opts[:role] }
|
36
|
+
err "no such role #{role.inspect}" unless role
|
37
|
+
perm = { :roleId => role.roleId,
|
38
|
+
:principal => opts[:principal],
|
39
|
+
:group => opts[:group],
|
40
|
+
:propagate => opts[:propagate] }
|
41
|
+
objs.each do |obj|
|
42
|
+
authMgr.SetEntityPermissions(:entity => obj, :permission => [perm])
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
opts :remove do
|
48
|
+
summary "Remove permissions for the given user from a managed entity"
|
49
|
+
arg :obj, nil, :lookup => VIM::ManagedEntity, :multi => true
|
50
|
+
opt :principal, "Principal", :type => :string, :required => true
|
51
|
+
opt :group, "Does the principal refer to a group?"
|
52
|
+
end
|
53
|
+
|
54
|
+
def remove objs, opts
|
55
|
+
conn = single_connection objs
|
56
|
+
authMgr = conn.serviceContent.authorizationManager
|
57
|
+
objs.each do |obj|
|
58
|
+
authMgr.RemoveEntityPermission :entity => obj,
|
59
|
+
:user => opts[:principal],
|
60
|
+
:isGroup => opts[:group]
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
def cur_auth_mgr
|
2
|
+
conn = $shell.fs.cur._connection
|
3
|
+
conn.serviceContent.authorizationManager
|
4
|
+
end
|
5
|
+
|
6
|
+
opts :list do
|
7
|
+
summary "List roles in the system"
|
8
|
+
end
|
9
|
+
|
10
|
+
def list
|
11
|
+
cur_auth_mgr.roleList.each do |role|
|
12
|
+
puts "#{role.name}: #{role.info.summary}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
opts :get do
|
18
|
+
summary "Display information about a role"
|
19
|
+
arg :role, "Role", :type => :string
|
20
|
+
end
|
21
|
+
|
22
|
+
def get name
|
23
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == name }
|
24
|
+
err "no such role #{role_name.inspect}" unless role
|
25
|
+
puts "label: #{role.info.label}"
|
26
|
+
puts "summary: #{role.info.summary}"
|
27
|
+
puts "privileges: #{role.privilege.sort * ' '}"
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
opts :permissions do
|
32
|
+
summary "List permissions given to this role"
|
33
|
+
arg :role, "Role", :type => :string
|
34
|
+
end
|
35
|
+
|
36
|
+
def permissions name
|
37
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == name }
|
38
|
+
err "no such role #{role_name.inspect}" unless role
|
39
|
+
cur_auth_mgr.RetrieveRolePermissions(roleId: role.roleId).each do |perm|
|
40
|
+
flags = []
|
41
|
+
flags << 'group' if perm[:group]
|
42
|
+
flags << 'propagate' if perm[:propagate]
|
43
|
+
puts " #{perm[:principal]}#{flags.empty? ? '' : " (#{flags * ', '})"}: #{perm.entity.name}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
opts :create do
|
49
|
+
summary "Create a new role"
|
50
|
+
arg :name, "Name of the role", :type => :string
|
51
|
+
arg :privilege, "Privileges to assign", :type => :string, :multi => true, :required => false
|
52
|
+
end
|
53
|
+
|
54
|
+
def create name, privileges
|
55
|
+
cur_auth_mgr.AddAuthorizationRole :name => name, :privIds => privileges
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
opts :delete do
|
60
|
+
summary "Delete a role"
|
61
|
+
arg :name, "Name of the role", :type => :string
|
62
|
+
opt :force, "Don't fail if the role is in use"
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete name, opts
|
66
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == name }
|
67
|
+
err "no such role #{role_name.inspect}" unless role
|
68
|
+
cur_auth_mgr.RemoveAuthorizationRole :roleId => role.roleId, :failIfUsed => opts[:force]
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
opts :rename do
|
73
|
+
summary "Rename a role"
|
74
|
+
arg :old, "Old name", :type => :string
|
75
|
+
arg :new, "New name", :type => :string
|
76
|
+
end
|
77
|
+
|
78
|
+
def rename old, new
|
79
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == old }
|
80
|
+
err "no such role #{old.inspect}" unless role
|
81
|
+
cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
|
82
|
+
:newName => new,
|
83
|
+
:privIds => role.privilege
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
opts :add_privilege do
|
88
|
+
summary "Add privileges to a role"
|
89
|
+
arg :name, "Role name", :type => :string
|
90
|
+
arg :privileges, "Privileges", :type => :string, :multi => true
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_privilege name, privileges
|
94
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == name }
|
95
|
+
err "no such role #{name.inspect}" unless role
|
96
|
+
cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
|
97
|
+
:newName => role.name,
|
98
|
+
:privIds => (role.privilege | privileges)
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
opts :remove_privilege do
|
104
|
+
summary "Remove privileges from a role"
|
105
|
+
arg :name, "Role name", :type => :string
|
106
|
+
arg :privileges, "Privileges", :type => :string, :multi => true
|
107
|
+
end
|
108
|
+
|
109
|
+
def remove_privilege name, privileges
|
110
|
+
role = cur_auth_mgr.roleList.find { |x| x.name == name }
|
111
|
+
err "no such role #{name.inspect}" unless role
|
112
|
+
cur_auth_mgr.UpdateAuthorizationRole :roleId => role.roleId,
|
113
|
+
:newName => role.name,
|
114
|
+
:privIds => (role.privilege - privileges)
|
115
|
+
|
116
|
+
end
|
data/lib/rvc/modules/vim.rb
CHANGED
@@ -37,7 +37,6 @@ URI_REGEX = %r{
|
|
37
37
|
opts :connect do
|
38
38
|
summary 'Open a connection to ESX/VC'
|
39
39
|
arg :uri, "Host to connect to"
|
40
|
-
opt :insecure, "don't verify ssl certificate", :short => 'k', :default => (ENV['RBVMOMI_INSECURE'] == '1')
|
41
40
|
opt :rev, "Override protocol revision", :type => :string
|
42
41
|
end
|
43
42
|
|
@@ -51,46 +50,32 @@ def connect uri, opts
|
|
51
50
|
password = match[2] || ENV['RBVMOMI_PASSWORD']
|
52
51
|
host = match[3]
|
53
52
|
path = match[4]
|
54
|
-
|
53
|
+
bad_cert = false
|
55
54
|
|
56
55
|
vim = nil
|
57
56
|
loop do
|
58
57
|
begin
|
59
58
|
vim = RbVmomi::VIM.new :host => host,
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
59
|
+
:port => 443,
|
60
|
+
:path => '/sdk',
|
61
|
+
:ns => 'urn:vim25',
|
62
|
+
:rev => (opts[:rev]||'4.0'),
|
63
|
+
:ssl => true,
|
64
|
+
:insecure => bad_cert
|
66
65
|
break
|
67
66
|
rescue OpenSSL::SSL::SSLError
|
68
|
-
|
69
|
-
|
67
|
+
# We'll check known_hosts next
|
68
|
+
raise if bad_cert
|
69
|
+
bad_cert = true
|
70
70
|
rescue Errno::EHOSTUNREACH, SocketError
|
71
71
|
err $!.message
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
if
|
76
|
-
|
77
|
-
else
|
75
|
+
if bad_cert
|
76
|
+
# Fall back to SSH-style known_hosts
|
78
77
|
peer_public_key = vim.http.peer_cert.public_key
|
79
|
-
|
80
|
-
result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s
|
81
|
-
end
|
82
|
-
|
83
|
-
if result == :not_found
|
84
|
-
puts "The authenticity of host '#{host}' can't be established."
|
85
|
-
puts "Public key fingerprint is #{arg}."
|
86
|
-
err "Connection failed" unless prompt_cert_unknown
|
87
|
-
puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
|
88
|
-
known_hosts.add 'vim', host, peer_public_key.to_s
|
89
|
-
elsif result == :mismatch
|
90
|
-
err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
|
91
|
-
elsif result == :ok
|
92
|
-
else
|
93
|
-
err "Unexpected result from known_hosts check"
|
78
|
+
check_known_hosts(host, peer_public_key)
|
94
79
|
end
|
95
80
|
|
96
81
|
unless opts[:rev]
|
@@ -107,6 +92,15 @@ def connect uri, opts
|
|
107
92
|
puts "Using default username #{username.inspect}."
|
108
93
|
end
|
109
94
|
|
95
|
+
# If we already have a password, then don't bother querying if we have an OSX
|
96
|
+
# keychain entry for it. If we have either of them, use it.
|
97
|
+
# So will use command line first, then ENV, then keychain on OSX, then prompt.
|
98
|
+
loaded_from_keychain = nil
|
99
|
+
password = keychain_password( username , host ) if password.nil?
|
100
|
+
if not password.nil?
|
101
|
+
loaded_from_keychain = password
|
102
|
+
end
|
103
|
+
|
110
104
|
password_given = password != nil
|
111
105
|
loop do
|
112
106
|
begin
|
@@ -126,6 +120,9 @@ def connect uri, opts
|
|
126
120
|
end
|
127
121
|
end
|
128
122
|
|
123
|
+
# if we got to here, save the password, unless we loaded it from keychain
|
124
|
+
save_keychain_password( username , password , host ) unless loaded_from_keychain == password
|
125
|
+
|
129
126
|
# Stash the address we used to connect so VMRC can use it.
|
130
127
|
vim.define_singleton_method(:_host) { host }
|
131
128
|
|
@@ -134,16 +131,136 @@ def connect uri, opts
|
|
134
131
|
conn_name.succ! while $shell.connections.member? conn_name
|
135
132
|
|
136
133
|
$shell.connections[conn_name] = vim
|
134
|
+
$shell.session.set_connection conn_name,
|
135
|
+
'host' => host,
|
136
|
+
'username' => username,
|
137
|
+
'rev' => opts[:rev]
|
137
138
|
end
|
138
139
|
|
139
140
|
def prompt_password
|
140
141
|
ask("password: ") { |q| q.echo = false }
|
141
142
|
end
|
142
143
|
|
143
|
-
def
|
144
|
-
|
144
|
+
def keychain_password username , hostname
|
145
|
+
return nil unless RbConfig::CONFIG['host_os'] =~ /^darwin10/
|
146
|
+
|
147
|
+
begin
|
148
|
+
require 'osx_keychain'
|
149
|
+
rescue LoadError
|
150
|
+
return nil
|
151
|
+
end
|
152
|
+
|
153
|
+
keychain = OSXKeychain.new
|
154
|
+
return keychain["rvc", "#{username}@#{hostname}" ]
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
def save_keychain_password username , password , hostname
|
159
|
+
# only works for OSX at the minute.
|
160
|
+
return false unless RbConfig::CONFIG['host_os'] =~ /^darwin10/
|
161
|
+
|
162
|
+
# check we already managed to load that gem.
|
163
|
+
if defined? OSXKeychain::VERSION
|
164
|
+
|
165
|
+
if agree("Save password for connection (y/n)? ", true)
|
166
|
+
keychain = OSXKeychain.new
|
167
|
+
|
168
|
+
# update the keychain, unless it's already set to that.
|
169
|
+
keychain.set("rvc", "#{username}@#{hostname}" , password ) unless
|
170
|
+
keychain["rvc", "#{username}@#{hostname}" ] == password
|
171
|
+
end
|
172
|
+
else
|
173
|
+
return false
|
174
|
+
end
|
145
175
|
end
|
146
176
|
|
147
|
-
|
148
|
-
|
177
|
+
|
178
|
+
def check_known_hosts host, peer_public_key
|
179
|
+
known_hosts = RVC::KnownHosts.new
|
180
|
+
result, arg = known_hosts.verify 'vim', host, peer_public_key.to_s
|
181
|
+
|
182
|
+
if result == :not_found
|
183
|
+
puts "The authenticity of host '#{host}' can't be established."
|
184
|
+
puts "Public key fingerprint is #{arg}."
|
185
|
+
err "Connection failed" unless agree("Are you sure you want to continue connecting (y/n)? ", true)
|
186
|
+
puts "Warning: Permanently added '#{host}' (vim) to the list of known hosts"
|
187
|
+
known_hosts.add 'vim', host, peer_public_key.to_s
|
188
|
+
elsif result == :mismatch
|
189
|
+
err "Public key fingerprint for host '#{host}' does not match #{known_hosts.filename}:#{arg}."
|
190
|
+
elsif result == :ok
|
191
|
+
else
|
192
|
+
err "Unexpected result from known_hosts check"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class RbVmomi::VIM
|
197
|
+
def display_info
|
198
|
+
puts serviceContent.about.fullName
|
199
|
+
end
|
200
|
+
|
201
|
+
def _connection
|
202
|
+
self
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
opts :tasks do
|
208
|
+
summary "Watch tasks in progress"
|
209
|
+
end
|
210
|
+
|
211
|
+
def tasks
|
212
|
+
conn = single_connection [$shell.fs.cur]
|
213
|
+
|
214
|
+
begin
|
215
|
+
view = conn.serviceContent.viewManager.CreateListView
|
216
|
+
|
217
|
+
collector = conn.serviceContent.taskManager.CreateCollectorForTasks(:filter => {
|
218
|
+
:time => {
|
219
|
+
:beginTime => conn.serviceInstance.CurrentTime.to_datetime, # XXX
|
220
|
+
:timeType => :queuedTime
|
221
|
+
}
|
222
|
+
})
|
223
|
+
collector.SetCollectorPageSize :maxCount => 1
|
224
|
+
|
225
|
+
filter_spec = {
|
226
|
+
:objectSet => [
|
227
|
+
{
|
228
|
+
:obj => view,
|
229
|
+
:skip => true,
|
230
|
+
:selectSet => [
|
231
|
+
VIM::TraversalSpec(:path => 'view', :type => view.class.wsdl_name)
|
232
|
+
]
|
233
|
+
},
|
234
|
+
{ :obj => collector },
|
235
|
+
],
|
236
|
+
:propSet => [
|
237
|
+
{ :type => 'Task', :pathSet => %w(info.state) },
|
238
|
+
{ :type => 'TaskHistoryCollector', :pathSet => %w(latestPage) },
|
239
|
+
]
|
240
|
+
}
|
241
|
+
filter = conn.propertyCollector.CreateFilter(:partialUpdates => false, :spec => filter_spec)
|
242
|
+
|
243
|
+
ver = ''
|
244
|
+
loop do
|
245
|
+
result = conn.propertyCollector.WaitForUpdates(:version => ver)
|
246
|
+
ver = result.version
|
247
|
+
result.filterSet[0].objectSet.each do |r|
|
248
|
+
remove = []
|
249
|
+
case r.obj
|
250
|
+
when VIM::TaskHistoryCollector
|
251
|
+
infos = collector.ReadNextTasks :maxCount => 100
|
252
|
+
view.ModifyListView :add => infos.map(&:task)
|
253
|
+
when VIM::Task
|
254
|
+
puts "#{Time.now} #{r.obj.info.name} #{r.obj.info.entityName} #{r['info.state']}" unless r['info.state'] == nil
|
255
|
+
remove << r.obj if %w(error success).member? r['info.state']
|
256
|
+
end
|
257
|
+
view.ModifyListView :remove => remove unless remove.empty?
|
258
|
+
end
|
259
|
+
end
|
260
|
+
rescue Interrupt
|
261
|
+
ensure
|
262
|
+
filter.DestroyPropertyFilter if filter
|
263
|
+
collector.DestroyCollector if collector
|
264
|
+
view.DestroyView if view
|
265
|
+
end
|
149
266
|
end
|
data/lib/rvc/modules/vm.rb
CHANGED
@@ -104,6 +104,9 @@ opts :create do
|
|
104
104
|
opt :pool, "Resource pool", :short => 'p', :type => :string, :lookup => VIM::ResourcePool
|
105
105
|
opt :host, "Host", :short => 'h', :type => :string, :lookup => VIM::HostSystem
|
106
106
|
opt :datastore, "Datastore", :short => 'd', :type => :string, :lookup => VIM::Datastore
|
107
|
+
opt :disksize, "Size in KB of primary disk", :short => 's', :type => :int, :default => 4000000
|
108
|
+
opt :memory, "Size in MB of memory", :short => 'm', :type => :int, :default => 128
|
109
|
+
opt :cpucount, "Number of CPUs", :short => 'c', :type => :int, :default => 1
|
107
110
|
end
|
108
111
|
|
109
112
|
def create dest, opts
|
@@ -115,8 +118,8 @@ def create dest, opts
|
|
115
118
|
:name => name,
|
116
119
|
:guestId => 'otherGuest',
|
117
120
|
:files => { :vmPathName => datastore_path },
|
118
|
-
:numCPUs =>
|
119
|
-
:memoryMB =>
|
121
|
+
:numCPUs => opts[:cpucount],
|
122
|
+
:memoryMB => opts[:memory],
|
120
123
|
:deviceChange => [
|
121
124
|
{
|
122
125
|
:operation => :add,
|
@@ -137,7 +140,7 @@ def create dest, opts
|
|
137
140
|
),
|
138
141
|
:controllerKey => 1000,
|
139
142
|
:unitNumber => 0,
|
140
|
-
:capacityInKB =>
|
143
|
+
:capacityInKB => opts[:disksize]
|
141
144
|
)
|
142
145
|
}, {
|
143
146
|
:operation => :add,
|
@@ -516,10 +519,13 @@ opts :snapshot do
|
|
516
519
|
summary "Snapshot a VM"
|
517
520
|
arg :vm, nil, :lookup => VIM::VirtualMachine
|
518
521
|
arg :name, "Name of new snapshot"
|
522
|
+
opt :description, "Description", :short => 'd', :default => ""
|
523
|
+
opt :quiesce, "Quiesce", :short => 'q', :default => false
|
524
|
+
opt :memory, "Memory", :short => 'm', :default => true
|
519
525
|
end
|
520
526
|
|
521
|
-
def snapshot vm, name
|
522
|
-
tasks [vm], :CreateSnapshot, :memory =>
|
527
|
+
def snapshot vm, name, opts
|
528
|
+
tasks [vm], :CreateSnapshot, :description => opts[:description], :memory => opts[:memory], :name => name, :quiesce => opts[:quiesce]
|
523
529
|
end
|
524
530
|
|
525
531
|
|
@@ -648,7 +654,7 @@ def find_vmx_files ds
|
|
648
654
|
files = []
|
649
655
|
results.each do |result|
|
650
656
|
result.file.each do |file|
|
651
|
-
files << result.folderPath
|
657
|
+
files << "#{result.folderPath}/#{file.path}"
|
652
658
|
end
|
653
659
|
end
|
654
660
|
|
data/lib/rvc/modules/vmrc.rb
CHANGED
@@ -33,6 +33,9 @@ when /linux/
|
|
33
33
|
VMRC_SHA256 = "c86ecd9d9a1dd909a119c19d28325cb87d6e2853885d3014a7dac65175dd2ae1"
|
34
34
|
VMRC_BIN = "vmware-vmrc"
|
35
35
|
else
|
36
|
+
VMRC_NAME = nil
|
37
|
+
VMRC_SHA256 = nil
|
38
|
+
VMRC_BIN = nil
|
36
39
|
$stderr.puts "No VMRC available for OS #{RbConfig::CONFIG['host_os']}"
|
37
40
|
end
|
38
41
|
|
@@ -40,6 +43,7 @@ VMRC_BASENAME = "#{VMRC_NAME}.xpi"
|
|
40
43
|
VMRC_URL = "http://cloud.github.com/downloads/vmware/rvc/#{VMRC_BASENAME}"
|
41
44
|
|
42
45
|
def find_local_vmrc
|
46
|
+
return nil if VMRC_NAME.nil?
|
43
47
|
path = File.join(Dir.tmpdir, VMRC_NAME, 'plugins', VMRC_BIN)
|
44
48
|
File.exists?(path) && path
|
45
49
|
end
|
@@ -52,6 +56,7 @@ end
|
|
52
56
|
opts :view do
|
53
57
|
summary "Spawn a VMRC"
|
54
58
|
text "The VMware Remote Console allows you to interact with a VM's virtual mouse, keyboard, and screen."
|
59
|
+
opt :install, "Automatically install VMRC", :short => 'i'
|
55
60
|
arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
|
56
61
|
end
|
57
62
|
|
@@ -59,8 +64,16 @@ rvc_alias :view
|
|
59
64
|
rvc_alias :view, :vmrc
|
60
65
|
rvc_alias :view, :v
|
61
66
|
|
62
|
-
def view vms
|
63
|
-
|
67
|
+
def view vms, opts
|
68
|
+
unless vmrc = find_vmrc
|
69
|
+
if opts[:install]
|
70
|
+
install
|
71
|
+
vmrc = find_vmrc
|
72
|
+
else
|
73
|
+
err "VMRC not found. You may need to run vmrc.install."
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
64
77
|
vms.each do |vm|
|
65
78
|
moref = vm._ref
|
66
79
|
ticket = vm._connection.serviceInstance.content.sessionManager.AcquireCloneTicket
|
@@ -143,6 +156,7 @@ def extract src, dst
|
|
143
156
|
dst_filename = File.join(dst, e.name)
|
144
157
|
case e.ftype
|
145
158
|
when :file
|
159
|
+
FileUtils.mkdir_p File.dirname(dst_filename)
|
146
160
|
zf.extract e.name, dst_filename
|
147
161
|
File.chmod(e.unix_perms, dst_filename) if e.unix_perms
|
148
162
|
when :directory
|
data/lib/rvc/modules.rb
CHANGED
@@ -18,12 +18,16 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
+
require 'rvc/util'
|
22
|
+
|
21
23
|
module RVC
|
22
24
|
|
23
25
|
ALIASES = {}
|
24
26
|
MODULES = {}
|
25
27
|
|
26
28
|
class CmdModule
|
29
|
+
include RVC::Util
|
30
|
+
|
27
31
|
def initialize module_name
|
28
32
|
@module_name = module_name
|
29
33
|
@opts = {}
|
data/lib/rvc/option_parser.rb
CHANGED
@@ -74,6 +74,8 @@ class OptionParser < Trollop::Parser
|
|
74
74
|
end
|
75
75
|
end
|
76
76
|
@args << [name,spec]
|
77
|
+
description = "Path to a" if description == nil and spec[:lookup]
|
78
|
+
description = "Child of a" if description == nil and spec[:lookup_parent]
|
77
79
|
text " #{name}: " + [description, spec[:lookup], spec[:lookup_parent]].compact.join(' ')
|
78
80
|
end
|
79
81
|
|
@@ -82,41 +84,41 @@ class OptionParser < Trollop::Parser
|
|
82
84
|
|
83
85
|
@specs.each do |name,spec|
|
84
86
|
next unless klass = spec[:lookup] and path = opts[name]
|
85
|
-
opts[name] = lookup_single! path, klass
|
87
|
+
opts[name] = RVC::Util.lookup_single! path, klass
|
86
88
|
end
|
87
89
|
|
88
90
|
argv = leftovers
|
89
91
|
args = []
|
90
92
|
@args.each do |name,spec|
|
91
93
|
if spec[:multi]
|
92
|
-
err "missing argument '#{name}'" if spec[:required] and argv.empty?
|
94
|
+
RVC::Util.err "missing argument '#{name}'" if spec[:required] and argv.empty?
|
93
95
|
a = (argv.empty? ? spec[:default] : argv.dup)
|
94
96
|
a = a.map { |x| postprocess_arg x, spec }.inject([], :+)
|
95
|
-
err "no matches for '#{name}'" if spec[:required] and a.empty?
|
97
|
+
RVC::Util.err "no matches for '#{name}'" if spec[:required] and a.empty?
|
96
98
|
args << a
|
97
99
|
argv.clear
|
98
100
|
else
|
99
101
|
x = argv.shift
|
100
|
-
err "missing argument '#{name}'" if spec[:required] and x.nil?
|
102
|
+
RVC::Util.err "missing argument '#{name}'" if spec[:required] and x.nil?
|
101
103
|
x = spec[:default] if x.nil?
|
102
104
|
a = x.nil? ? [] : postprocess_arg(x, spec)
|
103
|
-
err "more than one match for #{name}" if a.size > 1
|
104
|
-
err "no match for '#{name}'" if spec[:required] and a.empty?
|
105
|
+
RVC::Util.err "more than one match for #{name}" if a.size > 1
|
106
|
+
RVC::Util.err "no match for '#{name}'" if spec[:required] and a.empty?
|
105
107
|
args << a.first
|
106
108
|
end
|
107
109
|
end
|
108
|
-
err "too many arguments" unless argv.empty?
|
110
|
+
RVC::Util.err "too many arguments" unless argv.empty?
|
109
111
|
return args, opts
|
110
112
|
end
|
111
113
|
|
112
114
|
def postprocess_arg x, spec
|
113
115
|
if spec[:lookup]
|
114
|
-
lookup!(x, spec[:lookup]).
|
115
|
-
tap { |a| err "no matches for #{x.inspect}" if a.empty? }
|
116
|
+
RVC::Util.lookup!(x, spec[:lookup]).
|
117
|
+
tap { |a| RVC::Util.err "no matches for #{x.inspect}" if a.empty? }
|
116
118
|
elsif spec[:lookup_parent]
|
117
|
-
lookup!(File.dirname(x), spec[:lookup_parent]).
|
119
|
+
RVC::Util.lookup!(File.dirname(x), spec[:lookup_parent]).
|
118
120
|
map { |y| [y, File.basename(x)] }.
|
119
|
-
tap { |a| err "no matches for #{File.dirname(x).inspect}" if a.empty? }
|
121
|
+
tap { |a| RVC::Util.err "no matches for #{File.dirname(x).inspect}" if a.empty? }
|
120
122
|
else
|
121
123
|
[x]
|
122
124
|
end
|
data/lib/rvc/shell.rb
CHANGED
@@ -21,10 +21,11 @@
|
|
21
21
|
module RVC
|
22
22
|
|
23
23
|
class Shell
|
24
|
-
attr_reader :fs, :connections
|
24
|
+
attr_reader :fs, :connections, :session
|
25
25
|
attr_accessor :debug
|
26
26
|
|
27
|
-
def initialize
|
27
|
+
def initialize session
|
28
|
+
@session = session
|
28
29
|
@persist_ruby = false
|
29
30
|
@fs = RVC::FS.new RVC::RootNode.new
|
30
31
|
@ruby_evaluator = RubyEvaluator.new @fs
|
@@ -39,7 +40,7 @@ class Shell
|
|
39
40
|
end
|
40
41
|
|
41
42
|
if input[0..0] == '!'
|
42
|
-
system_fg input[1..-1]
|
43
|
+
RVC::Util.system_fg input[1..-1]
|
43
44
|
return
|
44
45
|
end
|
45
46
|
|
@@ -57,13 +58,13 @@ class Shell
|
|
57
58
|
end
|
58
59
|
rescue SystemExit, IOError
|
59
60
|
raise
|
60
|
-
rescue UserError, RuntimeError, RbVmomi::Fault
|
61
|
+
rescue RVC::Util::UserError, RuntimeError, RbVmomi::Fault
|
61
62
|
if ruby or debug
|
62
63
|
puts "#{$!.class}: #{$!.message}"
|
63
64
|
puts $!.backtrace * "\n"
|
64
65
|
else
|
65
66
|
case $!
|
66
|
-
when RbVmomi::Fault, UserError
|
67
|
+
when RbVmomi::Fault, RVC::Util::UserError
|
67
68
|
puts $!.message
|
68
69
|
else
|
69
70
|
puts "#{$!.class}: #{$!.message}"
|
@@ -78,20 +79,20 @@ class Shell
|
|
78
79
|
def eval_command input
|
79
80
|
cmd, *args = Shellwords.shellwords(input)
|
80
81
|
return unless cmd
|
81
|
-
err "invalid command" unless cmd.is_a? String
|
82
|
+
RVC::Util.err "invalid command" unless cmd.is_a? String
|
82
83
|
case cmd
|
83
84
|
when RVC::FS::MARK_PATTERN
|
84
|
-
CMD.basic.cd lookup_single(cmd)
|
85
|
+
CMD.basic.cd RVC::Util.lookup_single(cmd)
|
85
86
|
else
|
86
87
|
if cmd.include? '.'
|
87
88
|
module_name, cmd, = cmd.split '.'
|
88
89
|
elsif ALIASES.member? cmd
|
89
90
|
module_name, cmd, = ALIASES[cmd].split '.'
|
90
91
|
else
|
91
|
-
err "unknown alias #{cmd}"
|
92
|
+
RVC::Util.err "unknown alias #{cmd}"
|
92
93
|
end
|
93
94
|
|
94
|
-
m = MODULES[module_name] or err("unknown module #{module_name}")
|
95
|
+
m = MODULES[module_name] or RVC::Util.err("unknown module #{module_name}")
|
95
96
|
|
96
97
|
opts_block = m.opts_for(cmd.to_sym)
|
97
98
|
parser = RVC::OptionParser.new cmd, &opts_block
|
@@ -123,7 +124,11 @@ class Shell
|
|
123
124
|
end
|
124
125
|
|
125
126
|
def prompt
|
126
|
-
|
127
|
+
if false
|
128
|
+
"#{@fs.display_path}#{$terminal.color(@persist_ruby ? '~' : '>', :yellow)} "
|
129
|
+
else
|
130
|
+
"#{@fs.display_path}#{@persist_ruby ? '~' : '>'} "
|
131
|
+
end
|
127
132
|
end
|
128
133
|
|
129
134
|
def introspect_object obj
|
@@ -175,6 +180,10 @@ class Shell
|
|
175
180
|
puts klass
|
176
181
|
end
|
177
182
|
end
|
183
|
+
|
184
|
+
def delete_numeric_marks
|
185
|
+
@session.marks.grep(/^\d+$/).each { |x| @session.set_mark x, nil }
|
186
|
+
end
|
178
187
|
end
|
179
188
|
|
180
189
|
class RubyEvaluator
|
@@ -204,7 +213,7 @@ class RubyEvaluator
|
|
204
213
|
if a.empty?
|
205
214
|
if MODULES.member? str
|
206
215
|
MODULES[str]
|
207
|
-
elsif str =~ /_?([\w\d]+)(!?)/ && objs =
|
216
|
+
elsif str =~ /_?([\w\d]+)(!?)/ && objs = $shell.session.get_mark($1)
|
208
217
|
if $2 == '!'
|
209
218
|
objs
|
210
219
|
else
|