Class: QemuToolkit::VM

Inherits:
Object
  • Object
show all
Defined in:
lib/qemu-toolkit/vm.rb

Overview

Abstracts a virtual machine on a vm host. This class provides all sorts of methods that execute administration actions.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(backend) ⇒ VM

Returns a new instance of VM.



64
65
66
67
68
69
70
71
72
73
74
# File 'lib/qemu-toolkit/vm.rb', line 64

def initialize(backend)
  @disks = []
  @drives = []
  @nics = []
  @nets = []
  @cpus = 2
  @ram = 1024
  @backend = backend
  @vnc_display = nil
  @extra_args = []
end

Instance Attribute Details

#cpusObject

The number of cpus to configure, defaults to 2.



56
57
58
# File 'lib/qemu-toolkit/vm.rb', line 56

def cpus
  @cpus
end

#iscsi_targetObject

iSCSI target iqn and ip address to connect to



49
50
51
# File 'lib/qemu-toolkit/vm.rb', line 49

def iscsi_target
  @iscsi_target
end

#keyboard_layoutObject

Keyboard layout



62
63
64
# File 'lib/qemu-toolkit/vm.rb', line 62

def keyboard_layout
  @keyboard_layout
end

#nameObject

VM name



47
48
49
# File 'lib/qemu-toolkit/vm.rb', line 47

def name
  @name
end

#netsObject (readonly)

A list of network configuration statements that will be passed through to qemu.



54
55
56
# File 'lib/qemu-toolkit/vm.rb', line 54

def nets
  @nets
end

#nicsObject (readonly)

A list of network cards that will be connected to vnics on the host.



51
52
53
# File 'lib/qemu-toolkit/vm.rb', line 51

def nics
  @nics
end

#ramObject

Ram in megabytes



58
59
60
# File 'lib/qemu-toolkit/vm.rb', line 58

def ram
  @ram
end

#vnc_displayObject

VNC display port



60
61
62
# File 'lib/qemu-toolkit/vm.rb', line 60

def vnc_display
  @vnc_display
end

Class Method Details

.[](name, backend = nil) ⇒ Object

Access the definition of a single vm.



41
42
43
# File 'lib/qemu-toolkit/vm.rb', line 41

def [](name, backend=nil)
  all(backend).find { |vm| vm.name === name }
end

.all(backend = nil) ⇒ Object

Load all vm descriptions and provide an iterator for them.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/qemu-toolkit/vm.rb', line 16

def all(backend=nil)
  Enumerator.new do |yielder|
    libdir = Config.etc('lib')
    if ::File.directory? libdir
      $:.unshift libdir
    end
    
    Dir[Config.etc('*.rb')].each do |vm_file|
      # Load all virtual machines from the given file
      dsl = DSL::File.new
      dsl.add_toplevel_target :virtual_machine, lambda { |name| 
        VM.new(backend).tap { |vm| vm.name = name } }
        
      dsl.load_file(vm_file)

      # Yield them all in turn
      dsl.objects.each do |vm|
        yielder << vm
      end
    end
  end
end

Instance Method Details

#add_disk(path) ⇒ Object



79
80
81
# File 'lib/qemu-toolkit/vm.rb', line 79

def add_disk(path)
  @disks << path
end

#add_drive(parameters) ⇒ Object



76
77
78
# File 'lib/qemu-toolkit/vm.rb', line 76

def add_drive(parameters)
  @drives << parameters
end

#add_extra_arg(argument) ⇒ Object



88
89
90
# File 'lib/qemu-toolkit/vm.rb', line 88

def add_extra_arg(argument)
  @extra_args << argument
end

#add_net(type, parameters) ⇒ Object



85
86
87
# File 'lib/qemu-toolkit/vm.rb', line 85

def add_net(type, parameters)
  @nets << [type, parameters]
end

#add_nic(name, parameters) ⇒ Object



82
83
84
# File 'lib/qemu-toolkit/vm.rb', line 82

def add_nic(name, parameters)
  @nics << [name, parameters]
end

#command(opts = {}) ⇒ Object

Returns the command that is needed to run this virtual machine. Note that this also modifies system configuration and is not just a routine that returns a string.

Returns:

  • String command to run the machine



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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
# File 'lib/qemu-toolkit/vm.rb', line 110

def command opts={}
  cmd = []
  cmd << "-name #{name}"
  cmd << "-m #{ram}"
  cmd << "-daemonize"
  cmd << '-nographic'
  cmd << "-cpu qemu64"
  cmd << "-smp #{cpus}"
  cmd << "-no-hpet"
  cmd << "-enable-kvm"
  cmd << "-vga cirrus"
  cmd << "-parallel none"
  cmd << "-usb"
  cmd << '-usbdevice tablet'
  
  if keyboard_layout
    cmd << "-k #{keyboard_layout}"
  end

  # Add disks
  cmd += disk_options
  
  # Was an iso image given to boot from?
  if iso_path=opts[:bootiso]
    cmd << "-cdrom #{iso_path}"
    cmd << "-boot order=cd,once=d"
  else
    cmd << '-boot order=cd'
  end
  
  # Set paths for communication with vm
  cmd << "-pidfile #{pid_path}"
  
  cmd << socket_chardev(:monitor, monitor_path)
  cmd << "-monitor chardev:monitor"
  
  cmd << socket_chardev(:serial0, run_path('vm.console'))
  cmd << "-serial chardev:serial0"
  cmd << socket_chardev(:serial1, run_path('vm.ttyb'))
  cmd << "-serial chardev:serial1"
  
  # vnc socket
  cmd << "-vnc unix:#{run_path('vm.vnc')}"
  
  # If vnc_display is set, allow configuring a TCP based VNC port: 
  if vnc_display
    cmd << "-vnc #{vnc_display}"
  end
  
  # networking: nic
  vlan = 0
  # Look up all existing vnics for this virtual machine
  vnics = Vnic.for_prefix(name, @backend)
  
  nics.each do |nic_name, parameters|
    # All vnics that travel via the given interface (:via)
    vnics_for_interface = vnics[parameters[:via]] || []
    
    # Get a vnic that travels via the given interface.
    vnic = vnics_for_interface.shift ||
      Vnic.create(name, parameters[:via], @backend)
    
    cmd << "-net vnic,"+
      parameter_list(
        vlan: vlan, name: nic_name, 
        ifname: vnic.vnic_name, 
        macaddr: parameters[:macaddr])
    cmd << "-net nic,"+
      parameter_list(
        vlan: vlan, name: nic_name, 
        model: parameters[:model] || 'virtio', 
        macaddr: parameters[:macaddr])

    vlan += 1
  end
  
  # networking: net
  nets.each do |type, parameters|
    cmd << "-net #{type},"+
      parameter_list(parameters)
  end
  
  # Extra arguments
  cmd += @extra_args
  
  return cmd
end

#connect(socket) ⇒ Object

Connects the current terminal to the given socket. Available sockets include :monitor, :vnc, :console, :ttyb.



242
243
244
245
246
247
# File 'lib/qemu-toolkit/vm.rb', line 242

def connect(socket)
  socket_path = run_path("vm.#{socket}")
  cmd = "socat stdio unix-connect:#{socket_path}"
  
  exec cmd
end

#disk_optionsObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/qemu-toolkit/vm.rb', line 197

def disk_options
  cmd = []
  
  if @disks.empty? && !iscsi_target && @drives.empty?
    raise "No disks defined, can't run." 
  end
  
  disk_index = 0
  if iscsi_target
    target = produce_target(*iscsi_target)
    target.ensure_exists
    
    target.disks.each do |device|
      params = {
          file: device, 
          if: 'virtio', 
          index: disk_index, 
          media: 'disk', 
          cache: 'none'
      }
      params[:boot] = 'on' if disk_index == 0
      cmd << "-drive " + parameter_list(params)
      
      disk_index += 1
    end
  end
  
  @disks.each do |path|
    cmd << "-drive file=#{path},if=virtio,index=#{disk_index},"+
      "media=disk,boot=on"
    disk_index += 1
  end
  
  @drives.each do |drive_options|
    cmd << "-drive " + 
      parameter_list(drive_options.merge(index: disk_index))
    disk_index += 1
  end
  
  return cmd
end

#killObject

Kills the vm the hard way.



251
252
253
# File 'lib/qemu-toolkit/vm.rb', line 251

def kill
  run_cmd "kill #{pid}"
end

#pidObject

Attempts to read and return the pid of the running VM process.



288
289
290
# File 'lib/qemu-toolkit/vm.rb', line 288

def pid
  Integer(File.read(pid_path).lines.first.chomp)
end

#produce_target(host, port) ⇒ Object

Returns an ISCSITarget for host and port.



263
264
265
# File 'lib/qemu-toolkit/vm.rb', line 263

def produce_target(host, port)
  ISCSITarget.new(host, port, @backend)
end

#running?Boolean

Returns true if the virtual machine seems to be currently running.

Returns:

  • (Boolean)


269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
# File 'lib/qemu-toolkit/vm.rb', line 269

def running?
  if File.exist?(pid_path) 
    # Prod the process using kill. This will not actually kill the
    # process!
    begin
      Process.kill(0, pid)
    rescue Errno::ESRCH
      # When this point is reached, the process doesn't exist. 
      return false
    end

    return true
  end
  
  return false
end

#shutdownObject

Sends a shutdown command via the monitor socket of the virtual machine.



257
258
259
# File 'lib/qemu-toolkit/vm.rb', line 257

def shutdown
  monitor_cmd 'system_powerdown'
end

#start(dryrun, opts = {}) ⇒ Object

Runs the VM using qemu.



93
94
95
96
97
98
99
100
101
102
# File 'lib/qemu-toolkit/vm.rb', line 93

def start(dryrun, opts={})
  if dryrun
    puts command(opts) 
  else
    # Make sure var/run/qemu-toolkit/VMNAME exists.
    FileUtils.mkdir_p run_path
    
    @backend.qemu("vm<#{name}>", command(opts))
  end
end