blob: 13f431a772e083a8eab474e11696ac7f102411d3 [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)
100 shutil.copyfile(self.unattended_file, dest)
101
102 if self.finish_program:
103 dest_fname = os.path.basename(self.finish_program)
104 dest = os.path.join(self.floppy_mount, dest_fname)
105 shutil.copyfile(self.finish_program, dest)
106
107 u_cmd = 'umount %s' % self.floppy_mount
108 if os.system(u_cmd):
109 raise SetupError('Could not unmount floppy at %s.' %
110 self.floppy_mount)
111
112 os.chmod(self.floppy_img, 0755)
113
114 print "Boot floppy created successfuly"
115
116
117 def setup_pxe_boot(self):
118 """
119 Sets up a PXE boot environment using the built in qemu TFTP server.
120 Copies the PXE Linux bootloader pxelinux.0 from the host (needs the
121 pxelinux package or equivalent for your distro), and vmlinuz and
122 initrd.img files from the CD to a directory that qemu will serve trough
123 TFTP to the VM.
124 """
125 print "Setting up PXE boot using TFTP root %s" % self.tftp_root
126
127 pxe_file = None
128 pxe_paths = ['/usr/lib/syslinux/pxelinux.0',
129 '/usr/share/syslinux/pxelinux.0']
130 for path in pxe_paths:
131 if os.path.isfile(path):
132 pxe_file = path
133 break
134
135 if not pxe_file:
136 raise SetupError('Cannot find PXE boot loader pxelinux.0. Make '
137 'sure pxelinux or equivalent package for your '
138 'distro is installed.')
139
140 pxe_dest = os.path.join(self.tftp_root, 'pxelinux.0')
141 shutil.copyfile(pxe_file, pxe_dest)
142
lmr39abded2009-10-26 14:22:04 +0000143 m_cmd = 'mount -t iso9660 -v -o loop,ro %s %s' % (self.cdrom_iso,
144 self.cdrom_mount)
lmr5d73e2f2009-10-09 20:46:36 +0000145 if os.system(m_cmd):
146 raise SetupError('Could not mount CD image %s.' % self.cdrom_iso)
147
148 p = os.path.join('images', 'pxeboot')
149 pxe_dir = os.path.join(self.cdrom_mount, p)
150 pxe_image = os.path.join(pxe_dir, 'vmlinuz')
151 pxe_initrd = os.path.join(pxe_dir, 'initrd.img')
152
153 if not os.path.isdir(pxe_dir):
154 raise SetupError('The ISO image does not have a %s dir. The script '
155 'assumes that the cd has a %s dir where to search '
156 'for the vmlinuz image.' % (p, p))
157
158 if not os.path.isfile(pxe_image) or not os.path.isfile(pxe_initrd):
159 raise SetupError('The location %s is lacking either a vmlinuz or a '
160 'initrd.img file. Cannot find a PXE image to '
161 'proceed.' % pxe_dir)
162
163 tftp_image = os.path.join(self.tftp_root, 'vmlinuz')
164 tftp_initrd = os.path.join(self.tftp_root, 'initrd.img')
165 shutil.copyfile(pxe_image, tftp_image)
166 shutil.copyfile(pxe_initrd, tftp_initrd)
167
168 u_cmd = 'umount %s' % self.cdrom_mount
169 if os.system(u_cmd):
170 raise SetupError('Could not unmount CD at %s.' % self.cdrom_mount)
171
172 pxe_config_dir = os.path.join(self.tftp_root, 'pxelinux.cfg')
173 if not os.path.isdir(pxe_config_dir):
174 os.makedirs(pxe_config_dir)
175 pxe_config_path = os.path.join(pxe_config_dir, 'default')
176
177 pxe_config = open(pxe_config_path, 'w')
178 pxe_config.write('DEFAULT pxeboot\n')
179 pxe_config.write('TIMEOUT 20\n')
180 pxe_config.write('PROMPT 0\n')
181 pxe_config.write('LABEL pxeboot\n')
182 pxe_config.write(' KERNEL vmlinuz\n')
183 pxe_config.write(' KERNEL vmlinuz\n')
184 pxe_config.write(' APPEND initrd=initrd.img %s\n' %
185 self.kernel_args)
186 pxe_config.close()
187
188 print "PXE boot successfuly set"
189
190 def cleanup(self):
191 """
192 Clean up previously used mount points.
193 """
194 print "Cleaning up unused mount points"
195 for mount in [self.floppy_mount, self.cdrom_mount]:
196 if os.path.isdir(mount):
197 if os.path.ismount(mount):
198 print "Path %s is still mounted, please verify" % mount
199 else:
200 print "Removing mount point %s" % mount
201 os.rmdir(mount)
202
203
204 def setup(self):
205 print "Starting unattended install setup"
206
207 print "Variables set:"
208 print " qemu_img_bin: " + str(self.qemu_img_bin)
209 print " cdrom iso: " + str(self.cdrom_iso)
210 print " unattended_file: " + str(self.unattended_file)
211 print " kernel_args: " + str(self.kernel_args)
212 print " tftp_root: " + str(self.tftp_root)
213 print " floppy_mount: " + str(self.floppy_mount)
214 print " floppy_img: " + str(self.floppy_img)
215 print " finish_program: " + str(self.finish_program)
216
217 self.create_boot_floppy()
218 if self.tftp_root:
219 self.setup_pxe_boot()
220 self.cleanup()
221 print "Unattended install setup finished successfuly"
222
223
224if __name__ == "__main__":
225 os_install = UnattendedInstall()
226 os_install.setup()