kitchen-qemu 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kitchen/driver/qemu.rb +100 -27
- data/lib/kitchen/driver/qemu_version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a4524644c5f8decc92563913b719bed878cb8481
|
4
|
+
data.tar.gz: 48e60f381449ce1e9b9b123d7874d24b4d5b67c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82fe2072030831acf5700f87178a8a889485dc949c8254540306dd9e290be1b657fd609688765ef85e548bfde5f1c9fb47a9df7cd26686035dac62639dccc244
|
7
|
+
data.tar.gz: 43b5e78b3dfb446405e0f2d809b33db8281fc44636851782c421d82321bbbbef755ea60149d0e4007e14c78ef4ea1c3293d3ddd961891a96622a7163a4251797
|
data/lib/kitchen/driver/qemu.rb
CHANGED
@@ -33,13 +33,14 @@ module Kitchen
|
|
33
33
|
kitchen_driver_api_version 2
|
34
34
|
plugin_version Kitchen::Driver::QEMU_VERSION
|
35
35
|
|
36
|
-
default_config :arch,
|
37
|
-
default_config :username,
|
38
|
-
default_config :password,
|
39
|
-
default_config :port,
|
40
|
-
default_config :display,
|
41
|
-
default_config :memory,
|
42
|
-
default_config :nic_model,
|
36
|
+
default_config :arch, 'x86_64'
|
37
|
+
default_config :username, 'kitchen'
|
38
|
+
default_config :password, 'kitchen'
|
39
|
+
default_config :port, 2222
|
40
|
+
default_config :display, 'none'
|
41
|
+
default_config :memory, '512'
|
42
|
+
default_config :nic_model, 'virtio'
|
43
|
+
default_config :hostshares, []
|
43
44
|
|
44
45
|
required_config :image do |_attr, value, _subject|
|
45
46
|
raise UserError, 'Must specify image file' unless value
|
@@ -60,6 +61,52 @@ module Kitchen
|
|
60
61
|
config[:binary] = @@ARCHBINARY[config[:arch]] or
|
61
62
|
raise UserError, "Unknown architecture '#{config[:arch]}'"
|
62
63
|
end
|
64
|
+
|
65
|
+
# kitchen-vagrant compatibility
|
66
|
+
config[:hostname] = config[:vm_hostname] if config[:hostname].nil?
|
67
|
+
|
68
|
+
acpi_poweroff = false
|
69
|
+
if config[:image].kind_of?(String)
|
70
|
+
config[:image] = [{
|
71
|
+
:file => config[:image],
|
72
|
+
:readonly => true,
|
73
|
+
:snapshot => true,
|
74
|
+
}]
|
75
|
+
else
|
76
|
+
raise UserError, "Invalid image entry for #{instance.to_str}" unless
|
77
|
+
config[:image].kind_of?(Array)
|
78
|
+
config[:image].each do |image|
|
79
|
+
raise UserError, "Invalid image entry for #{instance.to_str}" unless
|
80
|
+
image.kind_of?(Hash) && image[:file]
|
81
|
+
image[:snapshot] = true if image[:snapshot].nil?
|
82
|
+
image[:readonly] = image[:snapshot] if image[:readonly].nil?
|
83
|
+
acpi_poweroff = true unless image[:readonly]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
config[:acpi_poweroff] = acpi_poweroff if config[:acpi_poweroff].nil?
|
87
|
+
|
88
|
+
raise UserError, "Invalid share entry for #{instance.to_str}" unless
|
89
|
+
config[:hostshares].kind_of?(Array)
|
90
|
+
# kitchen-vagrant compatibility
|
91
|
+
if config[:hostshares].empty? && config[:synced_folders].kind_of?(Array)
|
92
|
+
config[:synced_folders].each do |folder|
|
93
|
+
if !folder[0].kind_of?(String) || !folder[1].kind_of?(String)
|
94
|
+
config[:hostshares].clear
|
95
|
+
break
|
96
|
+
end
|
97
|
+
config[:hostshares].push({ :path => folder[0], :mountpoint => folder[1] })
|
98
|
+
end
|
99
|
+
else
|
100
|
+
config[:hostshares].each do |share|
|
101
|
+
raise UserError, "Invalid share entry for #{instance.to_str}" unless
|
102
|
+
share.kind_of?(Hash) && share[:path]
|
103
|
+
raise UserError, "No mountpoint defined for share '#{share[:path]}' of #{instance.to_str}" unless
|
104
|
+
share[:mountpoint]
|
105
|
+
raise UserError, "Invalid mount options for share '#{share[:path]}' of #{instance.to_str}" unless
|
106
|
+
share[:mount_options].nil? || share[:mount_options].kind_of?(Array)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
63
110
|
config[:vga] = 'qxl' if config[:spice] && !config[:vga]
|
64
111
|
self
|
65
112
|
end
|
@@ -69,7 +116,7 @@ module Kitchen
|
|
69
116
|
# @param state [Hash] mutable instance and driver state
|
70
117
|
# @raise [ActionFailed] if the action could not be completed
|
71
118
|
def create(state)
|
72
|
-
monitor =
|
119
|
+
monitor = monitor_path
|
73
120
|
if File.exist?(monitor)
|
74
121
|
begin
|
75
122
|
mon = UNIXSocket.new(monitor)
|
@@ -84,28 +131,24 @@ module Kitchen
|
|
84
131
|
|
85
132
|
create_privkey or raise ActionFailed, "Unable to create file '#{privkey_path}'"
|
86
133
|
|
87
|
-
fqdn = config[:
|
134
|
+
fqdn = config[:hostname] || instance.name
|
88
135
|
hostname = fqdn.match(/^([^.]+)/)[0]
|
89
136
|
|
90
|
-
state[:hostname]
|
91
|
-
state[:port]
|
92
|
-
state[:username]
|
93
|
-
state[:password]
|
137
|
+
state[:hostname] = 'localhost'
|
138
|
+
state[:port] = config[:port]
|
139
|
+
state[:username] = config[:username]
|
140
|
+
state[:password] = config[:password]
|
141
|
+
state[:acpi_poweroff] = config[:acpi_poweroff]
|
94
142
|
|
95
143
|
cmd = [
|
96
144
|
config[:binary], '-daemonize',
|
97
145
|
'-display', config[:display].to_s,
|
98
146
|
'-chardev', "socket,id=mon-qmp,path=#{monitor},server,nowait",
|
99
147
|
'-mon', 'chardev=mon-qmp,mode=control,default',
|
100
|
-
'-
|
101
|
-
'-mon', 'chardev=mon-rdl,mode=readline',
|
148
|
+
'-serial', "mon:unix:path=#{serial_path},server,nowait",
|
102
149
|
'-m', config[:memory].to_s,
|
103
150
|
'-net', "nic,model=#{config[:nic_model]}",
|
104
151
|
'-net', "user,net=192.168.1.0/24,hostname=#{hostname},hostfwd=tcp::#{state[:port]}-:22",
|
105
|
-
'-device', 'virtio-scsi-pci,id=scsi',
|
106
|
-
'-device', 'scsi-hd,drive=root',
|
107
|
-
'-drive', "if=none,id=root,readonly,file=#{config[:image]}",
|
108
|
-
'-snapshot',
|
109
152
|
]
|
110
153
|
|
111
154
|
kvm = config[:kvm]
|
@@ -131,6 +174,25 @@ module Kitchen
|
|
131
174
|
cmd.push('-spice', config[:spice].to_s) if config[:spice]
|
132
175
|
cmd.push('-vnc', config[:vnc].to_s) if config[:vnc]
|
133
176
|
|
177
|
+
cmd.push('-device', 'virtio-scsi-pci,id=scsi')
|
178
|
+
config[:image].each_with_index do |image, i|
|
179
|
+
drive = ['if=none', "id=drive#{i}"]
|
180
|
+
drive.push('readonly') if image[:readonly]
|
181
|
+
drive.push('snapshot=on') if image[:snapshot]
|
182
|
+
drive.push("file=#{image[:file]}")
|
183
|
+
cmd.push('-device', "scsi-hd,drive=drive#{i}",
|
184
|
+
'-drive', drive.join(','))
|
185
|
+
end
|
186
|
+
|
187
|
+
config[:hostshares].each_with_index do |share, i|
|
188
|
+
path = share[:path]
|
189
|
+
path = "#{config[:kitchen_root]}/#{path}" unless path[0] == '/'
|
190
|
+
raise ActionFailed, "Share path '#{path}' not a directory" unless
|
191
|
+
::File.directory?(path)
|
192
|
+
cmd.push('-fsdev', "local,id=fsdev#{i},security_model=none,path=#{path}",
|
193
|
+
'-device', "virtio-9p-pci,fsdev=fsdev#{i},mount_tag=path#{i}")
|
194
|
+
end
|
195
|
+
|
134
196
|
info 'Spawning QEMU..'
|
135
197
|
error = nil
|
136
198
|
Open3.popen3({ 'QEMU_AUDIO_DRV' => 'none' }, *cmd) do |_, _, err, thr|
|
@@ -155,6 +217,11 @@ module Kitchen
|
|
155
217
|
conn.execute("sudo sh -c 'echo 127.0.0.1 #{names} >> /etc/hosts; hostnamectl set-hostname #{hostname} || hostname #{hostname} || true' 2>/dev/null")
|
156
218
|
conn.execute('install -dm700 "$HOME/.ssh"')
|
157
219
|
conn.execute("echo '#{@@PUBKEY}' > \"$HOME/.ssh/authorized_keys\"")
|
220
|
+
config[:hostshares].each_with_index do |share, i|
|
221
|
+
options = share[:mount_options] ?
|
222
|
+
share[:mount_options].join(',') : 'cache=none,access=any,version=9p2000.L'
|
223
|
+
conn.execute("sudo sh -c 'install -dm755 \"#{share[:mountpoint]}\" && mount -t 9p -o trans=virtio,#{options} path#{i} \"#{share[:mountpoint]}\"'")
|
224
|
+
end
|
158
225
|
conn.close
|
159
226
|
state[:ssh_key] = privkey_path
|
160
227
|
end
|
@@ -164,16 +231,22 @@ module Kitchen
|
|
164
231
|
# @param state [Hash] mutable instance state
|
165
232
|
# @raise [ActionFailed] if the action could not be completed
|
166
233
|
def destroy(state)
|
167
|
-
monitor =
|
234
|
+
monitor = monitor_path
|
168
235
|
return unless File.exist?(monitor)
|
169
236
|
|
170
237
|
instance.transport.connection(state).close
|
171
238
|
|
172
239
|
begin
|
173
240
|
mon = QMPClient.new(UNIXSocket.new(monitor), 2)
|
174
|
-
|
175
|
-
|
176
|
-
|
241
|
+
if state[:acpi_poweroff]
|
242
|
+
info 'Sending ACPI poweroff..'
|
243
|
+
mon.execute('system_powerdown')
|
244
|
+
mon.wait_for_eof(30)
|
245
|
+
else
|
246
|
+
info 'Quitting QEMU..'
|
247
|
+
mon.execute('quit')
|
248
|
+
mon.wait_for_eof(5)
|
249
|
+
end
|
177
250
|
mon.close
|
178
251
|
rescue Errno::ECONNREFUSED
|
179
252
|
info 'Connection to monitor refused. Assuming QEMU already quit.'
|
@@ -231,11 +304,11 @@ tY4IM9IaSC2LuPFVc0Kx6TwObdeQScOokIxL3HfayfLKieTLC+w2
|
|
231
304
|
File.join(config[:kitchen_root], '.kitchen', 'kitchen-qemu.key')
|
232
305
|
end
|
233
306
|
|
234
|
-
def
|
307
|
+
def monitor_path
|
235
308
|
File.join(config[:kitchen_root], '.kitchen', "#{instance.name}.qmp")
|
236
309
|
end
|
237
310
|
|
238
|
-
def
|
311
|
+
def serial_path
|
239
312
|
File.join(config[:kitchen_root], '.kitchen', "#{instance.name}.mon")
|
240
313
|
end
|
241
314
|
|
@@ -247,12 +320,12 @@ tY4IM9IaSC2LuPFVc0Kx6TwObdeQScOokIxL3HfayfLKieTLC+w2
|
|
247
320
|
|
248
321
|
def cleanup!
|
249
322
|
begin
|
250
|
-
File.delete(
|
323
|
+
File.delete(monitor_path)
|
251
324
|
rescue Errno::ENOENT
|
252
325
|
# do nothing
|
253
326
|
end
|
254
327
|
begin
|
255
|
-
File.delete(
|
328
|
+
File.delete(serial_path)
|
256
329
|
rescue Errno::ENOENT
|
257
330
|
# do nothing
|
258
331
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kitchen-qemu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emil Renner Berthing
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-03-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-kitchen
|