vbox-ng 0.1.3 → 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/Gemfile CHANGED
@@ -10,7 +10,7 @@ gem "awesome_print", "~> 1.1.0"
10
10
  group :development do
11
11
  gem "rspec", "~> 2.12.0"
12
12
  # gem "rdoc", "~> 3.12"
13
- gem "bundler", "~> 1.0.0"
13
+ gem "bundler", ">= 1.0.0"
14
14
  gem "jeweler", "~> 1.8.4"
15
- # gem "rcov", ">= 0"
15
+ gem "simplecov"
16
16
  end
@@ -10,6 +10,7 @@ GEM
10
10
  rake
11
11
  rdoc
12
12
  json (1.7.5)
13
+ multi_json (1.0.4)
13
14
  rake (10.0.2)
14
15
  rdoc (3.12)
15
16
  json (~> 1.4)
@@ -21,12 +22,17 @@ GEM
21
22
  rspec-expectations (2.12.0)
22
23
  diff-lcs (~> 1.1.3)
23
24
  rspec-mocks (2.12.0)
25
+ simplecov (0.7.1)
26
+ multi_json (~> 1.0)
27
+ simplecov-html (~> 0.7.1)
28
+ simplecov-html (0.7.1)
24
29
 
25
30
  PLATFORMS
26
31
  ruby
27
32
 
28
33
  DEPENDENCIES
29
34
  awesome_print (~> 1.1.0)
30
- bundler (~> 1.0.0)
35
+ bundler (>= 1.0.0)
31
36
  jeweler (~> 1.8.4)
32
37
  rspec (~> 2.12.0)
38
+ simplecov
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- vbox-ng
1
+ vbox-ng [![Build Status](https://secure.travis-ci.org/zed-0xff/vbox-ng.png)](http://secure.travis-ci.org/zed-0xff/vbox-ng) [![Dependency Status](https://gemnasium.com/zed-0xff/vbox-ng.png)](https://gemnasium.com/zed-0xff/vbox-ng)
2
2
  ======
3
3
 
4
4
  Description
@@ -25,15 +25,14 @@ Commandline usage
25
25
  acpipowerbutton, acpisleepbutton, clone, delete, show, snapshots
26
26
 
27
27
  OPTIONS:
28
- -g, --[no-]glob (default: auto) assume <vm_name> is a wildcard,
29
- and run on multiple VMs.
30
- All glob(7) patterns like *,?,[a-z] are supported
31
- plus additional pattern {1-20} which matches
32
- a sequence of numbers: 1,2,3,...,19,20
28
+ -g, --[no-]glob assume <vm_name> is a wildcard & run on multiple VMs.
29
+ All glob(7) patterns are supported plus additional
30
+ pattern "{1-20}" - expands to a sequence: 1,2,3,...,19,20
33
31
  -n, --dry-run do not change anything, just print commands to be invoked
34
32
  -v, --verbose increase verbosity
35
- -c, --clones N clone: make N clones
33
+ -N, --clones N clone: make N clones
36
34
  -S, --snapshot MODE clone: use LAST shapshot or make NEW
35
+ --name NAME clone: name for the clone VM
37
36
  -H, --headless start: start VM in headless mode
38
37
  -h, --help show this message
39
38
 
@@ -41,7 +40,7 @@ Commandline usage
41
40
  vbox -v - list VMs with memory and dir sizes
42
41
  vbox "d{1-10}" list - list only VMs named 'd1','d2','d3',...,'d10'
43
42
  vbox "test*" start - start VMs which name starts with 'test'
44
- vbox "v[ace]" cpus=2 - set 'number of cpus'=2 on VMs named 'va','vc','ve'
43
+ vbox "v[ace]" cpus=2 acpi=on - set number of cpus & ACPI on VMs named 'va','vc','ve'
45
44
  vbox d0 - list all parameters of VM named 'd0'
46
45
  vbox d0 clone -c 10 -S last - make 10 new linked clones of vm 'd0' using the
47
46
  latest hdd snapshot, if any
data/Rakefile CHANGED
@@ -32,17 +32,27 @@ Jeweler::RubygemsDotOrgTasks.new
32
32
 
33
33
  require 'rspec/core'
34
34
  require 'rspec/core/rake_task'
35
+
35
36
  RSpec::Core::RakeTask.new(:spec) do |spec|
36
37
  spec.pattern = FileList['spec/**/*_spec.rb']
37
38
  end
38
39
 
39
- RSpec::Core::RakeTask.new(:rcov) do |spec|
40
- spec.pattern = 'spec/**/*_spec.rb'
41
- spec.rcov = true
42
- end
43
-
44
40
  task :default => :spec
45
41
 
42
+ namespace :spec do
43
+ desc "record VBoxManage simulation"
44
+ task :record do
45
+ ENV['RECORD_VBOXMANAGE'] = '1'
46
+ Rake::Task[:spec].invoke
47
+ end
48
+
49
+ desc "replay VBoxManage simulation"
50
+ task :replay do
51
+ ENV['SIMULATE_VBOXMANAGE'] = '1'
52
+ Rake::Task[:spec].execute
53
+ end
54
+ end
55
+
46
56
  #require 'rdoc/task'
47
57
  #Rake::RDocTask.new do |rdoc|
48
58
  # version = File.exist?('VERSION') ? File.read('VERSION') : ""
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.3
1
+ 1.0.0
@@ -5,7 +5,19 @@ module VBOX
5
5
  # UUID_RE = /\{\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\}/ # only in ruby 1.9 :(
6
6
  UUID_RE = /\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}/i
7
7
 
8
- def self.api
9
- @@api ||= CmdLineAPI.new
8
+ @@verbosity = ENV['VBOX_DEBUG'].to_i
9
+
10
+ class << self
11
+ def api
12
+ @@api ||= CmdLineAPI.new
13
+ end
14
+
15
+ def verbosity
16
+ @@verbosity
17
+ end
18
+
19
+ def verbosity= v
20
+ @@verbosity = v
21
+ end
10
22
  end
11
23
  end
@@ -5,7 +5,19 @@ module VBOX
5
5
  # UUID_RE = /\{\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\}/ # only in ruby 1.9 :(
6
6
  UUID_RE = /\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}/i
7
7
 
8
- def self.api
9
- @@api ||= CmdLineAPI.new
8
+ @@verbosity = ENV['VBOX_DEBUG'].to_i
9
+
10
+ class << self
11
+ def api
12
+ @@api ||= CmdLineAPI.new
13
+ end
14
+
15
+ def verbosity
16
+ @@verbosity
17
+ end
18
+
19
+ def verbosity= v
20
+ @@verbosity = v
21
+ end
10
22
  end
11
23
  end
@@ -11,6 +11,7 @@ module VBOX
11
11
  @argv = argv
12
12
  end
13
13
 
14
+ private
14
15
  def _join_by_width words, params = {}
15
16
  params[:max_length] ||= 30
16
17
  params[:separator] ||= ", "
@@ -28,6 +29,7 @@ module VBOX
28
29
  lines.join params[:newline]
29
30
  end
30
31
 
32
+ public
31
33
  def banner
32
34
  bname = File.basename($0)
33
35
  r = []
@@ -56,7 +58,7 @@ module VBOX
56
58
  r << %Q{\t#{bname} -v - list VMs with memory and dir sizes}
57
59
  r << %Q{\t#{bname} "d{1-10}" list - list only VMs named 'd1','d2','d3',...,'d10'}
58
60
  r << %Q{\t#{bname} "test*" start - start VMs which name starts with 'test'}
59
- r << %Q{\t#{bname} "v[ace]" cpus=2 - set 'number of cpus'=2 on VMs named 'va','vc','ve'}
61
+ r << %Q{\t#{bname} "v[ace]" cpus=2 acpi=on - set number of cpus & ACPI on VMs named 'va','vc','ve'}
60
62
  r << %Q{\t#{bname} d0 - list all parameters of VM named 'd0'}
61
63
  r << %Q{\t#{bname} d0 clone -c 10 -S last - make 10 new linked clones of vm 'd0' using the}
62
64
  r << %Q{\t#{space} latest hdd snapshot, if any}
@@ -70,17 +72,16 @@ module VBOX
70
72
  r.join("\n")
71
73
  end
72
74
 
73
- def run
75
+ def parse_argv
74
76
  @options = { :verbose => 0 }
75
77
  optparser = OptionParser.new do |opts|
76
78
  opts.banner = banner
77
79
  opts.summary_indent = "\t"
78
80
 
79
81
  opts.on "-g", "--[no-]glob",
80
- "(default: auto) assume <vm_name> is a wildcard,",
81
- "and run on multiple VMs.",
82
- "All glob(7) patterns like *,?,[a-z] are supported",
83
- "plus additional pattern {1-20} which matches","a sequence of numbers: 1,2,3,...,19,20" do |x|
82
+ "assume <vm_name> is a wildcard & run on multiple VMs.",
83
+ "All glob(7) patterns are supported plus additional",
84
+ "pattern \"{1-20}\" - expands to a sequence: 1,2,3,...,19,20" do |x|
84
85
  @options[:multiple] = x
85
86
  end
86
87
  opts.on "-n", "--dry-run", "do not change anything, just print commands to be invoked" do
@@ -90,13 +91,16 @@ module VBOX
90
91
  @options[:verbose] ||= 0
91
92
  @options[:verbose] += 1
92
93
  end
93
- opts.on "-c", "--clones N", Integer, "clone: make N clones" do |x|
94
+ opts.on "-N", "--clones N", Integer, "clone: make N clones" do |x|
94
95
  @options[:clones] = x
95
96
  end
96
97
  a = 'new last take make'.split.map{ |x| [x, x.upcase] }.flatten
97
98
  opts.on "-S", "--snapshot MODE", a, "clone: use LAST shapshot or make NEW" do |x|
98
99
  @options[:snapshot] = x.downcase
99
100
  end
101
+ opts.on "--name NAME", "clone: name for the clone VM" do |x|
102
+ @options[:name] = x
103
+ end
100
104
  opts.on "-H", "--headless", "start: start VM in headless mode" do
101
105
  @options[:headless] = true
102
106
  end
@@ -113,97 +117,226 @@ module VBOX
113
117
  @options[:multiple] = "{#{@argv.first}}" !~ UUID_RE
114
118
  end
115
119
 
116
- @vbox = VBOX::CmdLineAPI.new(@options)
120
+ VBOX.verbosity = @options[:verbose]
121
+ end
117
122
 
118
- if @argv.size == 0 || @argv.last == 'list'
119
- vms = @vbox.list_vms
120
- @vbox.list_vms(:running => true).each do |vm|
121
- vms.find{ |vm1| vm1.uuid == vm.uuid }.state = :running
122
- end
123
+ def list_vms name_or_glob
124
+ vms = _find_vms name_or_glob
123
125
 
124
- if @argv.size == 2 && @argv.last == 'list'
125
- if @options[:multiple]
126
- @globs = _expand_glob(@argv.first).flatten
127
- vms = vms.keep_if{ |vm| _fnmatch(vm.name) }
128
- else
129
- vms = vms.keep_if{ |vm| vm.name == @argv.first }
130
- end
126
+ longest = (vms.map(&:name).map(&:size)+[4]).max
127
+
128
+ puts "%-*s %5s %6s %-12s %s".gray % [longest, *%w'NAME MEM DIRSZ STATE UUID']
129
+ vms.each do |vm|
130
+ if @options[:verbose] > 0
131
+ vm.fetch_metadata
132
+ state = (vm.state == :poweroff) ? '' : vm.state.to_s.upcase
133
+ s = sprintf "%-*s %5d %6s %-12s %s", longest, vm.name, vm.memory_size, vm.dir_size,
134
+ state, vm.uuid
135
+ else
136
+ state = (vm.state == :poweroff) ? '' : vm.state.to_s.upcase
137
+ s = sprintf "%-*s %5s %6s %-12s %s", longest, vm.name, '', '',
138
+ state, vm.uuid
131
139
  end
140
+ s = s.green if vm.state == :running
141
+ puts s
142
+ end
143
+ end
132
144
 
133
- longest = (vms.map(&:name).map(&:size)+[4]).max
134
-
135
- puts "%-*s %5s %6s %-12s %s".gray % [longest, *%w'NAME MEM DIRSZ STATE UUID']
136
- vms.each do |vm|
137
- if @options[:verbose] > 0
138
- @vbox.get_vm_details vm
139
- state = (vm.state == :poweroff) ? '' : vm.state.to_s.upcase
140
- s = sprintf "%-*s %5d %6s %-12s %s", longest, vm.name, vm.memory_size||0, vm.dir_size||0,
141
- state, vm.uuid
142
- else
143
- state = (vm.state == :poweroff) ? '' : vm.state.to_s.upcase
144
- s = sprintf "%-*s %5s %6s %-12s %s", longest, vm.name, '', '',
145
- state, vm.uuid
146
- end
147
- s = s.green if vm.state == :running
148
- puts s
145
+ def vm_cmd name_or_glob, cmd='show', *args
146
+ vms = _find_vms(name_or_glob)
147
+ if vms.empty?
148
+ if cmd == 'create'
149
+ return vm_cmd_create(name_or_glob)
150
+ else
151
+ STDERR.puts "[?] no VMs matching #{name_or_glob.inspect}".red
152
+ exit 1
149
153
  end
150
- else
151
- name = @argv.shift
152
- cmd = @argv.shift || 'show' # default command is 'show'
154
+ end
153
155
 
154
- cmd = ALIASES[cmd] if ALIASES[cmd]
155
- if @options[:multiple]
156
- _run_multiple_cmd cmd, name
156
+ method =
157
+ if cmd['=']
158
+ # set some VM variables:
159
+ # vbox vm_name foo=bar bar=baz xxx=yyy
160
+ args.unshift(cmd)
161
+ "vm_cmd_set"
157
162
  else
158
- _run_cmd cmd, name
163
+ "vm_cmd_#{cmd}"
159
164
  end
165
+
166
+ unless self.respond_to?(method)
167
+ STDERR.puts "[?] unknown command #{cmd.inspect}".red
168
+ exit 1
169
+ end
170
+ vms.each do |vm|
171
+ send method, vm, *args
160
172
  end
161
173
  end
162
174
 
163
- # expand globs like "d{1-30}" to d1,d2,d3,d4,...,d29,d30
164
- def _expand_glob glob
165
- if glob[/\{(\d+)-(\d+)\}/]
166
- r = []
167
- $1.to_i.upto($2.to_i) do |i|
168
- r << _expand_glob(glob.sub($&,i.to_s))
175
+ SHOW_CATEGORIES = {
176
+ 'GENERAL' => %w'name cpus memory vram cpuexecutioncap UUID VMState',
177
+ 'VIRTUALIZATION OPTIONS' =>
178
+ [
179
+ /^(groups|ostype|hwvirt|nestedpag|largepag|vtxvpid|ioapic|pagefusion|hpet|synthcpu|pae)/,
180
+ /accelerate/, /balloon/i
181
+ ],
182
+ 'NET' => [ /^(nic|nat|mac|bridge|cable|hostonly)/, /^sock/, /^tcp/ ],
183
+ 'STORAGE' => [ /storage/, /SATA/, /IDE/ ],
184
+ 'SNAPSHOTS' => [ /snapshot/i ],
185
+ 'VIRTUAL HARDWARE' => [ /^(lpt|uart|audio|ehci|usb|hardware|chipset|monitor|hid|acpi|firmware|USB)/ ],
186
+ 'TELEPORTING' => [ /teleport/i, /cpuid/i ],
187
+ 'SHARED FOLDERS' => [ /SharedFolder/ ]
188
+ }
189
+
190
+ public
191
+ def vm_cmd_show vm
192
+ vars = vm.metadata.dup
193
+ unless @options[:verbose] > 0
194
+ vars.delete_if{ |k,v| ["none","off","disabled","emptydrive","",0,"0",nil].include?(v) }
195
+ end
196
+ maxlen = vars.keys.map(&:size).max
197
+
198
+ SHOW_CATEGORIES.each do |name, filters|
199
+ keys = []; title = nil
200
+ filters.each do |filter|
201
+ keys += filter.is_a?(Regexp) ? vars.keys.find_all{ |key| key =~ filter } : [filter]
169
202
  end
170
- r
171
- else
172
- [glob]
203
+ keys.each do |k|
204
+ if v = vars.delete(k)
205
+ puts (title = "--- #{name} ".ljust(80,'-')) unless title
206
+ printf(" %-*s: %s\n", maxlen, k, v)
207
+ end
208
+ end
209
+ end
210
+ puts "--- MISC ".ljust(80,'-')
211
+ vars.each do |k,v|
212
+ printf(" %-*s: %s\n", maxlen, k, v)
173
213
  end
174
214
  end
175
215
 
176
- def _fnmatch fname
177
- @globs.each do |glob|
178
- return true if File.fnmatch(glob, fname)
216
+ # create VM
217
+ def vm_cmd_create name
218
+ VM.new(:name => name).create!
219
+ end
220
+
221
+ # destroy VM
222
+ def vm_cmd_destroy vm
223
+ vm.destroy!
224
+ end
225
+ alias :vm_cmd_rm :vm_cmd_destroy
226
+ alias :vm_cmd_delete :vm_cmd_destroy
227
+
228
+ # set VM variables
229
+ def vm_cmd_set vm, *args
230
+ raise "all arguments must contain '='" unless args.all?{ |arg| arg['='] }
231
+ args.each do |arg|
232
+ k,v = arg.split("=",2)
233
+ vm.set_var k, v
179
234
  end
180
- false
235
+ vm.save
181
236
  end
182
237
 
183
- def _run_multiple_cmd cmd, name
184
- vms = @vbox.list_vms
185
- @globs = _expand_glob(name).flatten
186
- vms.each do |vm|
187
- if _fnmatch(vm.name)
188
- _run_cmd cmd, vm.name
189
- end
238
+ # start VM
239
+ def vm_cmd_start vm
240
+ vm.start! :headless => @options[:headless]
241
+ end
242
+
243
+ # pause VM
244
+ def vm_cmd_pause vm
245
+ vm.pause!
246
+ end
247
+
248
+ # resume VM
249
+ def vm_cmd_resume vm
250
+ vm.resume!
251
+ end
252
+ alias :vm_cmd_unpause :vm_cmd_resume
253
+
254
+ # reset VM
255
+ def vm_cmd_reset vm
256
+ vm.reset!
257
+ end
258
+
259
+ # save VM state
260
+ def vm_cmd_savestate vm
261
+ vm.savestate!
262
+ end
263
+ alias :vm_cmd_save_state :vm_cmd_savestate
264
+
265
+ # stop VM
266
+ def vm_cmd_poweroff vm
267
+ vm.poweroff!
268
+ end
269
+ alias :vm_cmd_stop :vm_cmd_poweroff
270
+
271
+ # ACPI 'Power' Button
272
+ def vm_cmd_acpipowerbutton vm
273
+ vm.acpipowerbutton!
274
+ end
275
+
276
+ # ACPI 'Sleep' Button
277
+ def vm_cmd_acpisleepbutton vm
278
+ vm.acpisleepbutton!
279
+ end
280
+
281
+ # clone VM
282
+ def vm_cmd_clone vm
283
+ # TODO: page fusion
284
+ unless @options[:snapshot]
285
+ puts "[!] please gimme --snapshot=LAST OR --snapshot=NEW option".red
286
+ exit 1
287
+ end
288
+ vm.clone! @options
289
+ end
290
+
291
+ # manage VM snapshots
292
+ def vm_cmd_snapshots vm, *args
293
+ vm.snapshots.each do |s|
294
+ printf "%s %s\n", s.uuid, s.name
190
295
  end
191
296
  end
297
+ alias :vm_cmd_snapshot :vm_cmd_snapshots
298
+
299
+ def run
300
+ parse_argv
301
+ # now @argv contains only VM name and commands, if any
192
302
 
193
- def _run_cmd cmd, name
194
- if COMMANDS.include?(cmd)
195
- @vbox.send cmd, name
196
- elsif cmd['=']
197
- # set some variable, f.ex. "macaddress1=BADC0FFEE000"
198
- @vbox.modify name, *cmd.split('=',2)
199
- elsif cmd == 'snapshots'
200
- @vbox.get_snapshots(name).each do |x|
201
- printf "%s %s\n", x.uuid, x.name
303
+ if @argv.empty? || (@argv.size <= 2 && @argv.include?('list'))
304
+ # vbox
305
+ # vbox list
306
+ # vbox list "a*"
307
+ # vbox "a*" list
308
+ @argv.delete_at(@argv.index('list') || 999) # delete only 1st 'list' entry
309
+ list_vms @argv.first
310
+ elsif @argv.empty? || (@argv.size <= 2 && @argv.include?('ls'))
311
+ # vbox
312
+ # vbox ls
313
+ # vbox ls "a*"
314
+ # vbox "a*" ls
315
+ @argv.delete_at(@argv.index('ls') || 999) # delete only 1st 'ls' entry
316
+ list_vms @argv.first
317
+ else
318
+ # vbox VM
319
+ # vbox VM show
320
+ # vbox VM ...
321
+ # - where 'VM' can be vm name or glob or UUID
322
+ vm_cmd *@argv
323
+ end
324
+ end
325
+
326
+ private
327
+
328
+ def _find_vms name_or_glob
329
+ if name_or_glob
330
+ if @options[:multiple]
331
+ # glob
332
+ VM.find_all(name_or_glob)
333
+ else
334
+ # exact name
335
+ [ VM.find(name_or_glob) ]
202
336
  end
203
337
  else
204
- STDERR.puts "[!] unknown command #{cmd.inspect}".red
205
- puts @help
206
- exit 1
338
+ # all VMs
339
+ VM.all
207
340
  end
208
341
  end
209
342
  end