blob: d9b8912a9a5b57afc148db6b4ec8f599f385da3c [file] [log] [blame]
lmr5d73e2f2009-10-09 20:46:36 +00001#!/usr/bin/python
2"""
3Simple script to setup unattended installs on KVM guests.
4"""
5# -*- coding: utf-8 -*-
6import os, sys, shutil, tempfile, re
7import common
8
9
10class SetupError(Exception):
11 """
12 Simple wrapper for the builtin Exception class.
13 """
14 pass
15
16
17class UnattendedInstall(object):
18 """
19 Creates a floppy disk image that will contain a config file for unattended
20 OS install. Optionally, sets up a PXE install server using qemu built in
21 TFTP and DHCP servers to install a particular operating system. The
22 parameters to the script are retrieved from environment variables.
23 """
24 def __init__(self):
25 """
26 Gets params from environment variables and sets class attributes.
27 """
28 script_dir = os.path.dirname(sys.modules[__name__].__file__)
29 kvm_test_dir = os.path.abspath(os.path.join(script_dir, ".."))
30 images_dir = os.path.join(kvm_test_dir, 'images')
31 self.deps_dir = os.path.join(kvm_test_dir, 'deps')
32 self.unattended_dir = os.path.join(kvm_test_dir, 'unattended')
33
lmra29a5cb2010-03-18 02:39:34 +000034 tftp_root = os.environ.get('KVM_TEST_tftp', '')
35 if tftp_root:
lmrc4e1d212009-10-13 21:13:00 +000036 self.tftp_root = os.path.join(kvm_test_dir, tftp_root)
lmr5d73e2f2009-10-09 20:46:36 +000037 if not os.path.isdir(self.tftp_root):
38 os.makedirs(self.tftp_root)
lmra29a5cb2010-03-18 02:39:34 +000039 else:
40 self.tftp_root = tftp_root
lmr5d73e2f2009-10-09 20:46:36 +000041
lmra29a5cb2010-03-18 02:39:34 +000042 self.kernel_args = os.environ.get('KVM_TEST_kernel_args', '')
43 self.finish_program= os.environ.get('KVM_TEST_finish_program', '')
44 cdrom_iso = os.environ.get('KVM_TEST_cdrom')
45 self.unattended_file = os.environ.get('KVM_TEST_unattended_file')
lmr5d73e2f2009-10-09 20:46:36 +000046
lmra29a5cb2010-03-18 02:39:34 +000047 self.qemu_img_bin = os.environ.get('KVM_TEST_qemu_img_binary')
lmrfb79e642010-01-18 05:24:11 +000048 if not os.path.isabs(self.qemu_img_bin):
49 self.qemu_img_bin = os.path.join(kvm_test_dir, self.qemu_img_bin)
lmr5d73e2f2009-10-09 20:46:36 +000050 self.cdrom_iso = os.path.join(kvm_test_dir, cdrom_iso)
51 self.floppy_mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
52 self.cdrom_mount = tempfile.mkdtemp(prefix='cdrom_', dir='/tmp')
lmree1e40f2010-06-10 15:32:27 +000053 self.nfs_mount = tempfile.mkdtemp(prefix='nfs_', dir='/tmp')
54 floppy_name = os.environ['KVM_TEST_floppy']
55 self.floppy_img = os.path.join(kvm_test_dir, floppy_name)
lmra29a5cb2010-03-18 02:39:34 +000056 floppy_dir = os.path.dirname(self.floppy_img)
57 if not os.path.isdir(floppy_dir):
58 os.makedirs(floppy_dir)
59
60 self.pxe_dir = os.environ.get('KVM_TEST_pxe_dir', '')
61 self.pxe_image = os.environ.get('KVM_TEST_pxe_image', '')
62 self.pxe_initrd = os.environ.get('KVM_TEST_pxe_initrd', '')
lmr5d73e2f2009-10-09 20:46:36 +000063
lmree1e40f2010-06-10 15:32:27 +000064 self.medium = os.environ.get('KVM_TEST_medium', '')
65 self.url = os.environ.get('KVM_TEST_url', '')
66 self.kernel = os.environ.get('KVM_TEST_kernel', '')
67 self.initrd = os.environ.get('KVM_TEST_initrd', '')
68 self.nfs_server = os.environ.get('KVM_TEST_nfs_server', '')
69 self.nfs_dir = os.environ.get('KVM_TEST_nfs_dir', '')
70 self.image_path = kvm_test_dir
71 self.kernel_path = os.path.join(self.image_path, self.kernel)
72 self.initrd_path = os.path.join(self.image_path, self.initrd)
73
lmr5d73e2f2009-10-09 20:46:36 +000074
75 def create_boot_floppy(self):
76 """
77 Prepares a boot floppy by creating a floppy image file, mounting it and
78 copying an answer file (kickstarts for RH based distros, answer files
79 for windows) to it. After that the image is umounted.
80 """
81 print "Creating boot floppy"
82
83 if os.path.exists(self.floppy_img):
84 os.remove(self.floppy_img)
85
lmrb010c7e2010-02-24 11:54:30 +000086 c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin,
87 self.floppy_img)
lmr5d73e2f2009-10-09 20:46:36 +000088 if os.system(c_cmd):
89 raise SetupError('Could not create floppy image.')
90
91 f_cmd = 'mkfs.msdos -s 1 %s' % self.floppy_img
92 if os.system(f_cmd):
93 raise SetupError('Error formatting floppy image.')
94
lmrb010c7e2010-02-24 11:54:30 +000095 try:
96 m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
97 if os.system(m_cmd):
98 raise SetupError('Could not mount floppy image.')
lmr5d73e2f2009-10-09 20:46:36 +000099
lmrb010c7e2010-02-24 11:54:30 +0000100 if self.unattended_file.endswith('.sif'):
101 dest_fname = 'winnt.sif'
102 setup_file = 'winnt.bat'
103 setup_file_path = os.path.join(self.unattended_dir, setup_file)
104 setup_file_dest = os.path.join(self.floppy_mount, setup_file)
105 shutil.copyfile(setup_file_path, setup_file_dest)
106 elif self.unattended_file.endswith('.ks'):
lmra29a5cb2010-03-18 02:39:34 +0000107 # Red Hat kickstart install
lmrb010c7e2010-02-24 11:54:30 +0000108 dest_fname = 'ks.cfg'
109 elif self.unattended_file.endswith('.xml'):
lmra29a5cb2010-03-18 02:39:34 +0000110 if self.tftp_root is '':
111 # Windows unattended install
112 dest_fname = "autounattend.xml"
113 else:
114 # SUSE autoyast install
115 dest_fname = "autoinst.xml"
lmr5d73e2f2009-10-09 20:46:36 +0000116
lmr5d73e2f2009-10-09 20:46:36 +0000117 dest = os.path.join(self.floppy_mount, dest_fname)
lmr5d73e2f2009-10-09 20:46:36 +0000118
lmrb010c7e2010-02-24 11:54:30 +0000119 # Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
lmree1e40f2010-06-10 15:32:27 +0000120 # provided for this test and replace the KVM_TEST_MEDIUM with
121 # the tree url or nfs address provided for this test.
lmrb010c7e2010-02-24 11:54:30 +0000122 unattended_contents = open(self.unattended_file).read()
123 dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
124 real_cdkey = os.environ.get('KVM_TEST_cdkey')
125 if re.search(dummy_cdkey_re, unattended_contents):
126 if real_cdkey:
127 unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
128 unattended_contents)
129 else:
130 print ("WARNING: 'cdkey' required but not specified for "
131 "this unattended installation")
lmr0ac82102010-06-18 13:40:32 +0000132
lmree1e40f2010-06-10 15:32:27 +0000133 dummy_re = r'\bKVM_TEST_MEDIUM\b'
134 if self.medium == "cdrom":
135 content = "cdrom"
136 elif self.medium == "url":
137 content = "url --url %s" % self.url
138 elif self.medium == "nfs":
139 content = "nfs --server=%s --dir=%s" % (self.nfs_server, self.nfs_dir)
140 else:
141 raise SetupError("Unexpected installation medium %s" % self.url)
lmr0ac82102010-06-18 13:40:32 +0000142
lmree1e40f2010-06-10 15:32:27 +0000143 unattended_contents = re.sub(dummy_re, content, unattended_contents)
lmrb010c7e2010-02-24 11:54:30 +0000144
lmr0ac82102010-06-18 13:40:32 +0000145 print
146 print "Unattended install %s contents:" % dest_fname
lmree1e40f2010-06-10 15:32:27 +0000147 print unattended_contents
lmrb010c7e2010-02-24 11:54:30 +0000148 # Write the unattended file contents to 'dest'
149 open(dest, 'w').write(unattended_contents)
150
151 if self.finish_program:
152 dest_fname = os.path.basename(self.finish_program)
153 dest = os.path.join(self.floppy_mount, dest_fname)
154 shutil.copyfile(self.finish_program, dest)
155
156 finally:
157 u_cmd = 'umount %s' % self.floppy_mount
158 if os.system(u_cmd):
159 raise SetupError('Could not unmount floppy at %s.' %
160 self.floppy_mount)
161 self.cleanup(self.floppy_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000162
163 os.chmod(self.floppy_img, 0755)
164
165 print "Boot floppy created successfuly"
166
167
168 def setup_pxe_boot(self):
169 """
170 Sets up a PXE boot environment using the built in qemu TFTP server.
171 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
172 pxelinux package or equivalent for your distro), and vmlinuz and
173 initrd.img files from the CD to a directory that qemu will serve trough
174 TFTP to the VM.
175 """
176 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
177
178 pxe_file = None
179 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
180 '/usr/share/syslinux/pxelinux.0']
181 for path in pxe_paths:
182 if os.path.isfile(path):
183 pxe_file = path
184 break
185
186 if not pxe_file:
187 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
188 'sure pxelinux or equivalent package for your '
189 'distro is installed.')
190
191 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
192 shutil.copyfile(pxe_file, pxe_dest)
193
lmrb010c7e2010-02-24 11:54:30 +0000194 try:
195 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
196 self.cdrom_mount)
197 if os.system(m_cmd):
198 raise SetupError('Could not mount CD image %s.' %
199 self.cdrom_iso)
lmr5d73e2f2009-10-09 20:46:36 +0000200
lmra29a5cb2010-03-18 02:39:34 +0000201 pxe_dir = os.path.join(self.cdrom_mount, self.pxe_dir)
202 pxe_image = os.path.join(pxe_dir, self.pxe_image)
203 pxe_initrd = os.path.join(pxe_dir, self.pxe_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000204
lmrb010c7e2010-02-24 11:54:30 +0000205 if not os.path.isdir(pxe_dir):
206 raise SetupError('The ISO image does not have a %s dir. The '
207 'script assumes that the cd has a %s dir '
208 'where to search for the vmlinuz image.' %
lmra29a5cb2010-03-18 02:39:34 +0000209 (self.pxe_dir, self.pxe_dir))
lmr5d73e2f2009-10-09 20:46:36 +0000210
lmrb010c7e2010-02-24 11:54:30 +0000211 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
212 raise SetupError('The location %s is lacking either a vmlinuz '
213 'or a initrd.img file. Cannot find a PXE '
lmra29a5cb2010-03-18 02:39:34 +0000214 'image to proceed.' % self.pxe_dir)
lmr5d73e2f2009-10-09 20:46:36 +0000215
lmrb010c7e2010-02-24 11:54:30 +0000216 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
217 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
218 shutil.copyfile(pxe_image, tftp_image)
219 shutil.copyfile(pxe_initrd, tftp_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000220
lmrb010c7e2010-02-24 11:54:30 +0000221 finally:
222 u_cmd = 'umount %s' % self.cdrom_mount
223 if os.system(u_cmd):
224 raise SetupError('Could not unmount CD at %s.' %
225 self.cdrom_mount)
226 self.cleanup(self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000227
228 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
229 if not os.path.isdir(pxe_config_dir):
230 os.makedirs(pxe_config_dir)
231 pxe_config_path = os.path.join(pxe_config_dir, 'default')
232
233 pxe_config = open(pxe_config_path, 'w')
234 pxe_config.write('DEFAULT pxeboot\n')
235 pxe_config.write('TIMEOUT 20\n')
236 pxe_config.write('PROMPT 0\n')
237 pxe_config.write('LABEL pxeboot\n')
238 pxe_config.write(' KERNEL vmlinuz\n')
lmr5d73e2f2009-10-09 20:46:36 +0000239 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
240 self.kernel_args)
241 pxe_config.close()
242
243 print "PXE boot successfuly set"
244
lmrb010c7e2010-02-24 11:54:30 +0000245
lmree1e40f2010-06-10 15:32:27 +0000246 def setup_url(self):
247 """
248 Download the vmlinuz and initrd.img from URL
249 """
250 print "Downloading the vmlinuz and initrd.img"
251 os.chdir(self.image_path)
252
253 kernel_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.kernel)
254 initrd_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.initrd)
255
256 if os.path.exists(self.kernel):
257 os.unlink(self.kernel)
258 if os.path.exists(self.initrd):
259 os.unlink(self.initrd)
260
261 if os.system(kernel_fetch_cmd) != 0:
262 raise SetupError("Could not fetch vmlinuz from %s" % self.url)
263 if os.system(initrd_fetch_cmd) != 0:
264 raise SetupError("Could not fetch initrd.img from %s" % self.url)
265
266 print "Downloading finish"
267
268 def setup_nfs(self):
269 """
270 Copy the vmlinuz and initrd.img from nfs.
271 """
272 print "Copying the vmlinuz and initrd.img from nfs"
273
274 m_cmd = "mount %s:%s %s -o ro" % (self.nfs_server, self.nfs_dir, self.nfs_mount)
275 if os.system(m_cmd):
276 raise SetupError('Could not mount nfs server.')
277
278 kernel_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
279 self.kernel,
280 self.image_path)
281 initrd_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
282 self.initrd,
283 self.image_path)
284
285 try:
286 if os.system(kernel_fetch_cmd):
287 raise SetupError("Could not copy the vmlinuz from %s" %
288 self.nfs_mount)
289 if os.system(initrd_fetch_cmd):
290 raise SetupError("Could not copy the initrd.img from %s" %
291 self.nfs_mount)
292 finally:
293 u_cmd = "umount %s" % self.nfs_mount
294 if os.system(u_cmd):
295 raise SetupError("Could not unmont nfs at %s" % self.nfs_mount)
296 self.cleanup(self.nfs_mount)
297
lmrb010c7e2010-02-24 11:54:30 +0000298 def cleanup(self, mount):
lmr5d73e2f2009-10-09 20:46:36 +0000299 """
lmrb010c7e2010-02-24 11:54:30 +0000300 Clean up a previously used mountpoint.
301
302 @param mount: Mountpoint to be cleaned up.
lmr5d73e2f2009-10-09 20:46:36 +0000303 """
lmrb010c7e2010-02-24 11:54:30 +0000304 if os.path.isdir(mount):
305 if os.path.ismount(mount):
306 print "Path %s is still mounted, please verify" % mount
307 else:
308 print "Removing mount point %s" % mount
309 os.rmdir(mount)
lmr5d73e2f2009-10-09 20:46:36 +0000310
311
312 def setup(self):
313 print "Starting unattended install setup"
314
315 print "Variables set:"
lmree1e40f2010-06-10 15:32:27 +0000316 print " medium: " + str(self.medium)
lmr5d73e2f2009-10-09 20:46:36 +0000317 print " qemu_img_bin: " + str(self.qemu_img_bin)
318 print " cdrom iso: " + str(self.cdrom_iso)
319 print " unattended_file: " + str(self.unattended_file)
320 print " kernel_args: " + str(self.kernel_args)
321 print " tftp_root: " + str(self.tftp_root)
322 print " floppy_mount: " + str(self.floppy_mount)
323 print " floppy_img: " + str(self.floppy_img)
324 print " finish_program: " + str(self.finish_program)
lmra29a5cb2010-03-18 02:39:34 +0000325 print " pxe_dir: " + str(self.pxe_dir)
326 print " pxe_image: " + str(self.pxe_image)
327 print " pxe_initrd: " + str(self.pxe_initrd)
lmree1e40f2010-06-10 15:32:27 +0000328 print " url: " + str(self.url)
329 print " kernel: " + str(self.kernel)
330 print " initrd: " + str(self.initrd)
331 print " nfs_server: " + str(self.nfs_server)
332 print " nfs_dir: " + str(self.nfs_dir)
333 print " nfs_mount: " + str(self.nfs_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000334
lmree1e40f2010-06-10 15:32:27 +0000335 if self.unattended_file and self.floppy_img is not None:
336 self.create_boot_floppy()
337 if self.medium == "cdrom":
338 if self.tftp_root:
339 self.setup_pxe_boot()
340 elif self.medium == "url":
341 self.setup_url()
342 elif self.medium == "nfs":
343 self.setup_nfs()
344 else:
345 raise SetupError("Unexpected installation method %s" %
346 self.medium)
lmr5d73e2f2009-10-09 20:46:36 +0000347 print "Unattended install setup finished successfuly"
348
349
350if __name__ == "__main__":
351 os_install = UnattendedInstall()
352 os_install.setup()