rvc 1.4.1 → 1.5.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 +9 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/bin/rvc +28 -23
- data/lib/rvc/extensions/VirtualMachine.rb +68 -0
- data/lib/rvc/modules/basic.rb +35 -14
- data/lib/rvc/modules/cluster.rb +1 -0
- data/lib/rvc/modules/host.rb +20 -0
- data/lib/rvc/modules/snapshot.rb +61 -0
- data/lib/rvc/modules/statsinterval.rb +89 -0
- data/lib/rvc/modules/vim.rb +7 -2
- data/lib/rvc/modules/vm.rb +115 -46
- data/lib/rvc/modules/vnc.rb +1 -1
- data/lib/rvc/option_parser.rb +2 -1
- data/lib/rvc/shell.rb +26 -10
- metadata +5 -3
data/README.rdoc
CHANGED
@@ -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.
|
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.
|
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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
data/lib/rvc/modules/basic.rb
CHANGED
@@ -42,7 +42,14 @@ rvc_alias :help
|
|
42
42
|
HELP_ORDER = %w(basic vm)
|
43
43
|
|
44
44
|
def help path
|
45
|
-
if
|
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
|
-
|
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
|
257
|
-
|
258
|
-
arg :
|
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
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
|
data/lib/rvc/modules/cluster.rb
CHANGED
data/lib/rvc/modules/host.rb
CHANGED
@@ -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
|
data/lib/rvc/modules/vim.rb
CHANGED
@@ -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'] || '
|
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
|
-
|
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
|
|
data/lib/rvc/modules/vm.rb
CHANGED
@@ -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 => :
|
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 = {
|
data/lib/rvc/modules/vnc.rb
CHANGED
@@ -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"
|
data/lib/rvc/option_parser.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/rvc/shell.rb
CHANGED
@@ -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
|
119
|
-
|
120
|
-
|
121
|
-
|
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 =
|
194
|
+
@binding = toplevel
|
192
195
|
@fs = fs
|
193
196
|
end
|
194
197
|
|
195
|
-
def
|
196
|
-
|
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.
|
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-
|
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.
|
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
|