blob: 63c01b1392ca3060c3d73e646a366b1dfb249c7b [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
34 try:
35 tftp_root = os.environ['KVM_TEST_tftp']
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)
39 except KeyError:
40 self.tftp_root = ''
41
42 try:
43 self.kernel_args = os.environ['KVM_TEST_kernel_args']
44 except KeyError:
45 self.kernel_args = ''
46
47 try:
48 self.finish_program= os.environ['KVM_TEST_finish_program']
49 except:
50 self.finish_program = None
51
52
53 cdrom_iso = os.environ['KVM_TEST_cdrom']
54 self.unattended_file = os.environ['KVM_TEST_unattended_file']
55
lmr149ea692009-12-11 20:34:18 +000056 self.qemu_img_bin = os.environ['KVM_TEST_qemu_img_binary']
lmrfb79e642010-01-18 05:24:11 +000057 if not os.path.isabs(self.qemu_img_bin):
58 self.qemu_img_bin = os.path.join(kvm_test_dir, self.qemu_img_bin)
lmr5d73e2f2009-10-09 20:46:36 +000059 self.cdrom_iso = os.path.join(kvm_test_dir, cdrom_iso)
60 self.floppy_mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
61 self.cdrom_mount = tempfile.mkdtemp(prefix='cdrom_', dir='/tmp')
62 self.floppy_img = os.path.join(images_dir, 'floppy.img')
63
64
65 def create_boot_floppy(self):
66 """
67 Prepares a boot floppy by creating a floppy image file, mounting it and
68 copying an answer file (kickstarts for RH based distros, answer files
69 for windows) to it. After that the image is umounted.
70 """
71 print "Creating boot floppy"
72
73 if os.path.exists(self.floppy_img):
74 os.remove(self.floppy_img)
75
lmrb010c7e2010-02-24 11:54:30 +000076 c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin,
77 self.floppy_img)
lmr5d73e2f2009-10-09 20:46:36 +000078 if os.system(c_cmd):
79 raise SetupError('Could not create floppy image.')
80
81 f_cmd = 'mkfs.msdos -s 1 %s' % self.floppy_img
82 if os.system(f_cmd):
83 raise SetupError('Error formatting floppy image.')
84
lmrb010c7e2010-02-24 11:54:30 +000085 try:
86 m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
87 if os.system(m_cmd):
88 raise SetupError('Could not mount floppy image.')
lmr5d73e2f2009-10-09 20:46:36 +000089
lmrb010c7e2010-02-24 11:54:30 +000090 if self.unattended_file.endswith('.sif'):
91 dest_fname = 'winnt.sif'
92 setup_file = 'winnt.bat'
93 setup_file_path = os.path.join(self.unattended_dir, setup_file)
94 setup_file_dest = os.path.join(self.floppy_mount, setup_file)
95 shutil.copyfile(setup_file_path, setup_file_dest)
96 elif self.unattended_file.endswith('.ks'):
97 dest_fname = 'ks.cfg'
98 elif self.unattended_file.endswith('.xml'):
99 dest_fname = "autounattend.xml"
lmr5d73e2f2009-10-09 20:46:36 +0000100
lmr5d73e2f2009-10-09 20:46:36 +0000101 dest = os.path.join(self.floppy_mount, dest_fname)
lmr5d73e2f2009-10-09 20:46:36 +0000102
lmrb010c7e2010-02-24 11:54:30 +0000103 # Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
104 # provided for this test
105 unattended_contents = open(self.unattended_file).read()
106 dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
107 real_cdkey = os.environ.get('KVM_TEST_cdkey')
108 if re.search(dummy_cdkey_re, unattended_contents):
109 if real_cdkey:
110 unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
111 unattended_contents)
112 else:
113 print ("WARNING: 'cdkey' required but not specified for "
114 "this unattended installation")
115
116 # Write the unattended file contents to 'dest'
117 open(dest, 'w').write(unattended_contents)
118
119 if self.finish_program:
120 dest_fname = os.path.basename(self.finish_program)
121 dest = os.path.join(self.floppy_mount, dest_fname)
122 shutil.copyfile(self.finish_program, dest)
123
124 finally:
125 u_cmd = 'umount %s' % self.floppy_mount
126 if os.system(u_cmd):
127 raise SetupError('Could not unmount floppy at %s.' %
128 self.floppy_mount)
129 self.cleanup(self.floppy_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000130
131 os.chmod(self.floppy_img, 0755)
132
133 print "Boot floppy created successfuly"
134
135
136 def setup_pxe_boot(self):
137 """
138 Sets up a PXE boot environment using the built in qemu TFTP server.
139 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
140 pxelinux package or equivalent for your distro), and vmlinuz and
141 initrd.img files from the CD to a directory that qemu will serve trough
142 TFTP to the VM.
143 """
144 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
145
146 pxe_file = None
147 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
148 '/usr/share/syslinux/pxelinux.0']
149 for path in pxe_paths:
150 if os.path.isfile(path):
151 pxe_file = path
152 break
153
154 if not pxe_file:
155 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
156 'sure pxelinux or equivalent package for your '
157 'distro is installed.')
158
159 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
160 shutil.copyfile(pxe_file, pxe_dest)
161
lmrb010c7e2010-02-24 11:54:30 +0000162 try:
163 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
164 self.cdrom_mount)
165 if os.system(m_cmd):
166 raise SetupError('Could not mount CD image %s.' %
167 self.cdrom_iso)
lmr5d73e2f2009-10-09 20:46:36 +0000168
lmrb010c7e2010-02-24 11:54:30 +0000169 p = os.path.join('images', 'pxeboot')
170 pxe_dir = os.path.join(self.cdrom_mount, p)
171 pxe_image = os.path.join(pxe_dir, 'vmlinuz')
172 pxe_initrd = os.path.join(pxe_dir, 'initrd.img')
lmr5d73e2f2009-10-09 20:46:36 +0000173
lmrb010c7e2010-02-24 11:54:30 +0000174 if not os.path.isdir(pxe_dir):
175 raise SetupError('The ISO image does not have a %s dir. The '
176 'script assumes that the cd has a %s dir '
177 'where to search for the vmlinuz image.' %
178 (p, p))
lmr5d73e2f2009-10-09 20:46:36 +0000179
lmrb010c7e2010-02-24 11:54:30 +0000180 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
181 raise SetupError('The location %s is lacking either a vmlinuz '
182 'or a initrd.img file. Cannot find a PXE '
183 'image to proceed.' % pxe_dir)
lmr5d73e2f2009-10-09 20:46:36 +0000184
lmrb010c7e2010-02-24 11:54:30 +0000185 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
186 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
187 shutil.copyfile(pxe_image, tftp_image)
188 shutil.copyfile(pxe_initrd, tftp_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000189
lmrb010c7e2010-02-24 11:54:30 +0000190 finally:
191 u_cmd = 'umount %s' % self.cdrom_mount
192 if os.system(u_cmd):
193 raise SetupError('Could not unmount CD at %s.' %
194 self.cdrom_mount)
195 self.cleanup(self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000196
197 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
198 if not os.path.isdir(pxe_config_dir):
199 os.makedirs(pxe_config_dir)
200 pxe_config_path = os.path.join(pxe_config_dir, 'default')
201
202 pxe_config = open(pxe_config_path, 'w')
203 pxe_config.write('DEFAULT pxeboot\n')
204 pxe_config.write('TIMEOUT 20\n')
205 pxe_config.write('PROMPT 0\n')
206 pxe_config.write('LABEL pxeboot\n')
207 pxe_config.write(' KERNEL vmlinuz\n')
208 pxe_config.write(' KERNEL vmlinuz\n')
209 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
210 self.kernel_args)
211 pxe_config.close()
212
213 print "PXE boot successfuly set"
214
lmrb010c7e2010-02-24 11:54:30 +0000215
216 def cleanup(self, mount):
lmr5d73e2f2009-10-09 20:46:36 +0000217 """
lmrb010c7e2010-02-24 11:54:30 +0000218 Clean up a previously used mountpoint.
219
220 @param mount: Mountpoint to be cleaned up.
lmr5d73e2f2009-10-09 20:46:36 +0000221 """
lmrb010c7e2010-02-24 11:54:30 +0000222 if os.path.isdir(mount):
223 if os.path.ismount(mount):
224 print "Path %s is still mounted, please verify" % mount
225 else:
226 print "Removing mount point %s" % mount
227 os.rmdir(mount)
lmr5d73e2f2009-10-09 20:46:36 +0000228
229
230 def setup(self):
231 print "Starting unattended install setup"
232
233 print "Variables set:"
234 print " qemu_img_bin: " + str(self.qemu_img_bin)
235 print " cdrom iso: " + str(self.cdrom_iso)
236 print " unattended_file: " + str(self.unattended_file)
237 print " kernel_args: " + str(self.kernel_args)
238 print " tftp_root: " + str(self.tftp_root)
239 print " floppy_mount: " + str(self.floppy_mount)
240 print " floppy_img: " + str(self.floppy_img)
241 print " finish_program: " + str(self.finish_program)
242
243 self.create_boot_floppy()
244 if self.tftp_root:
245 self.setup_pxe_boot()
lmr5d73e2f2009-10-09 20:46:36 +0000246 print "Unattended install setup finished successfuly"
247
248
249if __name__ == "__main__":
250 os_install = UnattendedInstall()
251 os_install.setup()