virtual_box 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.project CHANGED
@@ -5,8 +5,14 @@
5
5
  <projects>
6
6
  </projects>
7
7
  <buildSpec>
8
+ <buildCommand>
9
+ <name>com.aptana.ide.core.unifiedBuilder</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
8
13
  </buildSpec>
9
14
  <natures>
10
15
  <nature>org.radrails.rails.core.railsnature</nature>
16
+ <nature>com.aptana.ruby.core.rubynature</nature>
11
17
  </natures>
12
18
  </projectDescription>
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source :rubygems
2
2
 
3
- gem 'hashie', '>= 0.4.0'
3
+ gem 'posix-spawn', '>= 0.3.6'
4
4
  gem 'uuid', '>= 2.3.5'
5
5
 
6
6
  group :development do
@@ -9,6 +9,7 @@ group :development do
9
9
  gem 'minitest', '>= 2.12.1'
10
10
  gem 'mocha', '>= 0.11.0', :require => false
11
11
  gem 'net-ssh', '>= 2.3.0', :require => 'net/ssh'
12
+ gem 'redcarpet', '>= 2.1.1' # Yard needs this to parse Markdown.
12
13
  gem 'simplecov', '>= 0.6.1'
13
- gem 'yard', '>= 0.7.5'
14
+ gem 'yard', '>= 0.8.1'
14
15
  end
data/Gemfile.lock CHANGED
@@ -2,7 +2,6 @@ GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
4
  git (1.2.5)
5
- hashie (1.2.0)
6
5
  jeweler (1.8.3)
7
6
  bundler (~> 1.0)
8
7
  git (>= 1.2.5)
@@ -17,9 +16,11 @@ GEM
17
16
  metaclass (~> 0.0.1)
18
17
  multi_json (1.3.4)
19
18
  net-ssh (2.3.0)
19
+ posix-spawn (0.3.6)
20
20
  rake (0.9.2.2)
21
21
  rdoc (3.12)
22
22
  json (~> 1.4)
23
+ redcarpet (2.1.1)
23
24
  simplecov (0.6.2)
24
25
  multi_json (~> 1.3)
25
26
  simplecov-html (~> 0.5.3)
@@ -27,18 +28,19 @@ GEM
27
28
  systemu (2.5.0)
28
29
  uuid (2.3.5)
29
30
  macaddr (~> 1.0)
30
- yard (0.8.0)
31
+ yard (0.8.1)
31
32
 
32
33
  PLATFORMS
33
34
  ruby
34
35
 
35
36
  DEPENDENCIES
36
37
  bundler (>= 1.1.3)
37
- hashie (>= 0.4.0)
38
38
  jeweler (>= 1.8.3)
39
39
  minitest (>= 2.12.1)
40
40
  mocha (>= 0.11.0)
41
41
  net-ssh (>= 2.3.0)
42
+ posix-spawn (>= 0.3.6)
43
+ redcarpet (>= 2.1.1)
42
44
  simplecov (>= 0.6.1)
43
45
  uuid (>= 2.3.5)
44
- yard (>= 0.7.5)
46
+ yard (>= 0.8.1)
data/README.markdown CHANGED
@@ -28,7 +28,7 @@ Running tests relies on a few command-line tools.
28
28
  On Fedora, use the following command to install the packages.
29
29
 
30
30
  ```bash
31
- sudo yum install advancecomp curl mkisofs p7zip squashfs-tools
31
+ sudo yum install -y advancecomp curl mkisofs p7zip p7zip-plugins squashfs-tools
32
32
  ```
33
33
 
34
34
  On OSX, run the following command.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -8,13 +8,31 @@ module VirtualBox
8
8
  # Runs a command in a sub-shell, waiting until the command completes.
9
9
  #
10
10
  # @param [Array<String>] args the name and arguments for the command to be run
11
- #
12
- # @return [Hashie::Mash<Symbol, String>] hash with the following keys / methods:
11
+ # @return [Hash<Symbol, String>] hash with the following keys:
13
12
  # :status:: the command's exit status
14
13
  # :output:: a string with the command's output
15
14
  def self.run_command(args)
16
- output = Kernel.`(Shellwords.shelljoin(args))
17
- Hashie::Mash.new :status => $CHILD_STATUS.exitstatus, :output => output
15
+ begin
16
+ output = Kernel.`(Shellwords.shelljoin(args) + ' 2>&1')
17
+ { :status => $CHILD_STATUS.exitstatus, :output => output }
18
+
19
+ # TODO(pwnall): this should work, but it makes VirtualBox slow as hell
20
+ # child = POSIX::Spawn::Child.new(*args)
21
+ # Hashie::Mash.new :status => child.status.exitstatus, :output => child.out
22
+ rescue SystemCallError
23
+ { :status => -1, :output => '' }
24
+ end
25
+ end
26
+
27
+ # Runs a command in a sub-shell, waiting until the command completes.
28
+ #
29
+ # @param [Array<String>] args the name and arguments for the command to be run
30
+ # @return [String] the command's output
31
+ # @raise [VirtualBox::Error] if the command exits with a non-zero code
32
+ def self.run_command!(args)
33
+ result = run_command args
34
+ raise VirtualBox::Error, result unless result[:status] == 0
35
+ result[:output]
18
36
  end
19
37
 
20
38
  end # namespace VirtualBox
@@ -0,0 +1,25 @@
1
+ module VirtualBox
2
+
3
+ # Raised when the VirtualBox CLI returns a non-zero error code.
4
+ class Error < StandardError
5
+ # The exit code of the failed VirtualBox command.
6
+ # @return [Integer]
7
+ attr_reader :exit_code
8
+
9
+ # The combined stdout and stderr of the failed VirtualBox command.
10
+ # @return [String]
11
+ attr_reader :output
12
+
13
+ # Called by raise.
14
+ #
15
+ # @param [Hash<Symbol, Object>] cli_result the return value of a
16
+ # VirtualBox.run call.
17
+ def initialize(cli_result)
18
+ @exit_code = cli_result[:status]
19
+ @output = output
20
+
21
+ super "VirtualBox CLI exited with code #{@exit_code}:\n#{@output}\n"
22
+ end
23
+ end # class VirtualBox::Error
24
+
25
+ end # namespace VirtualBox
@@ -1,14 +1,9 @@
1
1
  module VirtualBox
2
2
 
3
+ class Net
4
+
3
5
  # Specification for a virtual DHCP server.
4
6
  class Dhcp
5
- # The name of the VirtualBox network served by this DHCP server.
6
- #
7
- # This name must match the name of a host-only or internal network that is
8
- # registered with VirtualBox.
9
- # @return [String]
10
- attr_accessor :net_name
11
-
12
7
  # This DHCP server's IP address on the virtual network that it serves.
13
8
  # @return [String]
14
9
  attr_accessor :ip
@@ -66,48 +61,54 @@ class Dhcp
66
61
  :start_ip => start_ip, :end_ip => end_ip }
67
62
  end
68
63
 
69
- # True if this DHCP rule has been registered with VirtualBox.
70
- def live?
71
- servers = self.class.all
72
- dhcp = servers.find { |server| server.net_name == net_name }
73
- dhcp ? true : false
74
- end
75
-
76
64
  # Adds this DHCP server to VirtualBox.
77
65
  #
78
- # @return [VirtualBox::Dhcp] self, for easy call chaining
79
- def add
80
- remove if live?
81
-
82
- result = VirtualBox.run_command ['VBoxManage', 'dhcpserver', 'add',
83
- '--netname', net_name, '--ip', ip, '--netmask', netmask,
84
- '--lowerip', start_ip, '--upperip', end_ip, '--enable']
85
- if result.status != 0
86
- raise 'Unexpected error code returned by VirtualBox'
66
+ # @param [String, VirtualBox::Net] net_or_name the name of the VirtualBox
67
+ # virtual network that this server will be connected to
68
+ # @return [VirtualBox::Net::Dhcp] self, for easy call chaining
69
+ def add(net_or_name)
70
+ command = ['VBoxManage', 'dhcpserver', 'add', '--ip', ip,
71
+ '--netmask', netmask, '--lowerip', start_ip, '--upperip', end_ip,
72
+ '--enable']
73
+ if net_or_name.kind_of? VirtualBox::Net
74
+ command.push '--ifname', net_or_name.if_name
75
+ else
76
+ command.push '--netname', net_or_name
87
77
  end
78
+
79
+ VirtualBox.run_command! command
88
80
  self
89
81
  end
90
82
 
91
83
  # Removes this DHCP server from VirtualBox.
92
84
  #
93
- # @return [VirtualBox::Dhcp] self, for easy call chaining
94
- def remove
95
- VirtualBox.run_command ['VBoxManage', 'dhcpserver', 'remove', '--netname',
96
- net_name]
85
+ # @param [String, VirtualBox::Net] net_or_name the name of the VirtualBox
86
+ # virtual network that this server was connected to
87
+ # @return [VirtualBox::Net::Dhcp] self, for easy call chaining
88
+ def remove(net_or_name)
89
+ command = ['VBoxManage', 'dhcpserver', 'remove']
90
+ if net_or_name.kind_of? VirtualBox::Net
91
+ command.push '--ifname', net_or_name.if_name
92
+ else
93
+ command.push '--netname', net_or_name
94
+ end
95
+
96
+ VirtualBox.run_command command
97
97
  self
98
98
  end
99
99
 
100
100
  # The DHCP servers added to with VirtualBox.
101
101
  #
102
- # @return [Array<VirtualBox::Dhcp>] all the DHCP servers added to VirtualBox
102
+ # @return [Hash<String, VirtualBox::Dhcp>] all the DHCP servers added to
103
+ # VirtualBox, indexed by the name of the virtual network that they serve
103
104
  def self.all
104
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list', '--long',
105
- 'dhcpservers']
106
- if result.status != 0
107
- raise 'Unexpected error code returned by VirtualBox'
108
- end
109
- result.output.split("\n\n").
110
- map { |dhcp_info| new.from_dhcp_info(dhcp_info) }
105
+ output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
106
+ '--long', 'dhcpservers']
107
+ Hash[output.split("\n\n").map { |dhcp_info|
108
+ dhcp = new
109
+ if_name = dhcp.from_dhcp_info(dhcp_info)
110
+ [if_name, dhcp]
111
+ }]
111
112
  end
112
113
 
113
114
  # Parses information about a DHCP server returned by VirtualBox.
@@ -115,18 +116,18 @@ class Dhcp
115
116
  # The parsed information is used to replace this network's specification.
116
117
  # @param [String] dhcp_info output from "VBoxManage list --long dhcpservers"
117
118
  # for one server
118
- # @return [VirtualBox::Dhcp] self, for easy call chaining
119
+ # @return [String] the name of the virtual network served by this DHCP server
119
120
  def from_dhcp_info(dhcp_info)
120
121
  info = Hash[dhcp_info.split("\n").map { |line|
121
122
  line.split(':', 2).map(&:strip)
122
123
  }]
123
124
 
124
- self.net_name = info['NetworkName']
125
125
  self.ip = info['IP']
126
126
  self.netmask = info['NetworkMask']
127
127
  self.start_ip = info['lowerIPAddress']
128
128
  self.end_ip = info['upperIPAddress']
129
- self
129
+
130
+ info['NetworkName']
130
131
  end
131
132
 
132
133
  # Converts an IP number into a string.
@@ -144,6 +145,8 @@ class Dhcp
144
145
  def self.ip_stob(ip_string)
145
146
  ip_string.split('.').map(&:to_i).pack('C*').unpack('N').first
146
147
  end
147
- end # class VirtualBox::Dhcp
148
+ end # class VirtualBox::Net::Dhcp
149
+
150
+ end # class VirtualBox::Net
148
151
 
149
152
  end # namespace VirtualBox
@@ -34,6 +34,21 @@ class Net
34
34
  # @return [String]
35
35
  attr_reader :mac
36
36
 
37
+ # The VirtualBox-powered DHCP server configured to serve this interface.
38
+ # @return [VirtualBox::Net::Dhcp, NilClass]
39
+ attr_accessor :dhcp
40
+
41
+ undef :dhcp=
42
+ def dhcp=(new_dhcp)
43
+ if new_dhcp.nil?
44
+ @dhcp = nil
45
+ elsif new_dhcp.kind_of? VirtualBox::Net::Dhcp
46
+ @dhcp = new_dhcp
47
+ else
48
+ @dhcp = VirtualBox::Net::Dhcp.new new_dhcp
49
+ end
50
+ end
51
+
37
52
  # Creates a virtual network specification rule based on the given attributes.
38
53
  #
39
54
  # The network is not automatically added to VirtualBox.
@@ -48,7 +63,7 @@ class Net
48
63
  # @return [Hash<Symbol, Object>] Ruby-friendly Hash that can be used to
49
64
  # re-create this virtual network specification
50
65
  def to_hash
51
- { :ip => ip, :netmask => netmask }
66
+ { :ip => ip, :netmask => netmask, :dhcp => dhcp && dhcp.to_hash }
52
67
  end
53
68
 
54
69
  # True if this virtual network has been added to VirtualBox.
@@ -68,12 +83,9 @@ class Net
68
83
  end
69
84
 
70
85
  # Create the network and pull its name.
71
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'hostonlyif',
72
- 'create']
73
- if result.status != 0
74
- raise 'Unexpected error code returned by VirtualBox'
75
- end
76
- unless match = /^interface\s+'(.*)'\s+.*created/i.match(result.output)
86
+ output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'hostonlyif',
87
+ 'create']
88
+ unless match = /^interface\s+'(.*)'\s+.*created/i.match(output)
77
89
  raise "VirtualBox output does not include interface name"
78
90
  end
79
91
  @if_name = match[1]
@@ -85,16 +97,16 @@ class Net
85
97
  @mac = network.mac
86
98
 
87
99
  if (ip && ip != network.ip) || (netmask && netmask != network.netmask)
88
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'hostonlyif',
100
+ VirtualBox.run_command! ['VBoxManage', '--nologo', 'hostonlyif',
89
101
  'ipconfig', if_name, '--ip', ip, '--netmask', netmask]
90
- if result.status != 0
91
- raise 'Unexpected error code returned by VirtualBox'
92
- end
93
102
  else
94
103
  self.ip = network.ip
95
104
  self.netmask = network.netmask
96
105
  end
97
106
 
107
+ # Register the DHCP server, if it's connected.
108
+ dhcp.add self if dhcp
109
+
98
110
  self
99
111
  end
100
112
 
@@ -103,22 +115,25 @@ class Net
103
115
  # @return [VirtualBox::Net] self, for easy call chaining
104
116
  def remove
105
117
  unless if_name.nil?
118
+ dhcp.remove self if dhcp
106
119
  VirtualBox.run_command ['VBoxManage', 'hostonlyif', 'remove', if_name]
107
120
  end
108
121
  self
109
122
  end
110
123
 
111
- # The DHCP servers added to with VirtualBox.
124
+ # The virtual networks added to VirtualBox.
112
125
  #
113
- # @return [Array<VirtualBox::Dhcp>] all the DHCP servers added to VirtualBox
126
+ # @return [Array<VirtualBox::Net>] all the DHCP servers added to VirtualBox
114
127
  def self.all
115
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list', '--long',
116
- 'hostonlyifs']
117
- if result.status != 0
118
- raise 'Unexpected error code returned by VirtualBox'
128
+ dhcps = VirtualBox::Net::Dhcp.all
129
+
130
+ output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
131
+ '--long', 'hostonlyifs']
132
+ output.split("\n\n").map do |net_info|
133
+ net = new.from_net_info(net_info)
134
+ net.dhcp = dhcps[net.name]
135
+ net
119
136
  end
120
- result.output.split("\n\n").
121
- map { |net_info| new.from_net_info(net_info) }
122
137
  end
123
138
 
124
139
  # Parses information about a DHCP server returned by VirtualBox.
@@ -139,6 +154,37 @@ class Net
139
154
  self.netmask = info['NetworkMask']
140
155
  self
141
156
  end
142
- end # class VirtualBox::Dhcp
157
+
158
+
159
+ # Information about the NICs attached to the computer.
160
+ #
161
+ # @return [Array<Hash<Symbol, Object>>] an array with one hash per NIC; hashes
162
+ # have the following keys:
163
+ # :name:: the NIC device's name (use when setting a Nic's net_name)
164
+ # :ip:: the IP address (compare against 0.0.0.0 to see if it's live)
165
+ # :mask:: the network mask used to figure out broadcasting
166
+ # :mac:: the NICs MAC address (format: "AB0123456789")
167
+ def self.host_nics
168
+ @host_nics ||= host_nics!
169
+ end
170
+
171
+ # Queries VirtualBox for the network interfaces on the computer.
172
+ #
173
+ # @return (see .host_nics)
174
+ def self.host_nics!
175
+ output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
176
+ '--long', 'hostifs']
177
+ output.split("\n\n").map do |nic_info|
178
+ info = Hash[nic_info.split("\n").map { |line|
179
+ line.split(':', 2).map(&:strip)
180
+ }]
181
+ {
182
+ :name => info['Name'], :ip => info['IPAddress'],
183
+ :mask => info['NetworkMask'],
184
+ :mac => info['HardwareAddress'].upcase.gsub(/[^0-9A-F]/, '')
185
+ }
186
+ end
187
+ end
188
+ end # class VirtualBox::Net
143
189
 
144
190
  end # namespace VirtualBox
@@ -8,10 +8,10 @@ module VirtualBox
8
8
  # The open-source edition of VirtualBox has some limitations, such as no
9
9
  # support for RDP and USB devices.
10
10
  def self.ose?
11
- unless version.edition
11
+ unless version[:edition]
12
12
  raise 'VirtualBox is not installed on this machine.'
13
13
  end
14
- version.edition == 'OSE'
14
+ version[:edition] == 'OSE'
15
15
  end
16
16
 
17
17
  # Version information about the VirtualBox package installed on this machine.
@@ -26,12 +26,12 @@ module VirtualBox
26
26
  return @version_info unless @version_info.nil?
27
27
 
28
28
  cmd_result = run_command ['VBoxManage', '--version']
29
- if cmd_result.status != 0
30
- @version_info = Hashie::Mash.new
29
+ if cmd_result[:status] != 0
30
+ @version_info = {}
31
31
  return @version_info
32
32
  end
33
33
 
34
- output = cmd_result.output.strip
34
+ output = cmd_result[:output].strip
35
35
 
36
36
  if revision_offset = output.rindex('r')
37
37
  revision = output[revision_offset + 1, output.length].to_i
@@ -47,8 +47,8 @@ module VirtualBox
47
47
  edition = ''
48
48
  end
49
49
 
50
- @version_info = Hashie::Mash.new :release => output, :svn => revision,
51
- :edition => edition
50
+ @version_info = { :release => output, :svn => revision,
51
+ :edition => edition }
52
52
  end
53
53
 
54
54
  # Removes the cached information on the VirtualBox package version.
@@ -272,13 +272,9 @@ class Board
272
272
  # @return [Hash<String, String> mapping from each VirtualBox OS type ID to its
273
273
  # description
274
274
  def self.list_os_types
275
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list',
276
- '--long', 'ostypes']
277
- if result.status != 0
278
- raise 'Unexpected error code returned by VirtualBox'
279
- end
280
-
281
- types = result.output.split("\n\n").map do |os_info|
275
+ output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
276
+ '--long', 'ostypes']
277
+ types = output.split("\n\n").map do |os_info|
282
278
  i = Hash[os_info.split("\n").map { |line| line.split(':').map(&:strip) }]
283
279
  [i['ID'], i['Description']]
284
280
  end
@@ -55,12 +55,9 @@ class Disk
55
55
  when :dvd
56
56
  'dvddrive'
57
57
  end
58
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'storageattach',
58
+ VirtualBox.run_command! ['VBoxManage', '--nologo', 'storageattach',
59
59
  vm.uid, '--storagectl', io_bus.name, '--port', port.to_s,
60
60
  '--device', device.to_s, '--type', media_arg, '--medium', file]
61
- if result.status != 0
62
- raise 'Unexpected error code returned by VirtualBox'
63
- end
64
61
  self
65
62
  end
66
63
 
@@ -90,13 +87,9 @@ class Disk
90
87
  memo = options[:memo] || 'Created with the virtual_box RubyGem'
91
88
  variant = options[:prealloc] ? 'Fixed' : 'Standard'
92
89
 
93
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'createhd',
90
+ VirtualBox.run_command! ['VBoxManage', '--nologo', 'createhd',
94
91
  '--filename', path, '--size', size_mb.to_s, '--format', format.to_s,
95
92
  '--variant', variant]
96
- if result.status != 0
97
- raise 'Unexpected error code returned by VirtualBox'
98
- end
99
-
100
93
  new :file => path, :format => format, :media => :disk
101
94
  end
102
95
 
@@ -209,11 +209,8 @@ class IoBus
209
209
  # be removed from
210
210
  # @return [VirtualBox::Vm::IoBus] self, for easy call chaining
211
211
  def remove_from(vm)
212
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'storagectl',
213
- vm.uuid, '--name', name, '--remove']
214
- if result.status != 0
215
- raise 'Unexpected error code returned by VirtualBox'
216
- end
212
+ VirtualBox.run_command! ['VBoxManage', '--nologo', 'storagectl', vm.uuid,
213
+ '--name', name, '--remove']
217
214
  self
218
215
  end
219
216
 
@@ -223,11 +220,8 @@ class IoBus
223
220
  # added to
224
221
  # @return [VirtualBox::Vm::IoBus] self, for easy call chaining
225
222
  def add_bus_to(vm)
226
- command = ['VBoxManage', '--nologo', 'storagectl', vm.uid].concat to_params
227
- result = VirtualBox.run_command command
228
- if result.status != 0
229
- raise 'Unexpected error code returned by VirtualBox'
230
- end
223
+ VirtualBox.run_command! ['VBoxManage', '--nologo', 'storagectl',
224
+ vm.uid].concat(to_params)
231
225
  self
232
226
  end
233
227
 
@@ -177,39 +177,6 @@ class Nic
177
177
  { :mode => mode, :chip => chip, :net_name => net_name, :mac => mac,
178
178
  :trace_file => trace_file }
179
179
  end
180
-
181
- # Information about the NICs attached to the computer.
182
- #
183
- # @return [Array<Hash<Symbol, Object>>] an array with one hash per NIC; hashes
184
- # have the following keys:
185
- # :id:: the inteface id (use when setting a Nic's net_id)
186
- # :ip:: the IP address (check for 0.0.0.0 to see if it's live)
187
- # :mask:: the netmask
188
- # :mac:: the NICs MAC address
189
- def self.host_nics
190
- @host_nics ||= get_host_nics
191
- end
192
-
193
- # Queries VirtualBox for the network interfaces on the computer.
194
- #
195
- # @return (see .host_nics)
196
- def self.get_host_nics
197
- result = VirtualBox.run_command ['VBoxManage', '--nologo', 'list',
198
- '--long', 'hostifs']
199
- if result.status != 0
200
- raise 'Unexpected error code returned by VirtualBox'
201
- end
202
-
203
- result.output.split("\n\n").map do |nic_info|
204
- i = Hash[nic_info.split("\n").map { |line|
205
- line.split(':', 2).map(&:strip)
206
- }]
207
- {
208
- :id => i['Name'], :ip => i['IPAddress'], :mask => i['NetworkMask'],
209
- :mac => i['HardwareAddress'].upcase.gsub(/[^0-9A-F]/, '')
210
- }
211
- end
212
- end
213
180
  end # class VirtualBox::Vm::Nic
214
181
 
215
182
  end # class VirtualBox::Vm