blob: fdadd0304185d282f2fa04524142b3f59f06fd1a [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')
lmra29a5cb2010-03-18 02:39:34 +000053 flopy_name = os.environ['KVM_TEST_floppy']
54 self.floppy_img = os.path.join(kvm_test_dir, flopy_name)
55 floppy_dir = os.path.dirname(self.floppy_img)
56 if not os.path.isdir(floppy_dir):
57 os.makedirs(floppy_dir)
58
59 self.pxe_dir = os.environ.get('KVM_TEST_pxe_dir', '')
60 self.pxe_image = os.environ.get('KVM_TEST_pxe_image', '')
61 self.pxe_initrd = os.environ.get('KVM_TEST_pxe_initrd', '')
lmr5d73e2f2009-10-09 20:46:36 +000062
63
64 def create_boot_floppy(self):
65 """
66 Prepares a boot floppy by creating a floppy image file, mounting it and
67 copying an answer file (kickstarts for RH based distros, answer files
68 for windows) to it. After that the image is umounted.
69 """
70 print "Creating boot floppy"
71
72 if os.path.exists(self.floppy_img):
73 os.remove(self.floppy_img)
74
lmrb010c7e2010-02-24 11:54:30 +000075 c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin,
76 self.floppy_img)
lmr5d73e2f2009-10-09 20:46:36 +000077 if os.system(c_cmd):
78 raise SetupError('Could not create floppy image.')
79
80 f_cmd = 'mkfs.msdos -s 1 %s' % self.floppy_img
81 if os.system(f_cmd):
82 raise SetupError('Error formatting floppy image.')
83
lmrb010c7e2010-02-24 11:54:30 +000084 try:
85 m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
86 if os.system(m_cmd):
87 raise SetupError('Could not mount floppy image.')
lmr5d73e2f2009-10-09 20:46:36 +000088
lmrb010c7e2010-02-24 11:54:30 +000089 if self.unattended_file.endswith('.sif'):
90 dest_fname = 'winnt.sif'
91 setup_file = 'winnt.bat'
92 setup_file_path = os.path.join(self.unattended_dir, setup_file)
93 setup_file_dest = os.path.join(self.floppy_mount, setup_file)
94 shutil.copyfile(setup_file_path, setup_file_dest)
95 elif self.unattended_file.endswith('.ks'):
lmra29a5cb2010-03-18 02:39:34 +000096 # Red Hat kickstart install
lmrb010c7e2010-02-24 11:54:30 +000097 dest_fname = 'ks.cfg'
98 elif self.unattended_file.endswith('.xml'):
lmra29a5cb2010-03-18 02:39:34 +000099 if self.tftp_root is '':
100 # Windows unattended install
101 dest_fname = "autounattend.xml"
102 else:
103 # SUSE autoyast install
104 dest_fname = "autoinst.xml"
lmr5d73e2f2009-10-09 20:46:36 +0000105
lmr5d73e2f2009-10-09 20:46:36 +0000106 dest = os.path.join(self.floppy_mount, dest_fname)
lmr5d73e2f2009-10-09 20:46:36 +0000107
lmrb010c7e2010-02-24 11:54:30 +0000108 # Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
109 # provided for this test
110 unattended_contents = open(self.unattended_file).read()
111 dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
112 real_cdkey = os.environ.get('KVM_TEST_cdkey')
113 if re.search(dummy_cdkey_re, unattended_contents):
114 if real_cdkey:
115 unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
116 unattended_contents)
117 else:
118 print ("WARNING: 'cdkey' required but not specified for "
119 "this unattended installation")
120
121 # Write the unattended file contents to 'dest'
122 open(dest, 'w').write(unattended_contents)
123
124 if self.finish_program:
125 dest_fname = os.path.basename(self.finish_program)
126 dest = os.path.join(self.floppy_mount, dest_fname)
127 shutil.copyfile(self.finish_program, dest)
128
129 finally:
130 u_cmd = 'umount %s' % self.floppy_mount
131 if os.system(u_cmd):
132 raise SetupError('Could not unmount floppy at %s.' %
133 self.floppy_mount)
134 self.cleanup(self.floppy_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000135
136 os.chmod(self.floppy_img, 0755)
137
138 print "Boot floppy created successfuly"
139
140
141 def setup_pxe_boot(self):
142 """
143 Sets up a PXE boot environment using the built in qemu TFTP server.
144 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
145 pxelinux package or equivalent for your distro), and vmlinuz and
146 initrd.img files from the CD to a directory that qemu will serve trough
147 TFTP to the VM.
148 """
149 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
150
151 pxe_file = None
152 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
153 '/usr/share/syslinux/pxelinux.0']
154 for path in pxe_paths:
155 if os.path.isfile(path):
156 pxe_file = path
157 break
158
159 if not pxe_file:
160 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
161 'sure pxelinux or equivalent package for your '
162 'distro is installed.')
163
164 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
165 shutil.copyfile(pxe_file, pxe_dest)
166
lmrb010c7e2010-02-24 11:54:30 +0000167 try:
168 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
169 self.cdrom_mount)
170 if os.system(m_cmd):
171 raise SetupError('Could not mount CD image %s.' %
172 self.cdrom_iso)
lmr5d73e2f2009-10-09 20:46:36 +0000173
lmra29a5cb2010-03-18 02:39:34 +0000174 pxe_dir = os.path.join(self.cdrom_mount, self.pxe_dir)
175 pxe_image = os.path.join(pxe_dir, self.pxe_image)
176 pxe_initrd = os.path.join(pxe_dir, self.pxe_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000177
lmrb010c7e2010-02-24 11:54:30 +0000178 if not os.path.isdir(pxe_dir):
179 raise SetupError('The ISO image does not have a %s dir. The '
180 'script assumes that the cd has a %s dir '
181 'where to search for the vmlinuz image.' %
lmra29a5cb2010-03-18 02:39:34 +0000182 (self.pxe_dir, self.pxe_dir))
lmr5d73e2f2009-10-09 20:46:36 +0000183
lmrb010c7e2010-02-24 11:54:30 +0000184 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
185 raise SetupError('The location %s is lacking either a vmlinuz '
186 'or a initrd.img file. Cannot find a PXE '
lmra29a5cb2010-03-18 02:39:34 +0000187 'image to proceed.' % self.pxe_dir)
lmr5d73e2f2009-10-09 20:46:36 +0000188
lmrb010c7e2010-02-24 11:54:30 +0000189 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
190 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
191 shutil.copyfile(pxe_image, tftp_image)
192 shutil.copyfile(pxe_initrd, tftp_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000193
lmrb010c7e2010-02-24 11:54:30 +0000194 finally:
195 u_cmd = 'umount %s' % self.cdrom_mount
196 if os.system(u_cmd):
197 raise SetupError('Could not unmount CD at %s.' %
198 self.cdrom_mount)
199 self.cleanup(self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000200
201 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
202 if not os.path.isdir(pxe_config_dir):
203 os.makedirs(pxe_config_dir)
204 pxe_config_path = os.path.join(pxe_config_dir, 'default')
205
206 pxe_config = open(pxe_config_path, 'w')
207 pxe_config.write('DEFAULT pxeboot\n')
208 pxe_config.write('TIMEOUT 20\n')
209 pxe_config.write('PROMPT 0\n')
210 pxe_config.write('LABEL pxeboot\n')
211 pxe_config.write(' KERNEL vmlinuz\n')
lmr5d73e2f2009-10-09 20:46:36 +0000212 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
213 self.kernel_args)
214 pxe_config.close()
215
216 print "PXE boot successfuly set"
217
lmrb010c7e2010-02-24 11:54:30 +0000218
219 def cleanup(self, mount):
lmr5d73e2f2009-10-09 20:46:36 +0000220 """
lmrb010c7e2010-02-24 11:54:30 +0000221 Clean up a previously used mountpoint.
222
223 @param mount: Mountpoint to be cleaned up.
lmr5d73e2f2009-10-09 20:46:36 +0000224 """
lmrb010c7e2010-02-24 11:54:30 +0000225 if os.path.isdir(mount):
226 if os.path.ismount(mount):
227 print "Path %s is still mounted, please verify" % mount
228 else:
229 print "Removing mount point %s" % mount
230 os.rmdir(mount)
lmr5d73e2f2009-10-09 20:46:36 +0000231
232
233 def setup(self):
234 print "Starting unattended install setup"
235
236 print "Variables set:"
237 print " qemu_img_bin: " + str(self.qemu_img_bin)
238 print " cdrom iso: " + str(self.cdrom_iso)
239 print " unattended_file: " + str(self.unattended_file)
240 print " kernel_args: " + str(self.kernel_args)
241 print " tftp_root: " + str(self.tftp_root)
242 print " floppy_mount: " + str(self.floppy_mount)
243 print " floppy_img: " + str(self.floppy_img)
244 print " finish_program: " + str(self.finish_program)
lmra29a5cb2010-03-18 02:39:34 +0000245 print " pxe_dir: " + str(self.pxe_dir)
246 print " pxe_image: " + str(self.pxe_image)
247 print " pxe_initrd: " + str(self.pxe_initrd)
lmr5d73e2f2009-10-09 20:46:36 +0000248
249 self.create_boot_floppy()
250 if self.tftp_root:
251 self.setup_pxe_boot()
lmr5d73e2f2009-10-09 20:46:36 +0000252 print "Unattended install setup finished successfuly"
253
254
255if __name__ == "__main__":
256 os_install = UnattendedInstall()
257 os_install.setup()