blob: 87a8973f0470a03e14f553d40dcc650668746cce [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
76 c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin, self.floppy_img)
77 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
84 m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
85 if os.system(m_cmd):
86 raise SetupError('Could not mount floppy image.')
87
88 if self.unattended_file.endswith('.sif'):
89 dest_fname = 'winnt.sif'
90 setup_file = 'winnt.bat'
91 setup_file_path = os.path.join(self.unattended_dir, setup_file)
92 setup_file_dest = os.path.join(self.floppy_mount, setup_file)
93 shutil.copyfile(setup_file_path, setup_file_dest)
94 elif self.unattended_file.endswith('.ks'):
95 dest_fname = 'ks.cfg'
lmr17e4dba2010-01-13 17:57:51 +000096 elif self.unattended_file.endswith('.xml'):
97 dest_fname = "autounattend.xml"
lmr5d73e2f2009-10-09 20:46:36 +000098
99 dest = os.path.join(self.floppy_mount, dest_fname)
lmrdc5514b2010-02-04 01:38:20 +0000100
101 # Replace KVM_TEST_CDKEY (in the unattended file) with the cdkey
102 # provided for this test
103 unattended_contents = open(self.unattended_file).read()
104 dummy_cdkey_re = r'\bKVM_TEST_CDKEY\b'
105 real_cdkey = os.environ.get('KVM_TEST_cdkey')
106 if re.search(dummy_cdkey_re, unattended_contents):
107 if real_cdkey:
108 unattended_contents = re.sub(dummy_cdkey_re, real_cdkey,
109 unattended_contents)
110 else:
111 print ("WARNING: 'cdkey' required but not specified for this "
112 "unattended installation")
113
114 # Write the unattended file contents to 'dest'
115 open(dest, 'w').write(unattended_contents)
lmr5d73e2f2009-10-09 20:46:36 +0000116
117 if self.finish_program:
118 dest_fname = os.path.basename(self.finish_program)
119 dest = os.path.join(self.floppy_mount, dest_fname)
120 shutil.copyfile(self.finish_program, dest)
121
122 u_cmd = 'umount %s' % self.floppy_mount
123 if os.system(u_cmd):
124 raise SetupError('Could not unmount floppy at %s.' %
125 self.floppy_mount)
126
127 os.chmod(self.floppy_img, 0755)
128
129 print "Boot floppy created successfuly"
130
131
132 def setup_pxe_boot(self):
133 """
134 Sets up a PXE boot environment using the built in qemu TFTP server.
135 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
136 pxelinux package or equivalent for your distro), and vmlinuz and
137 initrd.img files from the CD to a directory that qemu will serve trough
138 TFTP to the VM.
139 """
140 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
141
142 pxe_file = None
143 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
144 '/usr/share/syslinux/pxelinux.0']
145 for path in pxe_paths:
146 if os.path.isfile(path):
147 pxe_file = path
148 break
149
150 if not pxe_file:
151 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
152 'sure pxelinux or equivalent package for your '
153 'distro is installed.')
154
155 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
156 shutil.copyfile(pxe_file, pxe_dest)
157
lmr39abded2009-10-26 14:22:04 +0000158 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
159 self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000160 if os.system(m_cmd):
161 raise SetupError('Could not mount CD image %s.' % self.cdrom_iso)
162
163 p = os.path.join('images', 'pxeboot')
164 pxe_dir = os.path.join(self.cdrom_mount, p)
165 pxe_image = os.path.join(pxe_dir, 'vmlinuz')
166 pxe_initrd = os.path.join(pxe_dir, 'initrd.img')
167
168 if not os.path.isdir(pxe_dir):
169 raise SetupError('The ISO image does not have a %s dir. The script '
170 'assumes that the cd has a %s dir where to search '
171 'for the vmlinuz image.' % (p, p))
172
173 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
174 raise SetupError('The location %s is lacking either a vmlinuz or a '
175 'initrd.img file. Cannot find a PXE image to '
176 'proceed.' % pxe_dir)
177
178 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
179 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
180 shutil.copyfile(pxe_image, tftp_image)
181 shutil.copyfile(pxe_initrd, tftp_initrd)
182
183 u_cmd = 'umount %s' % self.cdrom_mount
184 if os.system(u_cmd):
185 raise SetupError('Could not unmount CD at %s.' % self.cdrom_mount)
186
187 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
188 if not os.path.isdir(pxe_config_dir):
189 os.makedirs(pxe_config_dir)
190 pxe_config_path = os.path.join(pxe_config_dir, 'default')
191
192 pxe_config = open(pxe_config_path, 'w')
193 pxe_config.write('DEFAULT pxeboot\n')
194 pxe_config.write('TIMEOUT 20\n')
195 pxe_config.write('PROMPT 0\n')
196 pxe_config.write('LABEL pxeboot\n')
197 pxe_config.write(' KERNEL vmlinuz\n')
198 pxe_config.write(' KERNEL vmlinuz\n')
199 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
200 self.kernel_args)
201 pxe_config.close()
202
203 print "PXE boot successfuly set"
204
205 def cleanup(self):
206 """
207 Clean up previously used mount points.
208 """
209 print "Cleaning up unused mount points"
210 for mount in [self.floppy_mount, self.cdrom_mount]:
211 if os.path.isdir(mount):
212 if os.path.ismount(mount):
213 print "Path %s is still mounted, please verify" % mount
214 else:
215 print "Removing mount point %s" % mount
216 os.rmdir(mount)
217
218
219 def setup(self):
220 print "Starting unattended install setup"
221
222 print "Variables set:"
223 print " qemu_img_bin: " + str(self.qemu_img_bin)
224 print " cdrom iso: " + str(self.cdrom_iso)
225 print " unattended_file: " + str(self.unattended_file)
226 print " kernel_args: " + str(self.kernel_args)
227 print " tftp_root: " + str(self.tftp_root)
228 print " floppy_mount: " + str(self.floppy_mount)
229 print " floppy_img: " + str(self.floppy_img)
230 print " finish_program: " + str(self.finish_program)
231
232 self.create_boot_floppy()
233 if self.tftp_root:
234 self.setup_pxe_boot()
235 self.cleanup()
236 print "Unattended install setup finished successfuly"
237
238
239if __name__ == "__main__":
240 os_install = UnattendedInstall()
241 os_install.setup()