vmit 0.0.3 → 0.0.3.99
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/vmit +89 -13
- data/lib/vmit.rb +25 -18
- data/lib/vmit/autoyast.rb +81 -22
- data/lib/vmit/debian_preseed.rb +35 -1
- data/lib/vmit/install_media.rb +224 -0
- data/lib/vmit/kickstart.rb +44 -8
- data/lib/vmit/libvirt_vm.rb +274 -0
- data/lib/vmit/plugins/bootstrap.rb +33 -17
- data/lib/vmit/registry.rb +208 -0
- data/lib/vmit/unattended_install.rb +43 -0
- data/lib/vmit/utils.rb +51 -1
- data/lib/vmit/version.rb +1 -1
- data/lib/vmit/workspace.rb +181 -0
- data/test/data/registry.yml +5 -0
- data/test/data/registry/key1 +1 -0
- data/test/data/registry/key2 +1 -0
- data/test/data/registry/key4/key1 +1 -0
- data/test/install_media_test.rb +67 -0
- data/test/registry_test.rb +118 -0
- data/test/{bootstrap_test.rb → workspace_test.rb} +23 -19
- data/vmit.gemspec +4 -1
- metadata +46 -19
- data/bin/vmit-ifdown +0 -10
- data/bin/vmit-ifup +0 -18
- data/lib/vmit/bootstrap.rb +0 -273
- data/lib/vmit/virtual_machine.rb +0 -299
data/lib/vmit/virtual_machine.rb
DELETED
@@ -1,299 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# Copyright (C) 2013 Duncan Mac-Vicar P. <dmacvicar@suse.de>
|
3
|
-
#
|
4
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
5
|
-
# this software and associated documentation files (the "Software"), to deal in
|
6
|
-
# the Software without restriction, including without limitation the rights to
|
7
|
-
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
8
|
-
# the Software, and to permit persons to whom the Software is furnished to do so,
|
9
|
-
# subject to the following conditions:
|
10
|
-
#
|
11
|
-
# The above copyright notice and this permission notice shall be included in all
|
12
|
-
# copies or substantial portions of the Software.
|
13
|
-
#
|
14
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
16
|
-
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
17
|
-
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
18
|
-
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
19
|
-
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
-
#
|
21
|
-
require 'cheetah'
|
22
|
-
require 'drb'
|
23
|
-
require 'fileutils'
|
24
|
-
require 'stringio'
|
25
|
-
require 'yaml'
|
26
|
-
|
27
|
-
require 'vmit/utils'
|
28
|
-
|
29
|
-
module Vmit
|
30
|
-
|
31
|
-
class VirtualMachine
|
32
|
-
|
33
|
-
attr_accessor :work_dir
|
34
|
-
|
35
|
-
VM_DEFAULTS = {
|
36
|
-
:memory => '1G',
|
37
|
-
}
|
38
|
-
SWITCH = 'br0'
|
39
|
-
|
40
|
-
# Accessor to current options
|
41
|
-
def [](key)
|
42
|
-
@opts[key]
|
43
|
-
end
|
44
|
-
|
45
|
-
def config_file
|
46
|
-
File.join(work_dir, 'config.yml')
|
47
|
-
end
|
48
|
-
|
49
|
-
def initialize(work_dir)
|
50
|
-
@pidfile = PidFile.new(:piddir => work_dir, :pidfile => "vmit.pid")
|
51
|
-
@work_dir = work_dir
|
52
|
-
|
53
|
-
@opts = {}
|
54
|
-
@opts.merge!(VM_DEFAULTS)
|
55
|
-
|
56
|
-
if File.exist?(config_file)
|
57
|
-
@opts.merge!(YAML::load(File.open(config_file)))
|
58
|
-
end
|
59
|
-
|
60
|
-
# By default the following keys are useful to be
|
61
|
-
# generated if they don't exist and then use the
|
62
|
-
# same in the future UNLESS they are
|
63
|
-
# overriden with vmit run
|
64
|
-
if not @opts.has_key?(:mac_address)
|
65
|
-
@opts[:mac_address] = Vmit::Utils.random_mac_address
|
66
|
-
end
|
67
|
-
|
68
|
-
if not @opts.has_key?(:uuid)
|
69
|
-
@opts[:uuid] = File.read("/proc/sys/kernel/random/uuid").strip
|
70
|
-
end
|
71
|
-
|
72
|
-
@network = if @opts.has_key?(:network)
|
73
|
-
Network.create(@opts[:network])
|
74
|
-
else
|
75
|
-
Vmit.logger.info 'No network selected. Using default.'
|
76
|
-
Network.default
|
77
|
-
end
|
78
|
-
Vmit.logger.info "Network: #{@network}"
|
79
|
-
end
|
80
|
-
|
81
|
-
# @return [Array,<String>] sorted list of snapshots
|
82
|
-
def disk_images
|
83
|
-
Dir.glob(File.join(work_dir, '*.qcow2')).sort do |a,b|
|
84
|
-
File.ctime(a) <=> File.ctime(b)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Takes a disk snapshot
|
89
|
-
def disk_snapshot!
|
90
|
-
disk_image_shift!
|
91
|
-
end
|
92
|
-
|
93
|
-
def disk_image_init!(opts={})
|
94
|
-
disk_image_shift!(opts)
|
95
|
-
end
|
96
|
-
|
97
|
-
DISK_INIT_DEFAULTS = {:disk_size => '10G'}
|
98
|
-
|
99
|
-
# Shifts an image, adding a new one using the
|
100
|
-
# previous newest one as backing file
|
101
|
-
#
|
102
|
-
# @param [Hash] opts options for the disk shift
|
103
|
-
# @option opts [String] :disk_size Disk size. Only used for image creation
|
104
|
-
def disk_image_shift!(opts={})
|
105
|
-
runtime_opts = DISK_INIT_DEFAULTS.merge(opts)
|
106
|
-
|
107
|
-
file_name = File.join(work_dir, "sda-#{Time.now.to_i}.qcow2")
|
108
|
-
images = disk_images
|
109
|
-
|
110
|
-
file_name = 'base.qcow2' if images.size == 0
|
111
|
-
|
112
|
-
args = ['/usr/bin/qemu-img', 'create',
|
113
|
-
'-f', "qcow2"]
|
114
|
-
|
115
|
-
if not images.empty?
|
116
|
-
args << '-b'
|
117
|
-
args << images.last
|
118
|
-
end
|
119
|
-
args << file_name
|
120
|
-
if images.empty?
|
121
|
-
args << runtime_opts[:disk_size]
|
122
|
-
end
|
123
|
-
|
124
|
-
Vmit.logger.info "Shifted image. Current is '#{file_name}'."
|
125
|
-
Cheetah.run(*args)
|
126
|
-
end
|
127
|
-
|
128
|
-
# Rolls back to the previous snapshot
|
129
|
-
def disk_rollback!
|
130
|
-
images = disk_images
|
131
|
-
|
132
|
-
return if images.empty?
|
133
|
-
|
134
|
-
if images.size == 1
|
135
|
-
Vmit.logger.fatal "Only the base snapshot left!"
|
136
|
-
return
|
137
|
-
end
|
138
|
-
Vmit.logger.info "Removing #{images.last}"
|
139
|
-
FileUtils.rm(images.last)
|
140
|
-
end
|
141
|
-
|
142
|
-
# @returns [String] The latest COW snapshot
|
143
|
-
def current_image
|
144
|
-
curr = disk_images.last
|
145
|
-
raise "No hard disk image available" if curr.nil?
|
146
|
-
curr
|
147
|
-
end
|
148
|
-
|
149
|
-
def options
|
150
|
-
@opts
|
151
|
-
end
|
152
|
-
|
153
|
-
# @return [Hash] Config of the virtual machine
|
154
|
-
# This is all options plus the defaults
|
155
|
-
def config
|
156
|
-
VM_DEFAULTS.merge(@opts)
|
157
|
-
end
|
158
|
-
|
159
|
-
# @return [Hash] config that differs from default
|
160
|
-
# and therefore relevant to be persisted in config.yml
|
161
|
-
def relevant_config
|
162
|
-
config.diff(VM_DEFAULTS)
|
163
|
-
end
|
164
|
-
|
165
|
-
# Saves the configuration in config.yml
|
166
|
-
def save_config!
|
167
|
-
if not relevant_config.empty?
|
168
|
-
Vmit.logger.info "Writing config.yml..."
|
169
|
-
File.open(config_file, 'w') do |f|
|
170
|
-
f.write(relevant_config.to_yaml)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
def to_s
|
176
|
-
config.to_s
|
177
|
-
end
|
178
|
-
|
179
|
-
BINDIR = File.join(File.dirname(__FILE__), '../../bin')
|
180
|
-
|
181
|
-
# Starts the virtual machine
|
182
|
-
#
|
183
|
-
# @param [Hash] runtime_opts Runtime options
|
184
|
-
# @option runtime_opts [String] :cdrom CDROM image
|
185
|
-
# @option runtime_opts [String] :kernel Kernel image
|
186
|
-
# @option runtime_opts [String] :initrd initrd image
|
187
|
-
# @option runtime_opts [String] :append Kernel command line
|
188
|
-
# @option runtime_opts [String] :floppy Floppy (image or directory)
|
189
|
-
def run(runtime_opts)
|
190
|
-
Vmit.logger.info "Starting VM..."
|
191
|
-
# Don't overwrite @opts so that
|
192
|
-
# run can be called various times
|
193
|
-
opts = {}
|
194
|
-
opts.merge!(@opts)
|
195
|
-
opts.merge!(runtime_opts)
|
196
|
-
|
197
|
-
config.each do |k,v|
|
198
|
-
Vmit.logger.info " => #{k} : #{v}"
|
199
|
-
end
|
200
|
-
|
201
|
-
begin
|
202
|
-
# HACK, will be replaced by a better config system
|
203
|
-
use_virtio = ! File.exist?(File.join(work_dir, '.disable-virtio'))
|
204
|
-
|
205
|
-
ifup = File.expand_path(File.join(BINDIR, 'vmit-ifup'))
|
206
|
-
ifdown = File.expand_path(File.join(BINDIR, 'vmit-ifdown'))
|
207
|
-
|
208
|
-
args = ['/usr/bin/qemu-kvm', '-boot', 'c',
|
209
|
-
'-m', "#{opts[:memory]}",
|
210
|
-
'-pidfile', File.join(work_dir, 'qemu.pid')]
|
211
|
-
|
212
|
-
if use_virtio
|
213
|
-
args << '-drive'
|
214
|
-
args << "file=#{current_image},if=virtio"
|
215
|
-
|
216
|
-
args << '-netdev'
|
217
|
-
args << "type=tap,script=#{ifup},downscript=#{ifdown},id=vnet0"
|
218
|
-
args << '-device'
|
219
|
-
args << "virtio-net-pci,netdev=vnet0,mac=#{opts[:mac_address]}"
|
220
|
-
else
|
221
|
-
args << '-drive'
|
222
|
-
args << "file=#{current_image}"
|
223
|
-
|
224
|
-
args << '-net'
|
225
|
-
args << "nic,macaddr=#{opts[:mac_address]}"
|
226
|
-
args << '-net'
|
227
|
-
args << "tap,script=#{ifup},downscript=#{ifdown}"
|
228
|
-
end
|
229
|
-
|
230
|
-
# advanced options, mostly to be used by plugins
|
231
|
-
[:cdrom, :kernel, :initrd, :append].each do |key|
|
232
|
-
if opts.has_key?(key)
|
233
|
-
args << "-#{key}"
|
234
|
-
args << case opts[key]
|
235
|
-
# append is multple
|
236
|
-
when Array then opts[key].join(' ')
|
237
|
-
else opts[key]
|
238
|
-
end
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
if opts.has_key?(:floppy)
|
243
|
-
if File.directory?(opts[:floppy])
|
244
|
-
args << '-fda'
|
245
|
-
args << "fat:floppy:#{opts[:floppy]}"
|
246
|
-
else
|
247
|
-
Vmit.logger.warn "#{opts[:floppy]} : only directories supported"
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
# options that translate to
|
252
|
-
# -no-something if :something => false
|
253
|
-
[:reboot].each do |key|
|
254
|
-
if opts.has_key?(key)
|
255
|
-
# default is true
|
256
|
-
args << "-no-#{key}" if not opts[key]
|
257
|
-
end
|
258
|
-
end
|
259
|
-
|
260
|
-
unless ENV['DISABLE_UUID']
|
261
|
-
args << '-uuid'
|
262
|
-
args << "#{opts[:uuid]}"
|
263
|
-
end
|
264
|
-
|
265
|
-
DRb.start_service nil, self
|
266
|
-
ENV['VMIT_SERVER'] = DRb.uri
|
267
|
-
|
268
|
-
ENV['VMIT_SWITCH'] = SWITCH
|
269
|
-
Vmit.logger.debug "Vmit server listening at #{DRb.uri}"
|
270
|
-
|
271
|
-
@network.auto do
|
272
|
-
begin
|
273
|
-
Cheetah.run(*args)
|
274
|
-
ensure
|
275
|
-
FileUtils.rm_f File.join(work_dir, 'qemu.pid')
|
276
|
-
end
|
277
|
-
end
|
278
|
-
rescue PidFile::DuplicateProcessError => e
|
279
|
-
Vmit.logger.fatal "VM in '#{work_dir}'' is already running (#{e})"
|
280
|
-
raise
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
# Called by vmit-ifup
|
285
|
-
def ifup(device)
|
286
|
-
Vmit.logger.info " Bringing interface #{device} up"
|
287
|
-
Cheetah.run '/sbin/ifconfig', device, '0.0.0.0', 'up'
|
288
|
-
@network.connect_interface(device)
|
289
|
-
end
|
290
|
-
|
291
|
-
# Called by vmit-ifdown
|
292
|
-
def ifdown(device)
|
293
|
-
Vmit.logger.info " Bringing down interface #{device}"
|
294
|
-
Cheetah.run '/sbin/ifconfig', device, '0.0.0.0', 'down'
|
295
|
-
@network.disconnect_interface(device)
|
296
|
-
end
|
297
|
-
end
|
298
|
-
|
299
|
-
end
|