rvc 1.4.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -131,6 +131,15 @@ is represented by a top-level node in the virtual filesystem. If more than one
131
131
  host is given on the command line, RVC will start in the root of the filesystem
132
132
  instead of automatically cd'ing to a connection.
133
133
 
134
+ == VNC Clients
135
+ === OSX
136
+ Using homebrew https://github.com/mxcl/homebrew install tiger-vnc using the
137
+ command
138
+
139
+ "brew install tiger-vnc"
140
+
141
+ this will allow you to use the vnc.view command
142
+
134
143
  == Extensibility
135
144
 
136
145
  RVC is designed to let users easily add commands they need. You can create a
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ begin
7
7
  gem.email = "rlane@vmware.com"
8
8
  #gem.homepage = ""
9
9
  gem.authors = ["Rich Lane"]
10
- gem.add_dependency 'rbvmomi', '>= 1.2.3'
10
+ gem.add_dependency 'rbvmomi', '>= 1.3.0'
11
11
  gem.add_dependency 'trollop', '>= 1.16.2'
12
12
  gem.add_dependency 'backports', '>= 1.18.2'
13
13
  gem.add_dependency 'highline', '>= 1.6.1'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.4.1
1
+ 1.5.0
data/bin/rvc CHANGED
@@ -53,8 +53,11 @@ EOS
53
53
  opt :path, "Initial directory", :short => :none, :default => ENV['RVC_PATH'], :type => :string
54
54
  opt :create_directory, "Create the initial directory if it doesn't exist", :short => :none
55
55
  opt :cmd, "command to evaluate", :short => 'c', :multi => true, :type => :string
56
+ opt :script, "file to execute", :short => 's', :type => :string
56
57
  end
57
58
 
59
+ $interactive = $opts[:script].nil? and $stdin.tty?
60
+
58
61
  RVC.reload_modules false
59
62
 
60
63
  session = if ENV['RVC_SESSION'] and not ENV['RVC_SESSION'].empty?
@@ -66,7 +69,7 @@ end
66
69
  $shell = RVC::Shell.new session
67
70
 
68
71
  # Prompt for hostname if none given. Useful on win32.
69
- if $shell.session.connections.empty? and ARGV.empty?
72
+ if $shell.session.connections.empty? and ARGV.empty? and $interactive
70
73
  ARGV << Readline.readline("Host to connect to (user@host): ")
71
74
  end
72
75
 
@@ -92,10 +95,12 @@ ARGV.each do |uri|
92
95
  end
93
96
  end
94
97
 
95
- RVC::Completion.install
96
- history_fn = "#{ENV['HOME']}/.rvc-history"
97
- IO.foreach(history_fn) { |l| Readline::HISTORY << l.chomp } rescue puts "Welcome to RVC. Try the 'help' command."
98
- history = File.open(history_fn, 'a')
98
+ if $interactive
99
+ RVC::Completion.install
100
+ history_fn = "#{ENV['HOME']}/.rvc-history"
101
+ IO.foreach(history_fn) { |l| Readline::HISTORY << l.chomp } rescue puts "Welcome to RVC. Try the 'help' command."
102
+ history = File.open(history_fn, 'a')
103
+ end
99
104
 
100
105
  if $opts[:path]
101
106
  begin
@@ -106,7 +111,7 @@ if $opts[:path]
106
111
  RVC::Util.lookup_single(parent_path).CreateFolder(:name => File.basename($opts[:path]))
107
112
  CMD.basic.cd RVC::Util.lookup_single($opts[:path])
108
113
  end
109
- elsif $shell.connections.size == 1
114
+ elsif $shell.connections.size == 1 and $interactive
110
115
  conn_name, conn = $shell.connections.first
111
116
  if conn.serviceContent.about.apiType == 'VirtualCenter'
112
117
  CMD.basic.cd RVC::Util.lookup_single(conn_name)
@@ -121,24 +126,24 @@ if defined? Ocra
121
126
  exit
122
127
  end
123
128
 
124
- unless CMD.vmrc.find_vmrc
125
- $stderr.puts "VMRC is not installed. You will be unable to view virtual machine consoles. Use the vmrc.install command to install it."
126
- end
127
-
128
- if $shell.connections.empty?
129
- $stderr.puts "Use the 'connect' command to connect to an ESX or VC server."
129
+ if $interactive
130
+ $stderr.puts "VMRC is not installed. You will be unable to view virtual machine consoles. Use the vmrc.install command to install it." unless CMD.vmrc.find_vmrc
131
+ $stderr.puts "Use the 'connect' command to connect to an ESX or VC server." if $shell.connections.empty?
132
+ CMD.basic.ls RVC::Util.lookup_single('.')
130
133
  end
131
134
 
132
- CMD.basic.ls RVC::Util.lookup_single('.')
133
-
134
- while true
135
- begin
136
- input = $opts[:cmd].shift || Readline.readline($shell.prompt, false) or break
137
- input = input.strip
138
- next if input.empty?
139
- (history.puts input; Readline::HISTORY << input) unless input == Readline::HISTORY.to_a[-1]
140
- $shell.eval_input input
141
- rescue Interrupt
142
- puts
135
+ if $opts[:script]
136
+ $shell.eval_ruby File.read($opts[:script]), $opts[:script]
137
+ else
138
+ while true
139
+ begin
140
+ input = $opts[:cmd].shift || Readline.readline($shell.prompt, false) or break
141
+ input = input.strip
142
+ next if input.empty?
143
+ (history.puts input; Readline::HISTORY << input) unless input == Readline::HISTORY.to_a[-1]
144
+ $shell.eval_input input
145
+ rescue Interrupt
146
+ puts
147
+ end
143
148
  end
144
149
  end
@@ -71,6 +71,7 @@ class RbVmomi::VIM::VirtualMachine
71
71
  'datastores' => RVC::FakeFolder.new(self, :rvc_children_datastores),
72
72
  'networks' => RVC::FakeFolder.new(self, :rvc_children_networks),
73
73
  'files' => RVC::FakeFolder.new(self, :rvc_children_files),
74
+ 'snapshots' => RVC::RootSnapshotFolder.new(self),
74
75
  }
75
76
  end
76
77
 
@@ -96,3 +97,70 @@ class RbVmomi::VIM::VirtualMachine
96
97
  end]
97
98
  end
98
99
  end
100
+
101
+ class RVC::RootSnapshotFolder
102
+ include RVC::InventoryObject
103
+
104
+ def initialize vm
105
+ @vm = vm
106
+ end
107
+
108
+ def children
109
+ info = @vm.snapshot
110
+ return {} unless info
111
+ Hash[info.rootSnapshotList.map { |x| [x.name, RVC::SnapshotFolder.new(@vm, [x.id])] }]
112
+ end
113
+
114
+ def display_info
115
+ puts "Root of a VM's snapshot tree"
116
+ end
117
+ end
118
+
119
+ class RVC::SnapshotFolder
120
+ include RVC::InventoryObject
121
+
122
+ def initialize vm, ids
123
+ @vm = vm
124
+ @ids = ids
125
+ end
126
+
127
+ def self.to_s
128
+ 'Snapshot'
129
+ end
130
+
131
+ def find_tree
132
+ cur = nil
133
+ info = @vm.snapshot
134
+ fail "snapshot not found" unless info
135
+ children = info.rootSnapshotList
136
+ @ids.each do |id|
137
+ cur = children.find { |x| x.id == id }
138
+ fail "snapshot not found" unless cur
139
+ children = cur.childSnapshotList
140
+ end
141
+ cur
142
+ end
143
+
144
+ def children
145
+ tree = find_tree
146
+ {}.tap do |h|
147
+ tree.childSnapshotList.each do |x|
148
+ name = x.name
149
+ name = x.name + '.1' if h.member? x.name
150
+ while h.member? name
151
+ name = name.succ
152
+ end
153
+ h[name] = RVC::SnapshotFolder.new(@vm, @ids+[x.id])
154
+ end
155
+ end
156
+ end
157
+
158
+ def display_info
159
+ tree = find_tree
160
+ puts "id: #{tree.id}"
161
+ puts "name: #{tree.name}"
162
+ puts "description: #{tree.description}"
163
+ puts "state: #{tree.state}"
164
+ puts "creation time: #{tree.createTime}"
165
+ end
166
+ end
@@ -42,7 +42,14 @@ rvc_alias :help
42
42
  HELP_ORDER = %w(basic vm)
43
43
 
44
44
  def help path
45
- if tgt = RVC::ALIASES[path]
45
+ if mod = RVC::MODULES[path]
46
+ opts = mod.instance_variable_get(:@opts)
47
+ opts.each do |method_name,method_opts|
48
+ parser = RVC::OptionParser.new method_name, &method_opts
49
+ help_summary parser, path, method_name
50
+ end
51
+ return
52
+ elsif tgt = RVC::ALIASES[path]
46
53
  fail unless tgt =~ /^(.+)\.(.+)$/
47
54
  opts_block = RVC::MODULES[$1].opts_for($2.to_sym)
48
55
  RVC::OptionParser.new(tgt, &opts_block).educate
@@ -69,9 +76,7 @@ def help path
69
76
  opts.each do |method_name,method_opts|
70
77
  parser = RVC::OptionParser.new method_name, &method_opts
71
78
  next unless obj.nil? or parser.applicable.any? { |x| obj.is_a? x }
72
- aliases = ALIASES.select { |k,v| v == "#{mod_name}.#{method_name}" }.map(&:first)
73
- aliases_text = aliases.empty? ? '' : " (#{aliases*', '})"
74
- puts "#{mod_name}.#{method_name}#{aliases_text}: #{parser.summary?}" if parser.summary?
79
+ help_summary parser, mod_name, method_name
75
80
  end
76
81
  end
77
82
 
@@ -84,6 +89,12 @@ To show only commands relevant to a specific object, use "help /path/to/object".
84
89
  end
85
90
  end
86
91
 
92
+ def help_summary parser, mod_name, method_name
93
+ aliases = ALIASES.select { |k,v| v == "#{mod_name}.#{method_name}" }.map(&:first)
94
+ aliases_text = aliases.empty? ? '' : " (#{aliases*', '})"
95
+ puts "#{mod_name}.#{method_name}#{aliases_text}: #{parser.summary?}" if parser.summary?
96
+ end
97
+
87
98
 
88
99
  opts :debug do
89
100
  summary "Toggle VMOMI logging to stderr"
@@ -253,20 +264,30 @@ end
253
264
 
254
265
 
255
266
  opts :mv do
256
- summary "Move/rename an entity"
257
- arg :src, "Source path"
258
- arg :dst, "Destination path"
267
+ summary "Move entities to another folder"
268
+ text "The entities' names are unchanged."
269
+ arg :objs, "Entities to move", :lookup => VIM::ManagedEntity, :multi => true
259
270
  end
260
271
 
261
272
  rvc_alias :mv
262
273
 
263
- def mv src, dst
264
- src_dir = File.dirname(src)
265
- dst_dir = File.dirname(dst)
266
- err "cross-directory mv not yet supported" unless src_dir == dst_dir
267
- dst_name = File.basename(dst)
268
- obj = lookup(src)
269
- obj.Rename_Task(:newName => dst_name).wait_for_completion
274
+ def mv objs
275
+ err "Destination entity missing" unless objs.size > 1
276
+ dst = objs.pop
277
+ progress [dst.MoveIntoFolder_Task(:list => objs)]
278
+ end
279
+
280
+
281
+ opts :rename do
282
+ summary "Rename an entity"
283
+ arg :objs, "Entity to rename", :lookup => VIM::ManagedEntity
284
+ arg :name, "New name"
285
+ end
286
+
287
+ rvc_alias :rename
288
+
289
+ def rename obj, name
290
+ progress [obj.Rename_Task(:newName => name)]
270
291
  end
271
292
 
272
293
 
@@ -51,6 +51,7 @@ def add_host cluster, hostname, opts
51
51
  :asConnected => false
52
52
  begin
53
53
  task.wait_for_completion
54
+ break
54
55
  rescue VIM::SSLVerifyFault
55
56
  puts "SSL thumbprint: #{$!.fault.thumbprint}"
56
57
  $stdout.write "Accept this thumbprint? (y/n) "
@@ -140,3 +140,23 @@ def add_iscsi_target hosts, opts
140
140
  storage.RescanAllHba
141
141
  end
142
142
  end
143
+
144
+ opts :add_nfs_datastore do
145
+ arg :host, nil, :lookup => VIM::HostSystem, :multi => true
146
+ opt :name, "Datastore name", :short => 'n', :type => :string, :required => true
147
+ opt :address, "Address of NFS server", :short => 'a', :type => :string, :required => true
148
+ opt :path, "Path on NFS server", :short => 'p', :type => :string, :required => true
149
+ end
150
+
151
+ def add_nfs_datastore hosts, opts
152
+ hosts.each do |host|
153
+ datastoreSystem, = host.collect 'configManager.datastoreSystem'
154
+ spec = {
155
+ :accessMode => 'readWrite',
156
+ :localPath => opts[:name],
157
+ :remoteHost => opts[:address],
158
+ :remotePath => opts[:path]
159
+ }
160
+ datastoreSystem.CreateNasDatastore :spec => spec
161
+ end
162
+ end
@@ -0,0 +1,61 @@
1
+ opts :create do
2
+ summary "Snapshot a VM"
3
+ arg :vm, nil, :lookup => VIM::VirtualMachine
4
+ arg :name, "Name of new snapshot"
5
+ opt :description, "Description", :short => 'd', :default => ""
6
+ opt :quiesce, "Quiesce", :short => 'q', :default => false
7
+ opt :memory, "Memory", :short => 'm', :default => true
8
+ end
9
+
10
+ def create vm, name, opts
11
+ tasks [vm], :CreateSnapshot, :description => opts[:description], :memory => opts[:memory], :name => name, :quiesce => opts[:quiesce]
12
+ end
13
+
14
+ rvc_alias :create, :snapshot
15
+
16
+
17
+ opts :revert do
18
+ summary "Revert a VM to a snapshot. Defaults to the current snapshot."
19
+ arg :arg, nil, :lookup => [VIM::VirtualMachine, RVC::SnapshotFolder]
20
+ end
21
+
22
+ def revert arg
23
+ if arg.is_a? VIM::VirtualMachine
24
+ tasks [arg], :RevertToCurrentSnapshot
25
+ else
26
+ tasks [arg.find_tree.snapshot], :RevertToSnapshot
27
+ end
28
+ end
29
+
30
+
31
+ opts :rename do
32
+ summary "Rename a snapshot"
33
+ arg :snapshot, nil, :lookup => RVC::SnapshotFolder
34
+ arg :name, "New name", :type => :string
35
+ end
36
+
37
+ def rename snapshot, name
38
+ snapshot.find_tree.snapshot.RenameSnapshot :name => name
39
+ end
40
+
41
+
42
+ opts :describe do
43
+ summary "Change the description of a snapshot"
44
+ arg :snapshot, nil, :lookup => RVC::SnapshotFolder
45
+ arg :description, "New description", :type => :string
46
+ end
47
+
48
+ def describe snapshot, description
49
+ snapshot.find_tree.snapshot.RenameSnapshot :description => description
50
+ end
51
+
52
+
53
+ opts :remove do
54
+ summary "Remove a snapshot"
55
+ arg :snapshot, nil, :lookup => RVC::SnapshotFolder
56
+ opt :remove_children, "Whether to remove the snapshot's children too"
57
+ end
58
+
59
+ def remove snapshot, opts
60
+ tasks [snapshot.find_tree.snapshot], :RemoveSnapshot, :removeChildren => opts[:remove_children]
61
+ end
@@ -0,0 +1,89 @@
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
+ #see vSphere Client: Administration -> vCenter Server Settings -> Statistics -> Statistics Intervals
22
+
23
+ def stats_time secs
24
+ length = secs / 60
25
+
26
+ [[60, "Minutes"], [24, "Hours"], [7, "Days"], [4, "Weeks"], [12, "Months"]].each do |div, name|
27
+ if length < div
28
+ return "#{length} #{name}"
29
+ end
30
+ length = length / div
31
+ end
32
+
33
+ return "#{length} Years"
34
+ end
35
+
36
+
37
+ opts :list do
38
+ summary "List intervals for collecting vCenter statistics"
39
+ end
40
+
41
+ def list
42
+ conn = single_connection [$shell.fs.cur]
43
+ perfman = conn.serviceContent.perfManager
44
+
45
+ columns = [" Name ", "Interval Duration", "Save For ", "Statistics Level"]
46
+ format = columns.map {|s| "%-#{s.size}s"}.join(" | ")
47
+ puts sprintf format, *columns
48
+ puts columns.map {|s| "-" * s.size }.join("-+-")
49
+
50
+ perfman.historicalInterval.each do |interval|
51
+ puts sprintf format, "[#{interval.enabled ? 'x' : ' '}] #{interval.name}",
52
+ stats_time(interval.samplingPeriod),
53
+ stats_time(interval.length), interval.level.to_s
54
+ end
55
+ end
56
+
57
+
58
+ opts :update do
59
+ summary "Update intervals for collecting vCenter statistics"
60
+ arg :name, "Name of the historical interval"
61
+ opt :period, "Number of seconds that data is sampled", :short => 's', :type => :int, :required => false
62
+ opt :length, "Number of seconds that the statistics are saved", :short => 'l', :type => :int, :required => false
63
+ opt :level, "Statistics collection level", :short => 'v', :type => :int, :required => false
64
+ opt :enabled, "Enable or disable the the interval", :short => 'e', :type => :string, :required => false
65
+ end
66
+
67
+ def update name, opts
68
+ conn = single_connection [$shell.fs.cur]
69
+ perfman = conn.serviceContent.perfManager
70
+
71
+ interval = perfman.historicalInterval.select {|i| i.name == name or i.name == "Past #{name}" }.first
72
+ err "no such interval" unless interval
73
+
74
+ interval.samplingPeriod = opts[:period] if opts[:period]
75
+ interval.length = opts[:length] if opts[:length]
76
+ interval.level = opts[:level] if opts[:level]
77
+
78
+ case opts[:enabled]
79
+ when nil
80
+ when "false"
81
+ interval.enabled = false
82
+ when "true"
83
+ interval.enabled = true
84
+ else
85
+ err "invalid value for enabled option"
86
+ end
87
+
88
+ perfman.UpdatePerfInterval(:interval => interval)
89
+ end
@@ -81,14 +81,19 @@ def connect uri, opts
81
81
  unless opts[:rev]
82
82
  # negotiate API version
83
83
  rev = vim.serviceContent.about.apiVersion
84
- vim.rev = [rev, ENV['RVC_VIMREV'] || '4.1'].min
84
+ vim.rev = [rev, ENV['RVC_VIMREV'] || '5.0'].min
85
85
  end
86
86
 
87
87
  isVC = vim.serviceContent.about.apiType == "VirtualCenter"
88
88
 
89
89
  # authenticate
90
90
  if username == nil
91
- username = isVC ? 'Administrator' : 'root'
91
+ if isVC
92
+ isLinux = vim.serviceContent.about.osType == "linux-x64"
93
+ username = isLinux ? 'root' : 'Administrator'
94
+ else
95
+ username = 'root'
96
+ end
92
97
  puts "Using default username #{username.inspect}."
93
98
  end
94
99
 
@@ -104,14 +104,46 @@ 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
107
+ opt :disksize, "Size in KB of primary disk (or add a unit of <M|G|T>)", :short => 's', :type => :string, :default => "4000000"
108
108
  opt :memory, "Size in MB of memory", :short => 'm', :type => :int, :default => 128
109
109
  opt :cpucount, "Number of CPUs", :short => 'c', :type => :int, :default => 1
110
+ text <<-EOB
111
+
112
+ Example:
113
+ vm.create -p ~foo/resourcePool/pools/prod -d ~data/bigdisk -s 10g ~vms/new
114
+
115
+ EOB
116
+ end
117
+
118
+
119
+ def realdisksize( size )
120
+ size.downcase!
121
+ if size =~ /([0-9][0-9,]*)([mgt])?/i
122
+ size = $1.delete(',').to_i
123
+ unit = $2
124
+
125
+ case unit
126
+ when 'm'
127
+ return size * 1024
128
+ when 'g'
129
+ return size * ( 1024 ** 2 )
130
+ when 't'
131
+ return size * ( 1024 ** 3 )
132
+ when nil
133
+ return size
134
+ else
135
+ err "Unknown size modifer of '#{unit}'"
136
+ end
137
+ else
138
+ err "Problem with #{size}"
139
+ end
110
140
  end
111
141
 
142
+
112
143
  def create dest, opts
113
144
  err "must specify resource pool (--pool)" unless opts[:pool]
114
145
  err "must specify datastore (--datastore)" unless opts[:datastore]
146
+ err "memory must be a multiple of 4MB" unless opts[:memory] % 4 == 0
115
147
  vmFolder, name = *dest
116
148
  datastore_path = "[#{opts[:datastore].name}]"
117
149
  config = {
@@ -140,7 +172,7 @@ def create dest, opts
140
172
  ),
141
173
  :controllerKey => 1000,
142
174
  :unitNumber => 0,
143
- :capacityInKB => opts[:disksize]
175
+ :capacityInKB => realdisksize( opts[:disksize] )
144
176
  )
145
177
  }, {
146
178
  :operation => :add,
@@ -217,6 +249,58 @@ def register vmx_file, opts
217
249
  :pool => rp).wait_for_completion
218
250
  end
219
251
 
252
+ opts :bootconfig do
253
+ summary "Alter the boot config settings"
254
+ arg :vm, nil, :lookup => VIM::VirtualMachine
255
+ opt :delay, "Time in milliseconds to delay boot", :short => 'd', :type => :int
256
+ opt :enablebootretry, "Enable rebooting if no boot device found", :short => 'r'
257
+ opt :disablebootretry, "Disable rebooting if no boot device found"
258
+ opt :retrydelay, "Time to wait before rebooting to retry", :short => 't', :type => :int
259
+ opt :show, "Show the current bootoptions", :short => 's'
260
+ conflicts :enablebootretry, :disablebootretry # not that this currently works nicely, but still.
261
+ end
262
+
263
+ def bootconfig vm, opts
264
+
265
+ if opts[:show]
266
+ pp vm.config.bootOptions
267
+ return
268
+ end
269
+
270
+ cur_delay = vm.config.bootOptions.bootDelay
271
+ cur_retrydelay = vm.config.bootOptions.bootRetryDelay
272
+ cur_retryenabled = vm.config.bootOptions.bootRetryEnabled
273
+
274
+ if opts[:delay] and opts[:delay] != cur_delay
275
+ new_delay = opts[:delay]
276
+ else
277
+ new_delay = cur_delay
278
+ end
279
+
280
+ if opts[:retrydelay] and opts[:retrydelay] != cur_retrydelay
281
+ new_retrydelay = opts[:retrydelay]
282
+ new_retryenabled = true
283
+ else
284
+ new_retrydelay = cur_retrydelay
285
+ end
286
+
287
+ if opts[:enablebootretry]
288
+ new_retryenabled = true
289
+ elsif opts[:disablebootretry]
290
+ new_retryenabled = false
291
+ else
292
+ new_retryenabled = cur_retryenabled
293
+ end
294
+
295
+ spec = { :bootOptions => {
296
+ :bootDelay => new_delay,
297
+ :bootRetryDelay => new_retrydelay,
298
+ :bootRetryEnabled => new_retryenabled,
299
+ }
300
+ }
301
+
302
+ vm.ReconfigVM_Task(:spec => spec).wait_for_completion
303
+ end
220
304
 
221
305
  opts :unregister do
222
306
  summary "Unregister a VM"
@@ -250,6 +334,8 @@ opts :answer do
250
334
  end
251
335
 
252
336
  def answer vm, str
337
+ q = vm.runtime.question
338
+ err "no question" unless q
253
339
  choice = q.choice.choiceInfo.find { |x| x.label == str }
254
340
  err("invalid answer") unless choice
255
341
  vm.AnswerVM :questionid => q.path, :answerChoice => choice.key
@@ -391,17 +477,18 @@ end
391
477
  opts :rvc do
392
478
  summary "RVC to a VM"
393
479
  arg :vm, nil, :lookup => VIM::VirtualMachine
480
+ opt :user, "Username", :type => :string
394
481
  end
395
482
 
396
483
  rvc_alias :rvc
397
484
 
398
- def rvc vm
485
+ def rvc vm, opts
399
486
  ip = vm_ip vm
400
487
 
401
488
  env = Hash[%w(RBVMOMI_PASSWORD RBVMOMI_HOST RBVMOMI_USER RBVMOMI_SSL RBVMOMI_PORT
402
489
  RBVMOMI_FOLDER RBVMOMI_DATASTORE RBVMOMI_PATH RBVMOMI_DATACENTER
403
490
  RBVMOMI_COMPUTER).map { |k| [k,nil] }]
404
- cmd = "rvc #{Shellwords.escape ip}"
491
+ cmd = "rvc #{opts[:user] && Shellwords.escape("#{opts[:user]}@")}#{Shellwords.escape ip}"
405
492
  system_fg(cmd, env)
406
493
  end
407
494
 
@@ -515,48 +602,6 @@ def remove_device vm, label
515
602
  end
516
603
 
517
604
 
518
- opts :snapshot do
519
- summary "Snapshot a VM"
520
- arg :vm, nil, :lookup => VIM::VirtualMachine
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
525
- end
526
-
527
- def snapshot vm, name, opts
528
- tasks [vm], :CreateSnapshot, :description => opts[:description], :memory => opts[:memory], :name => name, :quiesce => opts[:quiesce]
529
- end
530
-
531
-
532
- # TODO make fake folder
533
- opts :snapshots do
534
- summary "Display VM snapshot tree"
535
- arg :vm, nil, :lookup => VIM::VirtualMachine
536
- end
537
-
538
- def snapshots vm
539
- _display_snapshot_tree vm.snapshot.rootSnapshotList, 0
540
- end
541
-
542
- def _display_snapshot_tree nodes, indent
543
- nodes.each do |node|
544
- puts "#{' '*indent}#{node.name} #{node.createTime}"
545
- _display_snapshot_tree node.childSnapshotList, (indent+1)
546
- end
547
- end
548
-
549
-
550
- opts :revert do
551
- summary "Revert a VM to its current snapshot"
552
- arg :vm, nil, :lookup => VIM::VirtualMachine
553
- end
554
-
555
- def revert vm
556
- tasks [vm], :RevertToCurrentSnapshot
557
- end
558
-
559
-
560
605
  opts :migrate do
561
606
  summary "Migrate a VM"
562
607
  arg :vm, nil, :lookup => VIM::VirtualMachine, :multi => true
@@ -639,6 +684,30 @@ def annotate vm, str
639
684
  end
640
685
 
641
686
 
687
+ opts :modify_cpu do
688
+ summary "Change CPU configuration"
689
+ arg :vm, nil, :lookup => VIM::VirtualMachine
690
+ opt :num, "New number of CPUs", :type => :int, :required => true
691
+ end
692
+
693
+ def modify_cpu vm, opts
694
+ spec = { :numCPUs => opts[:num] }
695
+ tasks [vm], :ReconfigVM, :spec => spec
696
+ end
697
+
698
+
699
+ opts :modify_memory do
700
+ summary "Change memory configuration"
701
+ arg :vm, nil, :lookup => VIM::VirtualMachine
702
+ opt :size, "New memory size in MB", :type => :int, :required => true
703
+ end
704
+
705
+ def modify_memory vm, opts
706
+ spec = { :memoryMB => opts[:size] }
707
+ tasks [vm], :ReconfigVM, :spec => spec
708
+ end
709
+
710
+
642
711
  def find_vmx_files ds
643
712
  datastorePath = "[#{ds.name}] /"
644
713
  searchSpec = {
@@ -18,7 +18,7 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- VNC = ENV['VNC'] || search_path('vinagre') || search_path('tightvnc')
21
+ VNC = ENV['VNC'] || search_path('vinagre') || search_path('tightvnc') || search_path('vncviewer')
22
22
 
23
23
  opts :view do
24
24
  summary "Spawn a VNC client"
@@ -76,7 +76,8 @@ class OptionParser < Trollop::Parser
76
76
  @args << [name,spec]
77
77
  description = "Path to a" if description == nil and spec[:lookup]
78
78
  description = "Child of a" if description == nil and spec[:lookup_parent]
79
- text " #{name}: " + [description, spec[:lookup], spec[:lookup_parent]].compact.join(' ')
79
+ lookups = [spec[:lookup], spec[:lookup_parent]].flatten.compact
80
+ text " #{name}: " + [description, lookups*' or '].compact.join(' ')
80
81
  end
81
82
 
82
83
  def parse argv
@@ -113,14 +113,15 @@ class Shell
113
113
  nil
114
114
  end
115
115
 
116
- def eval_ruby input
117
- result = @ruby_evaluator.do_eval input
118
- if input =~ /\#$/
119
- introspect_object result
120
- else
121
- pp result
116
+ def eval_ruby input, file="<input>"
117
+ result = @ruby_evaluator.do_eval input, file
118
+ if $interactive
119
+ if input =~ /\#$/
120
+ introspect_object result
121
+ else
122
+ pp result
123
+ end
122
124
  end
123
- nil
124
125
  end
125
126
 
126
127
  def prompt
@@ -187,13 +188,27 @@ class Shell
187
188
  end
188
189
 
189
190
  class RubyEvaluator
191
+ include RVC::Util
192
+
190
193
  def initialize fs
191
- @binding = binding
194
+ @binding = toplevel
192
195
  @fs = fs
193
196
  end
194
197
 
195
- def do_eval input
196
- eval input, @binding
198
+ def toplevel
199
+ binding
200
+ end
201
+
202
+ def do_eval input, file
203
+ begin
204
+ eval input, @binding, file
205
+ rescue Exception => e
206
+ bt = e.backtrace
207
+ bt = bt.reverse.drop_while { |x| !(x =~ /toplevel/) }.reverse
208
+ bt[-1].gsub! ':in `toplevel\'', ''
209
+ e.set_backtrace bt
210
+ raise
211
+ end
197
212
  end
198
213
 
199
214
  def this
@@ -209,6 +224,7 @@ class RubyEvaluator
209
224
  end
210
225
 
211
226
  def method_missing sym, *a
227
+ super unless $shell
212
228
  str = sym.to_s
213
229
  if a.empty?
214
230
  if MODULES.member? str
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rvc
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 1.4.1
5
+ version: 1.5.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Rich Lane
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-09 00:00:00 -07:00
13
+ date: 2011-09-02 00:00:00 -07:00
14
14
  default_executable: rvc
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -21,7 +21,7 @@ dependencies:
21
21
  requirements:
22
22
  - - ">="
23
23
  - !ruby/object:Gem::Version
24
- version: 1.2.3
24
+ version: 1.3.0
25
25
  type: :runtime
26
26
  version_requirements: *id001
27
27
  - !ruby/object:Gem::Dependency
@@ -114,6 +114,8 @@ files:
114
114
  - lib/rvc/modules/permissions.rb
115
115
  - lib/rvc/modules/resource_pool.rb
116
116
  - lib/rvc/modules/role.rb
117
+ - lib/rvc/modules/snapshot.rb
118
+ - lib/rvc/modules/statsinterval.rb
117
119
  - lib/rvc/modules/vim.rb
118
120
  - lib/rvc/modules/vm.rb
119
121
  - lib/rvc/modules/vmrc.rb