Class: VirtualBox::Vm

Inherits:
Object
  • Object
show all
Defined in:
lib/virtual_box/vm.rb,
lib/virtual_box/vm/nic.rb,
lib/virtual_box/vm/disk.rb,
lib/virtual_box/vm/board.rb,
lib/virtual_box/vm/io_bus.rb

Overview

VirtualBox virtual machine.

Defined Under Namespace

Classes: Board, Disk, IoBus, Nic

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Vm

Creates a new virtual machine specification based on the given attributes.

Parameters:

  • options (Hash<Symbol, Object>) (defaults to: {})

    ActiveRecord-style initial values for attributes; can be used together with Vm#to_hash to save and restore



89
90
91
92
93
94
95
# File 'lib/virtual_box/vm.rb', line 89

def initialize(options = {})
  self.board = {}
  self.io_buses = []
  self.nics = []
  self.gui = false
  options.each { |k, v| self.send :"#{k}=", v }
end

Instance Attribute Details

#boardVirtualBox::Vm::Board

The general VM configuration.



22
23
24
# File 'lib/virtual_box/vm.rb', line 22

def board
  @board
end

#guiBoolean

If true, the VM’s screen will be displayed in a GUI.

This is only intended for manual testing. Many continuous integration servers cannot display the VirtualBox GUI, so this attribute should not be set to true in test suites.

Returns:

  • (Boolean)


38
39
40
# File 'lib/virtual_box/vm.rb', line 38

def gui
  @gui
end

#io_busesArray<VirtualBox::Vm::IoBus>

The IO controllers (and disks) connected to the VM.

Returns:



26
27
28
# File 'lib/virtual_box/vm.rb', line 26

def io_buses
  @io_buses
end

#nameString

A user-friendly name for this virtual machine.

If not assigned, a unique name will be generated based on the VM’s UUID.

Returns:

  • (String)


18
19
20
# File 'lib/virtual_box/vm.rb', line 18

def name
  @name
end

#nicsArray<VirtualBox::Vm::Nic>

The network cards connected to this virtual machine.

Returns:



30
31
32
# File 'lib/virtual_box/vm.rb', line 30

def nics
  @nics
end

#uidString

The UUID used to register this virtual machine with VirtualBox.

The UUID is used to identify the VM in many VirtualBox commands. It should not be changed once the VM is registered. In fact, the UUID should not be manually assigned under normal use.

Returns:

  • (String)


11
12
13
# File 'lib/virtual_box/vm.rb', line 11

def uid
  @uid
end

Class Method Details

.parse_machine_readable(output) ⇒ Hash<String, Object>

Parses the output of the ‘VBoxManage showvminfo –machinereadable’ command.

Parameters:

  • output (String)

    the command output

Returns:

  • (Hash<String, Object>)

    a Hash whose keys are the strings on the left side of “=” on each line, and whose values are the strings on the right side



290
291
292
293
294
295
296
297
# File 'lib/virtual_box/vm.rb', line 290

def self.parse_machine_readable(output)
  Hash[output.split("\n").map { |line|
    key, value = *line.split('=', 2)
    key = key[1...-1] if key[0] == ?"  # Remove string quotes ("").
    value = value[1...-1] if value[0] == ?"  # Remove string quotes ("").
    [key, value]
  }]    
end

.registered_uidsArray<String>

The UUIDs of all VMs that are registered with VirtualBox.

Returns:

  • (Array<String>)

    UUIDs for VMs that VirtualBox is aware of



211
212
213
214
215
216
217
# File 'lib/virtual_box/vm.rb', line 211

def self.registered_uids
  output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list', 'vms']
  output.split("\n").map do |id_info|
    uid_offset = id_info.rindex(?{) + 1
    uid = id_info[uid_offset...-1]  # Exclude the closing }
  end
end

.started_uidsArray<String>

The UUIDs of all VirtualBox VMs that are started.

Returns:

  • (Array<String>)

    UUIDs for VMs that are running in VirtualBox



222
223
224
225
226
227
228
229
# File 'lib/virtual_box/vm.rb', line 222

def self.started_uids
  output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'list',
                                    'runningvms']
  output.split("\n").map do |id_info|
    uid_offset = id_info.rindex(?{) + 1
    uid = id_info[uid_offset...-1]  # Exclude the closing }
  end
end

Instance Method Details

#control(action) ⇒ VirtualBox::Vm

Controls a started virtual machine.

Parameters:

  • action (Symbol)

    the following actions are supported:

    :kill

    hard power-off (pulling the power cord from the machine)

    :power_button

    Power button press

    :nmi

    NMI (non-maskable interrupt)

Returns:

Raises:



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/virtual_box/vm.rb', line 169

def control(action)
  result = nil
  
  3.times do
    action_arg = case action
    when :kill
      'poweroff'
    when :power_button
      'acpipowerbutton'
    when :nmi
      'injectnmi'
    else
      action
    end

    result =  VirtualBox.run_command ['VBoxManage', '--nologo', 'controlvm',
                                      uid, action_arg]
    return self if result[:status] == 0
    
    # Perhaps the VM is already powered off?
    if action == :kill || action == :power_button
      return self unless live?
      sleep 0.1
    else
      break
    end
  end
  raise VirtualBox::Error, result
  
  self
end

#live?Boolean

True if this virtual machine is running inside VirtualBox.

Returns:

  • (Boolean)

    true if this VM is being simulated by VirtualBox



204
205
206
# File 'lib/virtual_box/vm.rb', line 204

def live?
  (uid && self.class.started_uids.include?(uid)) ? true : false
end

#pull_configVirtualBox::Vm

Updates this VM’s configuration to reflect the VirtualBox configuration.

Returns:



254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
# File 'lib/virtual_box/vm.rb', line 254

def pull_config
  output = VirtualBox.run_command! ['VBoxManage', '--nologo', 'showvminfo',
                                    '--machinereadable', uid]
  config = self.class.parse_machine_readable output
  
  self.name = config['name']
  self.uid = config['UUID']
  board.from_params config
  
  nic_count = config.keys.select { |key| /^nic\d+$/ =~ key }.max[3..-1].to_i
  1.upto nic_count do |index|
    if config["nic#{index}"] == 'none'
      nics[index - 1] = nil
    else
      nics[index - 1] ||= VirtualBox::Vm::Nic.new
      nics[index - 1].from_params config, index
    end
  end

  bus_count = 1 + (config.keys.select { |key|
    /^storagecontrollername\d+$/ =~ key
  }.max || "storagecontrollername-1")[21..-1].to_i
  0.upto bus_count - 1 do |index|
    io_buses[index] ||= VirtualBox::Vm::IoBus.new
    io_buses[index].from_params config, index
  end
  
  self
end

#push_configVirtualBox::Vm

Updates the configuration in VirtualBox to reflect this VM’s configuration.

Returns:



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/virtual_box/vm.rb', line 234

def push_config
  command = ['VBoxManage', 'modifyvm', uid]
  command.concat board.to_params
  nics.each_with_index do |nic, index|
    if nic.nil?
      command.push "--nic#{index + 1}", 'none'
    else
      command.concat nic.to_params(index + 1)
    end
  end
  VirtualBox.run_command! command
  
  io_buses.each { |bus| bus.add_to self }
   
  self
end

#registerVirtualBox::Vm

Registers this VM with VirtualBox.

Returns:



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/virtual_box/vm.rb', line 121

def register
  VirtualBox.run_command! ['VBoxManage', 'createvm', '--name', name,
                           '--uuid', uid, '--register']
  begin
    push_config
  rescue
    unregister
    raise
  end
  
  self
end

#registered?Boolean

True if this VM has been registered with VirtualBox.

Returns:

  • (Boolean)

    true for VMs that are already registered with VirtualBox



114
115
116
# File 'lib/virtual_box/vm.rb', line 114

def registered?
  self.class.registered_uids.include? uid
end

#startVirtualBox::Vm

Starts the virtual machine.

Returns:



145
146
147
148
149
150
151
# File 'lib/virtual_box/vm.rb', line 145

def start
  register unless registered?
  
  VirtualBox.run_command! ['VBoxManage', '--nologo', 'startvm', uid,
                           '--type', gui ? 'gui' : 'headless']
  self
end

#stopVirtualBox::Vm

Stops the virtual machine simulation.

This is equivalent to pulling the power cord from a physical machine.

Returns:



157
158
159
160
# File 'lib/virtual_box/vm.rb', line 157

def stop
  control :kill
  self
end

#to_hashHash<Symbol, Object>

Hash capturing this specification. Can be passed to Vm#new.

Returns:

  • (Hash<Symbol, Object>)

    Ruby-friendly Hash that can be used to re-create this virtual machine specification



101
102
103
104
105
106
107
108
109
# File 'lib/virtual_box/vm.rb', line 101

def to_hash
  {
    :name => name, :uid => uid, :gui => gui,
    :board => board.to_hash, :io_buses => io_buses.map(&:to_hash),
    :nics => nics.map.
         with_index { |nic, i| nic && nic.to_hash.merge!(:port => i) }.
         reject!(&:nil?)
  }
end

#unregisterVirtualBox::Vm

De-registers this VM from VirtualBox’s database.

Returns:



137
138
139
140
# File 'lib/virtual_box/vm.rb', line 137

def unregister
  VirtualBox.run_command ['VBoxManage', 'unregistervm', uid, '--delete']
  self
end