kitchen-qemu 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/kitchen/driver/qemu.rb +250 -0
- data/lib/kitchen/driver/qemu_version.rb +22 -0
- data/lib/kitchen/driver/qmpclient.rb +98 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b271e7a403bb173917ba2844c8438c3090fc17fd
|
4
|
+
data.tar.gz: 060fadc6ce149e8aa44fcf92f6e33511001d3f1e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5c211c9f3e8919de20afbea050d1ee7aff057917a5f9ace7f1f8b2eeb0441c88af10a60a3f3996f45b7a51026e3739fad5f0ab5886e46ca26fb85e0ca8b57b89
|
7
|
+
data.tar.gz: 85aff6d2b0546d56bafef287b48570fd06445518b2cd7bcc14407383729cc32cc4c5de9d95341e453415168f3e8135e914c5c09436bd264d020e8be455e21c0d
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# This file is part of kitchen-qemu.
|
2
|
+
# Copyright 2016 Emil Renner Berthing <esmil@esmil.dk>
|
3
|
+
#
|
4
|
+
# kitchen-qemu is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# kitchen-qemu is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with kitchen-qemu. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'open3'
|
18
|
+
|
19
|
+
require 'kitchen'
|
20
|
+
require 'kitchen/driver/qemu_version'
|
21
|
+
require 'kitchen/driver/qmpclient'
|
22
|
+
|
23
|
+
module Kitchen
|
24
|
+
|
25
|
+
module Driver
|
26
|
+
|
27
|
+
# QEMU driver for Kitchen.
|
28
|
+
#
|
29
|
+
# @author Emil Renner Berthing <esmil@esmil.dk>
|
30
|
+
class Qemu < Kitchen::Driver::Base
|
31
|
+
include ShellOut
|
32
|
+
|
33
|
+
kitchen_driver_api_version 2
|
34
|
+
plugin_version Kitchen::Driver::QEMU_VERSION
|
35
|
+
|
36
|
+
default_config :arch, 'x86_64'
|
37
|
+
default_config :username, 'kitchen'
|
38
|
+
default_config :password, 'kitchen'
|
39
|
+
default_config :port, 2222
|
40
|
+
default_config :memory, '512'
|
41
|
+
default_config :nic_model, 'virtio'
|
42
|
+
|
43
|
+
required_config :image do |attr, value, subject|
|
44
|
+
raise UserError, 'Must specify image file' unless value
|
45
|
+
end
|
46
|
+
|
47
|
+
# A lifecycle method that should be invoked when the object is about ready
|
48
|
+
# to be used. A reference to an Instance is required as configuration
|
49
|
+
# dependant data may be access through an Instance. This also acts as a
|
50
|
+
# hook point where the object may wish to perform other last minute
|
51
|
+
# checks, validations, or configuration expansions.
|
52
|
+
#
|
53
|
+
# @param instance [Instance] an associated instance
|
54
|
+
# @return [self] itself, for use in chaining
|
55
|
+
# @raise [ClientError] if instance parameter is nil
|
56
|
+
def finalize_config!(instance)
|
57
|
+
super
|
58
|
+
if not config[:binary]
|
59
|
+
config[:binary] = @@archbinary[config[:arch]] or
|
60
|
+
raise UserError, "Unknown architecture '#{config[:arch]}'"
|
61
|
+
end
|
62
|
+
if not config[:vnc] and not config[:spice]
|
63
|
+
config[:spice] = "addr=#{spice_path},unix"
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Creates a QEMU instance.
|
69
|
+
#
|
70
|
+
# @param state [Hash] mutable instance and driver state
|
71
|
+
# @raise [ActionFailed] if the action could not be completed
|
72
|
+
def create(state)
|
73
|
+
monitor = monitor_path
|
74
|
+
if File.exist?(monitor)
|
75
|
+
begin
|
76
|
+
mon = UNIXSocket.new(monitor)
|
77
|
+
rescue Errno::ECONNREFUSED
|
78
|
+
File.delete(spice_path)
|
79
|
+
File.delete(monitor)
|
80
|
+
else
|
81
|
+
mon.close
|
82
|
+
raise ActionFailed, "QEMU instance #{instance.to_str} already running."
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
create_privkey or raise ActionFailed, "Unable to create file '#{privkey_path}'"
|
87
|
+
|
88
|
+
fqdn = config[:vm_hostname] || instance.name
|
89
|
+
hostname = fqdn.match(/^([^.]+)/)[0]
|
90
|
+
|
91
|
+
state[:hostname] = 'localhost'
|
92
|
+
state[:port] = config[:port]
|
93
|
+
state[:username] = config[:username]
|
94
|
+
state[:password] = config[:password]
|
95
|
+
|
96
|
+
cmd = [
|
97
|
+
config[:binary], '-daemonize',
|
98
|
+
'-chardev', "socket,id=monitor,path=#{monitor},server,nowait",
|
99
|
+
'-mon', 'chardev=monitor,mode=control,default',
|
100
|
+
'-m', config[:memory],
|
101
|
+
'-net', "nic,model=#{config[:nic_model]}",
|
102
|
+
'-net', "user,net=192.168.1.0/24,hostname=#{hostname},hostfwd=tcp::#{state[:port]}-:22",
|
103
|
+
'-device', 'virtio-scsi-pci,id=scsi',
|
104
|
+
'-device', 'scsi-hd,drive=root',
|
105
|
+
'-drive', "if=none,id=root,readonly,file=#{config[:image]}",
|
106
|
+
'-snapshot',
|
107
|
+
]
|
108
|
+
|
109
|
+
kvm = config[:kvm]
|
110
|
+
if kvm.nil? # autodetect
|
111
|
+
begin
|
112
|
+
kvm = File.stat('/dev/kvm')
|
113
|
+
rescue Errno::ENOENT
|
114
|
+
kvm = false
|
115
|
+
info 'KVM device /dev/kvm doesn\'t exist. Maybe the module is not loaded.'
|
116
|
+
else
|
117
|
+
kvm = kvm.writable? && kvm.readable?
|
118
|
+
info 'KVM device /dev/kvm not read/writeable. Maybe add your user to the kvm group.' unless kvm
|
119
|
+
end
|
120
|
+
end
|
121
|
+
if kvm
|
122
|
+
info 'KVM enabled.'
|
123
|
+
cmd.push('-enable-kvm', '-cpu', 'host')
|
124
|
+
else
|
125
|
+
info 'KVM disabled'
|
126
|
+
end
|
127
|
+
|
128
|
+
if config[:spice]
|
129
|
+
cmd.push('-vga', 'qxl', '-spice', config[:spice])
|
130
|
+
elsif config[:vnc]
|
131
|
+
cmd.push('-vnc', config[:vnc])
|
132
|
+
end
|
133
|
+
|
134
|
+
info 'Spawning QEMU..'
|
135
|
+
error = nil
|
136
|
+
Open3.popen3({ 'QEMU_AUDIO_DRV' => 'none' }, *cmd, :unsetenv_others=>true ) do |_, _, err, thr|
|
137
|
+
if not thr.value.success?
|
138
|
+
error = err.read.strip
|
139
|
+
end
|
140
|
+
end
|
141
|
+
raise ActionFailed, error if error
|
142
|
+
|
143
|
+
if hostname == fqdn
|
144
|
+
names = fqdn
|
145
|
+
else
|
146
|
+
names = "#{fqdn} #{hostname}"
|
147
|
+
end
|
148
|
+
|
149
|
+
info 'Waiting for SSH..'
|
150
|
+
conn = instance.transport.connection(state)
|
151
|
+
conn.wait_until_ready
|
152
|
+
conn.execute("sudo sh -c 'echo 127.0.0.1 #{names} >> /etc/hosts' 2>/dev/null")
|
153
|
+
conn.execute('install -dm700 "$HOME/.ssh"')
|
154
|
+
conn.execute("echo '#{@@pubkey}' > \"$HOME/.ssh/authorized_keys\"")
|
155
|
+
conn.close
|
156
|
+
state[:ssh_key] = privkey_path
|
157
|
+
end
|
158
|
+
|
159
|
+
# Destroys an instance.
|
160
|
+
#
|
161
|
+
# @param state [Hash] mutable instance state
|
162
|
+
# @raise [ActionFailed] if the action could not be completed
|
163
|
+
def destroy(state)
|
164
|
+
monitor = monitor_path
|
165
|
+
return unless File.exist?(monitor)
|
166
|
+
|
167
|
+
instance.transport.connection(state).close
|
168
|
+
|
169
|
+
begin
|
170
|
+
mon = QMPClient.new(UNIXSocket.new(monitor), 2)
|
171
|
+
info 'Quitting QEMU..'
|
172
|
+
mon.execute('quit')
|
173
|
+
mon.wait_for_eof(5)
|
174
|
+
mon.close
|
175
|
+
rescue Errno::ECONNREFUSED
|
176
|
+
info 'Connection to monitor refused. Assuming QEMU already quit.'
|
177
|
+
rescue QMPClient::Timeout
|
178
|
+
mon.close
|
179
|
+
raise ActionFailed, "QEMU instance #{instance.to_str} is unresponsive"
|
180
|
+
end
|
181
|
+
|
182
|
+
File.delete(spice_path)
|
183
|
+
File.delete(monitor)
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
@@privkey = %{-----BEGIN RSA PRIVATE KEY-----
|
189
|
+
MIIEowIBAAKCAQEAyG6ASL3bWS67rsA5LDvKnfdCBagK61B5LIr+NvdjK3oRKhCq
|
190
|
+
qRs7aNSPOqMu2NbKot4/BtD0hWipF7CAsMqK+241coMUwxRTlqvoe/L7xZ24Ktaj
|
191
|
+
rm1kk/xNUGP5vWyK8sYfYnDUuLSypRaZ/ZfWuKZgQLDdOw2FWqHFVLoJDsXgsa/y
|
192
|
+
i1F3b5l4+F36vN+7pbS9YIMmjdmKng8J/hsTPETZudayEWuAy+mNaz4M7ZtxS/gP
|
193
|
+
TIJcxIxC27PLOhA1XsABunaNR8Ety6ghsZX2YEYnnpcyB5Mmqi0VOJs6m5IZn2kf
|
194
|
+
1P3x0KaUQ2Fm9wqAkgYpSccWH4yytVQ8lzlpBwIDAQABAoIBAH3s5w5Msj5C3Un6
|
195
|
+
nTEMU82RZlqVbF7RjYANx5ATN6w+IgCSvhZG9Ll1KpPFqI41zNQs295Vc/tJeUtX
|
196
|
+
6lKovk8fu9a5QlcaMzYrxYHydHqBEA9iES5qrlFHp++FEIgRZO8IyPkZOJzfconE
|
197
|
+
PHWWayJR7ZFXTXdnlEwP7SHBTCWJ0PIRoNQEkOMX+tsbbS5w5kIpWNUx9YJzLezt
|
198
|
+
YSy/9RLLuhioq7ElBM6rgb3jn37W9+yhSsD+9nbQCePS5BoEzrwxZ298pyYpX/2B
|
199
|
+
DPKJ5RegClKJqGgBYjcit0n5fyzis01tzPRKGl/VjOFEEcKvoi3TLodORGCQks8q
|
200
|
+
5TVkt+ECgYEA+wRQOq8zcsBq+5+4PjML9KHxtkcug2gfD+5PtY7nvM9ndxmk3DC/
|
201
|
+
87mE30LlXjEHt1zp5XWWsoVadjedZ9b+H5RUS2b3qxgqR6v2GAgGkdz3zf9ZgsO7
|
202
|
+
lqHY6pnmKjXGRZ3w5fneVPeI0fQ3l77WpUUOAM9PGVKjpnLKBBKx5DcCgYEAzGkc
|
203
|
+
IT8SBPXBd+yYFNNnLjj6jIBnRC/giiRoepO9Ojf2ZGUzFT1sLYG0//gtn/rdru57
|
204
|
+
Y0FOwsA6AhYXJa+5oEBdARWdGJIBz9EQbuVidK/wj7wAqvIvOeGp1TnzMxAz1+8v
|
205
|
+
CGVw22nxqtCguPzeKEZmzm3kagJuApfxiEp42bECgYEAw8ZHdJ20uKkOR5X4srpJ
|
206
|
+
dtDfnlTCGEcbAufRTz9XylDQ13kutXVoIITu9tpL3jzLUd2rpwUhNbcAKPeTUqvB
|
207
|
+
o4uievSh8dV1FFUwKOoJhbYbp5SikXRrWD5+2eqSMxWhwCZA/nz1RLuTAH1C5p02
|
208
|
+
98t18ne9r3henrEkkiyqhd0CgYBH2XplxUGUNL34ZVVfnJ9cA/Mth8TElv+aDwoa
|
209
|
+
a+vLlvgoednm0VxA8qKohpei8A8T+ges77u7gM3jBdjFCmt5BKasRuidRlUUsyvP
|
210
|
+
jxl4Yo9wNmkVrWMkOUn1BRWTEVLnx88EaIOu3CJyJDsaSufbyENCtCXhjVEV4Eqp
|
211
|
+
2WN5QQKBgDHr6hoSHRNWpC6ruuaFF8MGrV8mnvWbCn9KUxQ6zWbm6hQSyEa/3b4d
|
212
|
+
kc9GOsm4n+EVnWyhUVjwxEExaCRuFa4aJ/ekLZYtepNnd9Nsoknqd1SVW163QjrY
|
213
|
+
tY4IM9IaSC2LuPFVc0Kx6TwObdeQScOokIxL3HfayfLKieTLC+w2
|
214
|
+
-----END RSA PRIVATE KEY-----
|
215
|
+
}
|
216
|
+
|
217
|
+
@@pubkey = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDIboBIvdtZLruuwDksO8qd90IFqArrUHksiv4292MrehEqEKqpGzto1I86oy7Y1sqi3j8G0PSFaKkXsICwyor7bjVygxTDFFOWq+h78vvFnbgq1qOubWST/E1QY/m9bIryxh9icNS4tLKlFpn9l9a4pmBAsN07DYVaocVUugkOxeCxr/KLUXdvmXj4Xfq837ultL1ggyaN2YqeDwn+GxM8RNm51rIRa4DL6Y1rPgztm3FL+A9MglzEjELbs8s6EDVewAG6do1HwS3LqCGxlfZgRieelzIHkyaqLRU4mzqbkhmfaR/U/fHQppRDYWb3CoCSBilJxxYfjLK1VDyXOWkH kitchen-qemu'
|
218
|
+
|
219
|
+
@@archbinary = {
|
220
|
+
'i386' => 'qemu-system-i386',
|
221
|
+
'amd64' => 'qemu-system-x86_64',
|
222
|
+
'x86' => 'qemu-system-i386',
|
223
|
+
'x86_64' => 'qemu-system-x86_64',
|
224
|
+
'32bit' => 'qemu-system-i386',
|
225
|
+
'64bit' => 'qemu-system-x86_64',
|
226
|
+
}
|
227
|
+
|
228
|
+
def privkey_path
|
229
|
+
File.join(config[:kitchen_root], '.kitchen', 'kitchen-qemu.key')
|
230
|
+
end
|
231
|
+
|
232
|
+
def monitor_path
|
233
|
+
File.join(config[:kitchen_root], '.kitchen', "#{instance.name}.mon")
|
234
|
+
end
|
235
|
+
|
236
|
+
def spice_path
|
237
|
+
File.join(config[:kitchen_root], '.kitchen', "#{instance.name}.spice")
|
238
|
+
end
|
239
|
+
|
240
|
+
def create_privkey
|
241
|
+
path = privkey_path
|
242
|
+
return true if File.file?(path)
|
243
|
+
File.open(path, File::CREAT|File::TRUNC|File::RDWR, 0600) { |f| f.write(@@privkey) }
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# vim: set ts=2 sw=2 et:
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# This file is part of kitchen-qemu.
|
2
|
+
# Copyright 2016 Emil Renner Berthing <esmil@esmil.dk>
|
3
|
+
#
|
4
|
+
# kitchen-qemu is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# kitchen-qemu is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with kitchen-qemu. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
module Kitchen
|
18
|
+
module Driver
|
19
|
+
# Version string for the QEMU Kitchen driver
|
20
|
+
QEMU_VERSION = '0.1.0'
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# This file is part of kitchen-qemu.
|
2
|
+
# Copyright 2016 Emil Renner Berthing <esmil@esmil.dk>
|
3
|
+
#
|
4
|
+
# kitchen-qemu is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# kitchen-qemu is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with kitchen-qemu. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'json'
|
18
|
+
require 'socket'
|
19
|
+
|
20
|
+
module Kitchen
|
21
|
+
module Driver
|
22
|
+
class QMPClient
|
23
|
+
class Timeout < Exception
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(io, timeout = 1)
|
27
|
+
@io = io
|
28
|
+
@ioa = [ io ]
|
29
|
+
@timeout = timeout
|
30
|
+
@buf = []
|
31
|
+
readnext(timeout) or raise Timeout
|
32
|
+
execute('qmp_capabilities') or raise Timeout
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute(cmd, timeout = @timeout)
|
37
|
+
send( 'execute' => cmd )
|
38
|
+
loop do
|
39
|
+
ret = readnext(timeout) or raise Timeout
|
40
|
+
if ret['return']
|
41
|
+
return ret['return']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def wait_for_eof(timeout = @timeout)
|
47
|
+
while IO.select(@ioa, nil, nil, timeout)
|
48
|
+
begin
|
49
|
+
@io.read_nonblock(4096)
|
50
|
+
rescue EOFError
|
51
|
+
return
|
52
|
+
rescue Errno::ECONNRESET
|
53
|
+
return
|
54
|
+
rescue IO::WaitReadable
|
55
|
+
# do notheng
|
56
|
+
end
|
57
|
+
end
|
58
|
+
raise Timeout
|
59
|
+
end
|
60
|
+
|
61
|
+
def close
|
62
|
+
@io.close
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def send(obj)
|
68
|
+
@io.write("#{obj.to_json}\r\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
def readnext(timeout)
|
72
|
+
loop do
|
73
|
+
if not @buf.empty? and @buf.last.match("\n")
|
74
|
+
s = @buf.pop.split("\n", 2)
|
75
|
+
@buf.push(s[0])
|
76
|
+
obj = JSON.parse(@buf.join(''))
|
77
|
+
@buf.clear
|
78
|
+
@buf.push(s[1]) unless s[1].empty?
|
79
|
+
return obj
|
80
|
+
end
|
81
|
+
|
82
|
+
loop do
|
83
|
+
return nil unless IO.select(@ioa, nil, nil, timeout)
|
84
|
+
begin
|
85
|
+
@buf.push(@io.read_nonblock(4096))
|
86
|
+
rescue IO::WaitReadable
|
87
|
+
# do notheng
|
88
|
+
else
|
89
|
+
break
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# vim: set ts=2 sw=2 et:
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kitchen-qemu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emil Renner Berthing
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: test-kitchen
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
description: Kitchen::Driver::Qemu - A QEMU Driver for Test Kitchen.
|
28
|
+
email:
|
29
|
+
- esmil@esmil.dk
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/kitchen/driver/qemu.rb
|
35
|
+
- lib/kitchen/driver/qemu_version.rb
|
36
|
+
- lib/kitchen/driver/qmpclient.rb
|
37
|
+
homepage: https://github.com/esmil/kitchen-qemu/
|
38
|
+
licenses:
|
39
|
+
- GPL-3.0+
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.5.1
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Kitchen::Driver::Qemu - A QEMU Driver for Test Kitchen.
|
61
|
+
test_files: []
|