vbox-ng 0.1.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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