This repository has been archived by the owner on Feb 9, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathstandard-inventory-qcow2
executable file
·290 lines (250 loc) · 10.9 KB
/
standard-inventory-qcow2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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
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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
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
283
284
285
286
287
288
289
290
#!/usr/bin/env python
import argparse
import errno
import json
import os
import shutil
import shlex
import signal
import socket
import subprocess
import sys
import tempfile
import time
import distutils.util
IDENTITY = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA1DrTSXQRF8isQQfPfK3U+eFC4zBrjur+Iy15kbHUYUeSHf5S
jXPYbHYqD1lHj4GJajC9okle9rykKFYZMmJKXLI6987wZ8vfucXo9/kwS6BDAJto
ZpZSj5sWCQ1PI0Ce8CbkazlTp5NIkjRfhXGP8mkNKMEhdNjaYceO49ilnNCIxhpb
eH5dH5hybmQQNmnzf+CGCCLBFmc4g3sFbWhI1ldyJzES5ZX3ahjJZYRUfnndoUM/
TzdkHGqZhL1EeFAsv5iV65HuYbchch4vBAn8jDMmHh8G1ixUCL3uAlosfarZLLyo
3HrZ8U/llq7rXa93PXHyI/3NL/2YP3OMxE8baQIDAQABAoIBAQCxuOUwkKqzsQ9W
kdTWArfj3RhnKigYEX9qM+2m7TT9lbKtvUiiPc2R3k4QdmIvsXlCXLigyzJkCsqp
IJiPEbJV98bbuAan1Rlv92TFK36fBgC15G5D4kQXD/ce828/BSFT2C3WALamEPdn
v8Xx+Ixjokcrxrdeoy4VTcjB0q21J4C2wKP1wEPeMJnuTcySiWQBdAECCbeZ4Vsj
cmRdcvL6z8fedRPtDW7oec+IPkYoyXPktVt8WsQPYkwEVN4hZVBneJPCcuhikYkp
T3WGmPV0MxhUvCZ6hSG8D2mscZXRq3itXVlKJsUWfIHaAIgGomWrPuqC23rOYCdT
5oSZmTvFAoGBAPs1FbbxDDd1fx1hisfXHFasV/sycT6ggP/eUXpBYCqVdxPQvqcA
ktplm5j04dnaQJdHZ8TPlwtL+xlWhmhFhlCFPtVpU1HzIBkp6DkSmmu0gvA/i07Z
pzo5Z+HRZFzruTQx6NjDtvWwiXVLwmZn2oiLeM9xSqPu55OpITifEWNjAoGBANhH
XwV6IvnbUWojs7uiSGsXuJOdB1YCJ+UF6xu8CqdbimaVakemVO02+cgbE6jzpUpo
krbDKOle4fIbUYHPeyB0NMidpDxTAPCGmiJz7BCS1fCxkzRgC+TICjmk5zpaD2md
HCrtzIeHNVpTE26BAjOIbo4QqOHBXk/WPen1iC3DAoGBALsD3DSj46puCMJA2ebI
2EoWaDGUbgZny2GxiwrvHL7XIx1XbHg7zxhUSLBorrNW7nsxJ6m3ugUo/bjxV4LN
L59Gc27ByMvbqmvRbRcAKIJCkrB1Pirnkr2f+xx8nLEotGqNNYIawlzKnqr6SbGf
Y2wAGWKmPyEoPLMLWLYkhfdtAoGANsFa/Tf+wuMTqZuAVXCwhOxsfnKy+MNy9jiZ
XVwuFlDGqVIKpjkmJyhT9KVmRM/qePwgqMSgBvVOnszrxcGRmpXRBzlh6yPYiQyK
2U4f5dJG97j9W7U1TaaXcCCfqdZDMKnmB7hMn8NLbqK5uLBQrltMIgt1tjIOfofv
BNx0raECgYEApAvjwDJ75otKz/mvL3rUf/SNpieODBOLHFQqJmF+4hrSOniHC5jf
f5GS5IuYtBQ1gudBYlSs9fX6T39d2avPsZjfvvSbULXi3OlzWD8sbTtvQPuCaZGI
Df9PUWMYZ3HRwwdsYovSOkT53fG6guy+vElUEDkrpZYczROZ6GUcx70=
-----END RSA PRIVATE KEY-----
"""
USER_DATA = """#cloud-config
users:
- default
- name: root
groups: sudo
shell: /bin/bash
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDUOtNJdBEXyKxBB898rdT54ULjMGuO6v4jLXmRsdRhR5Id/lKNc9hsdioPWUePgYlqML2iSV72vKQoVhkyYkpcsjr3zvBny9+5xej3+TBLoEMAm2hmllKPmxYJDU8jQJ7wJuRrOVOnk0iSNF+FcY/yaQ0owSF02Nphx47j2KWc0IjGGlt4fl0fmHJuZBA2afN/4IYIIsEWZziDewVtaEjWV3InMRLllfdqGMllhFR+ed2hQz9PN2QcapmEvUR4UCy/mJXrke5htyFyHi8ECfyMMyYeHwbWLFQIve4CWix9qtksvKjcetnxT+WWrutdr3c9cfIj/c0v/Zg/c4zETxtp standard-test-qcow2
ssh_pwauth: True
chpasswd:
list: |
root:foobar
expire: False
runcmd:
- mkfs.ext4 /dev/sdb
- mount /dev/sdb /usr/local
- sudo mount -o remount,rw /usr
"""
def main(argv):
parser = argparse.ArgumentParser(description="Inventory for a QCow2 test image")
parser.add_argument("--list", action="store_true", help="Verbose output")
parser.add_argument('--host', help="Get host variables")
parser.add_argument("subjects", nargs="*", default=shlex.split(os.environ.get("TEST_SUBJECTS", "")))
opts = parser.parse_args()
try:
if opts.host:
data = host(opts.host)
else:
data = list(opts.subjects)
sys.stdout.write(json.dumps(data, indent=4, separators=(',', ': ')))
except RuntimeError as ex:
sys.stderr.write("{0}: {1}\n".format(os.path.basename(sys.argv[0]), str(ex)))
return 1
return 0
def list(subjects):
hosts = []
variables = {}
for subject in subjects:
if subject.endswith((".qcow2", ".qcow2c")):
vars = host(subject)
if vars:
hosts.append(subject)
variables[subject] = vars
return {"localhost": {"hosts": hosts, "vars": {}}, "subjects": {"hosts": hosts, "vars": {}},
"_meta": {"hostvars": variables}}
def start_qemu(image, cloudinit, log, disk_directory=None, disk_size=None, portrange=(2222, 5555)):
for port in xrange(*portrange):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
sock.bind(("127.0.0.3", port))
if disk_size:
return subprocess.Popen(["/usr/bin/qemu-system-x86_64", "-m", "1024", image,
"-enable-kvm", "-snapshot", "-cdrom", cloudinit,
"-hdb", disk_directory,
"-net", "nic,model=virtio", "-net",
"user,hostfwd=tcp:127.0.0.3:{0}-:22".format(port),
"-device", "isa-serial,chardev=pts2", "-chardev", "file,id=pts2,path=" + log,
"-display", "none"], stdout=open(os.devnull, 'w')), port
else:
return subprocess.Popen(["/usr/bin/qemu-system-x86_64", "-m", "1024", image,
"-enable-kvm", "-snapshot", "-cdrom", cloudinit,
"-net", "nic,model=virtio", "-net",
"user,hostfwd=tcp:127.0.0.3:{0}-:22".format(port),
"-device", "isa-serial,chardev=pts2", "-chardev", "file,id=pts2,path=" + log,
"-display", "none"], stdout=open(os.devnull, 'w')), port
except IOError:
pass
finally:
sock.close()
else:
raise RuntimeError("unable to find free local port to map SSH to")
def host(image):
null = open(os.devnull, 'w')
try:
tty = os.open("/dev/tty", os.O_WRONLY)
os.dup2(tty, 2)
except OSError:
tty = None
pass
# A directory for temporary stuff
directory = tempfile.mkdtemp(prefix="inventory-cloud")
identity = os.path.join(directory, "identity")
with open(identity, 'w') as f:
f.write(IDENTITY)
os.chmod(identity, 0o600)
metadata = os.path.join(directory, "meta-data")
with open(metadata, 'w') as f:
f.write("")
userdata = os.path.join(directory, "user-data")
with open(userdata, 'w') as f:
f.write(USER_DATA)
# Create additional disk
disk_size = None
disk_directory = None
try:
disk_size = os.environ.get("EXTEND_DISK_SIZE")
if disk_size:
sys.stderr.write("\nCreate additional cloud init disk DISK SIZE {}\n".format(disk_size))
disk_directory = "{}/atomic-host-disk2-{}".format(directory, disk_size)
subprocess.check_call(["qemu-img", "create", "-f", "qcow2", disk_directory, disk_size], stdout=null)
except KeyError:
sys.stderr.write("\nCouldn't create additional cloud init disk DISK SIZE\n")
pass
# Create our cloud init so we can log in
cloudinit = os.path.join(directory, "cloud-init.iso")
subprocess.check_call(["/usr/bin/genisoimage", "-input-charset", "utf-8",
"-volid", "cidata", "-joliet", "-rock", "-quiet",
"-output", cloudinit, userdata, metadata], stdout=null)
# Determine if virtual machine should be kept available for diagnosis after completion
try:
diagnose = distutils.util.strtobool(os.getenv("TEST_DEBUG", "0"))
except ValueError:
diagnose = 0
sys.stderr.write("Launching virtual machine for {0}\n".format(image))
# And launch the actual VM
artifacts = os.environ.get("TEST_ARTIFACTS", os.path.join(os.getcwd(), "artifacts"))
try:
os.makedirs(artifacts)
except OSError as exc:
if exc.errno != errno.EEXIST or not os.path.isdir(artifacts):
raise
log = os.path.join(artifacts, "{0}.log".format(os.path.basename(image)))
proc = None # for failure detection
cpe = None # for exception scoping
for tries in xrange(0, 5):
try:
proc, port = start_qemu(image, cloudinit, log, disk_directory=disk_directory, disk_size=disk_size)
break
except subprocess.CalledProcessError as cpe:
time.sleep(1)
continue
if proc is None:
raise RuntimeError("Could not launch VM for qcow2 image"
" '{0}':{1}".format(image, cpe.output))
# The variables
variables = {"ansible_ssh_port": "{0}".format(port),
"ansible_ssh_host": "127.0.0.3",
"ansible_ssh_user": "root",
"ansible_ssh_pass": "foobar",
"ansible_ssh_private_key_file": identity,
"ansible_ssh_common_args": "-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"}
# Write out a handy inventory file, for our use and for debugging
args = " ".join(["{0}='{1}'".format(*item) for item in variables.items()])
inventory = os.path.join(directory, "inventory")
with open(inventory, "w") as f:
f.write("[subjects]\nlocalhost {1}\n".format(image, args))
# Wait for ssh to come up
ping = ["/usr/bin/ansible", "--inventory", inventory, "localhost", "--module-name", "raw", "--args", "/bin/true"]
for tries in xrange(0, 30):
try:
(pid, ret) = os.waitpid(proc.pid, os.WNOHANG)
if pid != 0:
raise RuntimeError("qemu failed to launch qcow2 image: {0}".format(image))
subprocess.check_call(ping, stdout=null, stderr=null)
break
except subprocess.CalledProcessError:
time.sleep(3)
else:
# Kill the qemu process
try:
os.kill(proc.pid, signal.SIGTERM)
except OSError:
pass
raise RuntimeError("could not access launched qcow2 image: {0}".format(image))
# Process of our parent
ppid = os.getppid()
child = os.fork()
if child:
return variables
# Daemonize and watch the processes
os.chdir("/")
os.setsid()
os.umask(0)
if tty is None:
tty = null.fileno()
# Duplicate standard input to standard output and standard error.
os.dup2(null.fileno(), 0)
os.dup2(tty, 1)
os.dup2(tty, 2)
# Now wait for the parent process to go away, then kill the VM
while True:
time.sleep(3)
try:
os.kill(ppid, 0)
os.kill(proc.pid, 0)
except OSError:
break # Either of the processes no longer exist
if diagnose:
sys.stderr.write("\n")
sys.stderr.write("DIAGNOSE: ssh -p {0} -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null "
"root@{1} # password: {2}\n".format(port, "127.0.0.3", "foobar"))
sys.stderr.write("DIAGNOSE: export ANSIBLE_INVENTORY={0}\n".format(inventory))
sys.stderr.write("DIAGNOSE: kill {0} # when finished\n".format(os.getpid()))
def _signal_handler(*args):
sys.stderr.write("\nDIAGNOSE ending...\n")
signal.signal(signal.SIGTERM, _signal_handler)
signal.pause()
# Kill the qemu process
try:
os.kill(proc.pid, signal.SIGTERM)
except OSError:
pass
shutil.rmtree(directory)
sys.exit(0)
if __name__ == '__main__':
sys.exit(main(sys.argv))