blob: 0377d8332d5644b1b0ef7b63bd07a4718375b187 [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")
lmree1e40f2010-06-10 15:32:27 +0000132
133 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)
142
143 unattended_contents = re.sub(dummy_re, content, unattended_contents)
lmrb010c7e2010-02-24 11:54:30 +0000144
lmree1e40f2010-06-10 15:32:27 +0000145 print unattended_contents
lmrb010c7e2010-02-24 11:54:30 +0000146 # Write the unattended file contents to 'dest'
147 open(dest, 'w').write(unattended_contents)
148
149 if self.finish_program:
150 dest_fname = os.path.basename(self.finish_program)
151 dest = os.path.join(self.floppy_mount, dest_fname)
152 shutil.copyfile(self.finish_program, dest)
153
154 finally:
155 u_cmd = 'umount %s' % self.floppy_mount
156 if os.system(u_cmd):
157 raise SetupError('Could not unmount floppy at %s.' %
158 self.floppy_mount)
159 self.cleanup(self.floppy_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000160
161 os.chmod(self.floppy_img, 0755)
162
163 print "Boot floppy created successfuly"
164
165
166 def setup_pxe_boot(self):
167 """
168 Sets up a PXE boot environment using the built in qemu TFTP server.
169 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
170 pxelinux package or equivalent for your distro), and vmlinuz and
171 initrd.img files from the CD to a directory that qemu will serve trough
172 TFTP to the VM.
173 """
174 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
175
176 pxe_file = None
177 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
178 '/usr/share/syslinux/pxelinux.0']
179 for path in pxe_paths:
180 if os.path.isfile(path):
181 pxe_file = path
182 break
183
184 if not pxe_file:
185 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
186 'sure pxelinux or equivalent package for your '
187 'distro is installed.')
188
189 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
190 shutil.copyfile(pxe_file, pxe_dest)
191
lmrb010c7e2010-02-24 11:54:30 +0000192 try:
193 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
194 self.cdrom_mount)
195 if os.system(m_cmd):
196 raise SetupError('Could not mount CD image %s.' %
197 self.cdrom_iso)
lmr5d73e2f2009-10-09 20:46:36 +0000198
lmra29a5cb2010-03-18 02:39:34 +0000199 pxe_dir = os.path.join(self.cdrom_mount, self.pxe_dir)
200 pxe_image = os.path.join(pxe_dir, self.pxe_image)
201 pxe_initrd = os.path.join(pxe_dir, self.pxe_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000202
lmrb010c7e2010-02-24 11:54:30 +0000203 if not os.path.isdir(pxe_dir):
204 raise SetupError('The ISO image does not have a %s dir. The '
205 'script assumes that the cd has a %s dir '
206 'where to search for the vmlinuz image.' %
lmra29a5cb2010-03-18 02:39:34 +0000207 (self.pxe_dir, self.pxe_dir))
lmr5d73e2f2009-10-09 20:46:36 +0000208
lmrb010c7e2010-02-24 11:54:30 +0000209 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
210 raise SetupError('The location %s is lacking either a vmlinuz '
211 'or a initrd.img file. Cannot find a PXE '
lmra29a5cb2010-03-18 02:39:34 +0000212 'image to proceed.' % self.pxe_dir)
lmr5d73e2f2009-10-09 20:46:36 +0000213
lmrb010c7e2010-02-24 11:54:30 +0000214 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
215 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
216 shutil.copyfile(pxe_image, tftp_image)
217 shutil.copyfile(pxe_initrd, tftp_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000218
lmrb010c7e2010-02-24 11:54:30 +0000219 finally:
220 u_cmd = 'umount %s' % self.cdrom_mount
221 if os.system(u_cmd):
222 raise SetupError('Could not unmount CD at %s.' %
223 self.cdrom_mount)
224 self.cleanup(self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000225
226 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
227 if not os.path.isdir(pxe_config_dir):
228 os.makedirs(pxe_config_dir)
229 pxe_config_path = os.path.join(pxe_config_dir, 'default')
230
231 pxe_config = open(pxe_config_path, 'w')
232 pxe_config.write('DEFAULT pxeboot\n')
233 pxe_config.write('TIMEOUT 20\n')
234 pxe_config.write('PROMPT 0\n')
235 pxe_config.write('LABEL pxeboot\n')
236 pxe_config.write(' KERNEL vmlinuz\n')
lmr5d73e2f2009-10-09 20:46:36 +0000237 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
238 self.kernel_args)
239 pxe_config.close()
240
241 print "PXE boot successfuly set"
242
lmrb010c7e2010-02-24 11:54:30 +0000243
lmree1e40f2010-06-10 15:32:27 +0000244 def setup_url(self):
245 """
246 Download the vmlinuz and initrd.img from URL
247 """
248 print "Downloading the vmlinuz and initrd.img"
249 os.chdir(self.image_path)
250
251 kernel_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.kernel)
252 initrd_fetch_cmd = "wget -q %s/isolinux/%s" % (self.url, self.initrd)
253
254 if os.path.exists(self.kernel):
255 os.unlink(self.kernel)
256 if os.path.exists(self.initrd):
257 os.unlink(self.initrd)
258
259 if os.system(kernel_fetch_cmd) != 0:
260 raise SetupError("Could not fetch vmlinuz from %s" % self.url)
261 if os.system(initrd_fetch_cmd) != 0:
262 raise SetupError("Could not fetch initrd.img from %s" % self.url)
263
264 print "Downloading finish"
265
266 def setup_nfs(self):
267 """
268 Copy the vmlinuz and initrd.img from nfs.
269 """
270 print "Copying the vmlinuz and initrd.img from nfs"
271
272 m_cmd = "mount %s:%s %s -o ro" % (self.nfs_server, self.nfs_dir, self.nfs_mount)
273 if os.system(m_cmd):
274 raise SetupError('Could not mount nfs server.')
275
276 kernel_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
277 self.kernel,
278 self.image_path)
279 initrd_fetch_cmd = "cp %s/isolinux/%s %s" % (self.nfs_mount,
280 self.initrd,
281 self.image_path)
282
283 try:
284 if os.system(kernel_fetch_cmd):
285 raise SetupError("Could not copy the vmlinuz from %s" %
286 self.nfs_mount)
287 if os.system(initrd_fetch_cmd):
288 raise SetupError("Could not copy the initrd.img from %s" %
289 self.nfs_mount)
290 finally:
291 u_cmd = "umount %s" % self.nfs_mount
292 if os.system(u_cmd):
293 raise SetupError("Could not unmont nfs at %s" % self.nfs_mount)
294 self.cleanup(self.nfs_mount)
295
lmrb010c7e2010-02-24 11:54:30 +0000296 def cleanup(self, mount):
lmr5d73e2f2009-10-09 20:46:36 +0000297 """
lmrb010c7e2010-02-24 11:54:30 +0000298 Clean up a previously used mountpoint.
299
300 @param mount: Mountpoint to be cleaned up.
lmr5d73e2f2009-10-09 20:46:36 +0000301 """
lmrb010c7e2010-02-24 11:54:30 +0000302 if os.path.isdir(mount):
303 if os.path.ismount(mount):
304 print "Path %s is still mounted, please verify" % mount
305 else:
306 print "Removing mount point %s" % mount
307 os.rmdir(mount)
lmr5d73e2f2009-10-09 20:46:36 +0000308
309
310 def setup(self):
311 print "Starting unattended install setup"
312
313 print "Variables set:"
lmree1e40f2010-06-10 15:32:27 +0000314 print " medium: " + str(self.medium)
lmr5d73e2f2009-10-09 20:46:36 +0000315 print " qemu_img_bin: " + str(self.qemu_img_bin)
316 print " cdrom iso: " + str(self.cdrom_iso)
317 print " unattended_file: " + str(self.unattended_file)
318 print " kernel_args: " + str(self.kernel_args)
319 print " tftp_root: " + str(self.tftp_root)
320 print " floppy_mount: " + str(self.floppy_mount)
321 print " floppy_img: " + str(self.floppy_img)
322 print " finish_program: " + str(self.finish_program)
lmra29a5cb2010-03-18 02:39:34 +0000323 print " pxe_dir: " + str(self.pxe_dir)
324 print " pxe_image: " + str(self.pxe_image)
325 print " pxe_initrd: " + str(self.pxe_initrd)
lmree1e40f2010-06-10 15:32:27 +0000326 print " url: " + str(self.url)
327 print " kernel: " + str(self.kernel)
328 print " initrd: " + str(self.initrd)
329 print " nfs_server: " + str(self.nfs_server)
330 print " nfs_dir: " + str(self.nfs_dir)
331 print " nfs_mount: " + str(self.nfs_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000332
lmree1e40f2010-06-10 15:32:27 +0000333 if self.unattended_file and self.floppy_img is not None:
334 self.create_boot_floppy()
335 if self.medium == "cdrom":
336 if self.tftp_root:
337 self.setup_pxe_boot()
338 elif self.medium == "url":
339 self.setup_url()
340 elif self.medium == "nfs":
341 self.setup_nfs()
342 else:
343 raise SetupError("Unexpected installation method %s" %
344 self.medium)
lmr5d73e2f2009-10-09 20:46:36 +0000345 print "Unattended install setup finished successfuly"
346
347
348if __name__ == "__main__":
349 os_install = UnattendedInstall()
350 os_install.setup()