blob: ee20b6059081f49fa81bf9d3756b9a6831f9f47d [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']
lmr5d73e2f2009-10-09 20:46:36 +000057 self.cdrom_iso = os.path.join(kvm_test_dir, cdrom_iso)
58 self.floppy_mount = tempfile.mkdtemp(prefix='floppy_', dir='/tmp')
59 self.cdrom_mount = tempfile.mkdtemp(prefix='cdrom_', dir='/tmp')
60 self.floppy_img = os.path.join(images_dir, 'floppy.img')
61
62
63 def create_boot_floppy(self):
64 """
65 Prepares a boot floppy by creating a floppy image file, mounting it and
66 copying an answer file (kickstarts for RH based distros, answer files
67 for windows) to it. After that the image is umounted.
68 """
69 print "Creating boot floppy"
70
71 if os.path.exists(self.floppy_img):
72 os.remove(self.floppy_img)
73
74 c_cmd = '%s create -f raw %s 1440k' % (self.qemu_img_bin, self.floppy_img)
75 if os.system(c_cmd):
76 raise SetupError('Could not create floppy image.')
77
78 f_cmd = 'mkfs.msdos -s 1 %s' % self.floppy_img
79 if os.system(f_cmd):
80 raise SetupError('Error formatting floppy image.')
81
82 m_cmd = 'mount -o loop %s %s' % (self.floppy_img, self.floppy_mount)
83 if os.system(m_cmd):
84 raise SetupError('Could not mount floppy image.')
85
86 if self.unattended_file.endswith('.sif'):
87 dest_fname = 'winnt.sif'
88 setup_file = 'winnt.bat'
89 setup_file_path = os.path.join(self.unattended_dir, setup_file)
90 setup_file_dest = os.path.join(self.floppy_mount, setup_file)
91 shutil.copyfile(setup_file_path, setup_file_dest)
92 elif self.unattended_file.endswith('.ks'):
93 dest_fname = 'ks.cfg'
lmr17e4dba2010-01-13 17:57:51 +000094 elif self.unattended_file.endswith('.xml'):
95 dest_fname = "autounattend.xml"
lmr5d73e2f2009-10-09 20:46:36 +000096
97 dest = os.path.join(self.floppy_mount, dest_fname)
98 shutil.copyfile(self.unattended_file, dest)
99
100 if self.finish_program:
101 dest_fname = os.path.basename(self.finish_program)
102 dest = os.path.join(self.floppy_mount, dest_fname)
103 shutil.copyfile(self.finish_program, dest)
104
105 u_cmd = 'umount %s' % self.floppy_mount
106 if os.system(u_cmd):
107 raise SetupError('Could not unmount floppy at %s.' %
108 self.floppy_mount)
109
110 os.chmod(self.floppy_img, 0755)
111
112 print "Boot floppy created successfuly"
113
114
115 def setup_pxe_boot(self):
116 """
117 Sets up a PXE boot environment using the built in qemu TFTP server.
118 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
119 pxelinux package or equivalent for your distro), and vmlinuz and
120 initrd.img files from the CD to a directory that qemu will serve trough
121 TFTP to the VM.
122 """
123 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
124
125 pxe_file = None
126 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
127 '/usr/share/syslinux/pxelinux.0']
128 for path in pxe_paths:
129 if os.path.isfile(path):
130 pxe_file = path
131 break
132
133 if not pxe_file:
134 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
135 'sure pxelinux or equivalent package for your '
136 'distro is installed.')
137
138 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
139 shutil.copyfile(pxe_file, pxe_dest)
140
lmr39abded2009-10-26 14:22:04 +0000141 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
142 self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000143 if os.system(m_cmd):
144 raise SetupError('Could not mount CD image %s.' % self.cdrom_iso)
145
146 p = os.path.join('images', 'pxeboot')
147 pxe_dir = os.path.join(self.cdrom_mount, p)
148 pxe_image = os.path.join(pxe_dir, 'vmlinuz')
149 pxe_initrd = os.path.join(pxe_dir, 'initrd.img')
150
151 if not os.path.isdir(pxe_dir):
152 raise SetupError('The ISO image does not have a %s dir. The script '
153 'assumes that the cd has a %s dir where to search '
154 'for the vmlinuz image.' % (p, p))
155
156 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
157 raise SetupError('The location %s is lacking either a vmlinuz or a '
158 'initrd.img file. Cannot find a PXE image to '
159 'proceed.' % pxe_dir)
160
161 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
162 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
163 shutil.copyfile(pxe_image, tftp_image)
164 shutil.copyfile(pxe_initrd, tftp_initrd)
165
166 u_cmd = 'umount %s' % self.cdrom_mount
167 if os.system(u_cmd):
168 raise SetupError('Could not unmount CD at %s.' % self.cdrom_mount)
169
170 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
171 if not os.path.isdir(pxe_config_dir):
172 os.makedirs(pxe_config_dir)
173 pxe_config_path = os.path.join(pxe_config_dir, 'default')
174
175 pxe_config = open(pxe_config_path, 'w')
176 pxe_config.write('DEFAULT pxeboot\n')
177 pxe_config.write('TIMEOUT 20\n')
178 pxe_config.write('PROMPT 0\n')
179 pxe_config.write('LABEL pxeboot\n')
180 pxe_config.write(' KERNEL vmlinuz\n')
181 pxe_config.write(' KERNEL vmlinuz\n')
182 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
183 self.kernel_args)
184 pxe_config.close()
185
186 print "PXE boot successfuly set"
187
188 def cleanup(self):
189 """
190 Clean up previously used mount points.
191 """
192 print "Cleaning up unused mount points"
193 for mount in [self.floppy_mount, self.cdrom_mount]:
194 if os.path.isdir(mount):
195 if os.path.ismount(mount):
196 print "Path %s is still mounted, please verify" % mount
197 else:
198 print "Removing mount point %s" % mount
199 os.rmdir(mount)
200
201
202 def setup(self):
203 print "Starting unattended install setup"
204
205 print "Variables set:"
206 print " qemu_img_bin: " + str(self.qemu_img_bin)
207 print " cdrom iso: " + str(self.cdrom_iso)
208 print " unattended_file: " + str(self.unattended_file)
209 print " kernel_args: " + str(self.kernel_args)
210 print " tftp_root: " + str(self.tftp_root)
211 print " floppy_mount: " + str(self.floppy_mount)
212 print " floppy_img: " + str(self.floppy_img)
213 print " finish_program: " + str(self.finish_program)
214
215 self.create_boot_floppy()
216 if self.tftp_root:
217 self.setup_pxe_boot()
218 self.cleanup()
219 print "Unattended install setup finished successfuly"
220
221
222if __name__ == "__main__":
223 os_install = UnattendedInstall()
224 os_install.setup()