/[gli]/branches/overhaul/src/GLIArchitectureTemplate.py
Gentoo

Contents of /branches/overhaul/src/GLIArchitectureTemplate.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1578 - (show annotations) (download) (as text)
Fri Jan 5 03:19:32 2007 UTC (11 years, 6 months ago) by codeman
File MIME type: text/x-python
File size: 87786 byte(s)
	reordered functions.  removed references to the client-config
	fixed fstab and other locations that used mountpoints to use the new mounts.
	removed unused rc.conf function.
	other smaller touchups.

1 """
2 # Copyright 1999-2005 Gentoo Foundation
3 # This source code is distributed under the terms of version 2 of the GNU
4 # General Public License as published by the Free Software Foundation, a copy
5 # of which can be found in the main directory of this project.
6 Gentoo Linux Installer
7
8 $Id: GLIArchitectureTemplate.py,v 1.297 2006/09/15 16:37:11 agaffney Exp $
9
10 The ArchitectureTemplate is largely meant to be an abstract class and an
11 interface (yes, it is both at the same time!). The purpose of this is to create
12 subclasses that populate all the methods with working methods for that architecture.
13 The only definitions that are filled in here are architecture independent.
14
15 """
16
17 import GLIUtility, GLILogger, os, string, sys, shutil, re, time
18 import GLIPortage
19 from GLIException import *
20 import parted
21 import GLIStorageDevice
22
23 MEGABYTE = 1024 * 1024
24 LOGFILE = '/var/log/installer.log'
25 COMPILE_LOGFILE = '/tmp/compile_output.log'
26
27 class ArchitectureTemplate:
28 ##
29 # Initialization of the ArchitectureTemplate. Called from some other arch template.
30 # @param install_profile=None An Install Profile
31 # @param client_controller=None Client Controller. not same as configuration.
32 def __init__(self,install_profile=None, client_controller=None):
33 self._install_profile = install_profile
34 self._cc = client_controller
35
36 # This will get used a lot, so it's probably
37 # better to store it in a variable than to call
38 # this method 100000 times.
39 self._chroot_dir = self._install_profile.get_root_mount_point()
40 self._logger = GLILogger.Logger(LOGFILE)
41 self._compile_logfile = COMPILE_LOGFILE
42 self._debug = self._client_configuration.get_verbose()
43
44 self._portage = GLIPortage.GLIPortage(self._chroot_dir, self._install_profile.get_grp_install(), self._logger, self._debug, self._cc, self._compile_logfile)
45
46 # This will cleanup the logfile if it's a dead link (pointing
47 # to the chroot logfile when partitions aren't mounted, else
48 # no action needs to be taken
49
50 if os.path.islink(self._compile_logfile) and not os.path.exists(self._compile_logfile):
51 os.unlink(self._compile_logfile)
52
53 # cache the list of successfully mounted devices and swap devices here
54 self._mounted_devices = []
55 self._swap_devices = []
56
57 # These must be filled in by the subclass. _steps is a list of
58 # functions, that will carry out the installation. They must be
59 # in order.
60 #
61 # For example, self._steps might be: [preinstall, stage1, stage2, stage3, postinstall],
62 # where each entry is a function (with no arguments) that carries out the desired actions.
63 # Of course, steps will be different depending on the install_profile
64
65 self._architecture_name = "generic"
66 self._install_steps = [
67 # { 'function': self.partition, 'name': "Partition"},
68 { 'function': self.mount_local_partitions, 'name': "Mount local partitions"},
69 { 'function': self.mount_network_shares, 'name': "Mount network (NFS) shares"},
70 { 'function': self.unpack_stage_tarball, 'name': "Unpack stage tarball"},
71 { 'function': self.update_config_files, 'name': "Updating config files"},
72 { 'function': self.configure_make_conf, 'name': "Configure /etc/make.conf"},
73 { 'function': self.prepare_chroot, 'name': "Preparing chroot"},
74 { 'function': self.install_portage_tree, 'name': "Syncing the Portage tree"},
75 { 'function': self.stage1, 'name': "Performing bootstrap"},
76 { 'function': self.stage2, 'name': "Performing 'emerge system'"},
77 { 'function': self.set_root_password, 'name': "Set the root password"},
78 { 'function': self.set_timezone, 'name': "Setting timezone"},
79 { 'function': self.emerge_kernel_sources, 'name': "Emerge kernel sources"},
80 { 'function': self.build_kernel, 'name': "Building kernel"},
81 { 'function': self.install_distcc, 'name': "Install distcc"},
82 { 'function': self.install_mta, 'name': "Installing MTA"},
83 { 'function': self.install_logging_daemon, 'name': "Installing system logger"},
84 { 'function': self.install_cron_daemon, 'name': "Installing Cron daemon"},
85 { 'function': self.install_filesystem_tools, 'name': "Installing filesystem tools"},
86 { 'function': self.setup_network_post, 'name': "Configuring post-install networking"},
87 { 'function': self.install_bootloader, 'name': "Configuring and installing bootloader"},
88 { 'function': self.setup_and_run_bootloader, 'name': "Setting up and running bootloader"},
89 { 'function': self.update_config_files, 'name': "Re-Updating config files"},
90 { 'function': self.set_users, 'name': "Add additional users."},
91 { 'function': self.install_packages, 'name': "Installing additional packages."},
92 # services for startup need to come after installing extra packages
93 # otherwise some of the scripts will not exist.
94 { 'function': self.set_services, 'name': "Setting up services for startup"},
95 { 'function': self.run_post_install_script, 'name': "Running custom post-install script"},
96 { 'function': self.finishing_cleanup, 'name': "Cleanup and unmounting local filesystems."}
97 ]
98
99
100 ##
101 # Returns the steps and their comments in an array
102 def get_install_steps(self):
103 return self._install_steps
104
105 ##
106 # Tells the frontend something
107 # @param type type of data
108 # @param data the data itself. usually a number.
109 def notify_frontend(self, type, data):
110 self._cc.addNotification(type, data)
111
112 # It is possible to override these methods in each Arch Template.
113 # It might be necessary to do so, if the arch needs something 'weird'.
114
115 ######################## Private Functions ########################
116 ##
117 # Private function to add a /etc/init.d/ script to the given runlevel in the chroot environement
118 # @param script_name the script to be added
119 # @param runlevel="default" the runlevel to add to
120 def _add_to_runlevel(self, script_name, runlevel="default"):
121 if not GLIUtility.is_file(self._chroot_dir + '/etc/init.d/' + script_name):
122 #raise GLIException("RunlevelAddError", 'fatal', '_add_to_runlevel', "Failure adding " + script_name + " to runlevel " + runlevel + "!")
123 #This is not a fatal error. If the init script is important it will exist.
124 self._logger.log("ERROR! Failure adding " + script_name + " to runlevel " + runlevel + " because it was not found!")
125 if self._debug: self._logger.log("DEBUG: running rc-update add " + script_name + " " + runlevel + " in chroot.")
126 status = GLIUtility.spawn("rc-update add " + script_name + " " + runlevel, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True)
127 if not GLIUtility.exitsuccess(status):
128 #raise GLIException("RunlevelAddError", 'fatal', '_add_to_runlevel', "Failure adding " + script_name + " to runlevel " + runlevel + "!")
129 #Again, an error here will not prevent a new system from booting. But it is important to log the error.
130 self._logger.log("ERROR! Could not add " + script_name + " to runlevel " + runlevel + ". returned a bad status code.")
131 else:
132 self._logger.log("Added "+script_name+" to runlevel "+runlevel)
133
134 ##
135 # Private Function. Will return a list of packages to be emerged for a given command. Not yet used.
136 # @param cmd full command to run ('/usr/portage/scripts/bootstrap.sh --pretend' or 'emerge -p system')
137 def _get_packages_to_emerge(self, cmd):
138 if self._debug: self._logger.log("DEBUG: _get_packages_to_emerge() called with '%s'" % cmd)
139 return GLIUtility.spawn(cmd + r" 2>/dev/null | grep -e '\[ebuild' | sed -e 's:\[ebuild .\+ \] ::' -e 's: \[.\+\] ::' -e 's: \+$::'", chroot=self._chroot_dir, return_output=True)[1].strip().split("\n")
140
141 ##
142 # Private Function. Will emerge a given package in the chroot environment.
143 # @param package package to be emerged
144 # @param binary=False defines whether to try a binary emerge (if GRP this gets ignored either way)
145 # @param binary_only=False defines whether to only allow binary emerges.
146 def _emerge(self, package, binary=True, binary_only=False):
147 #Error checking of this function is to be handled by the parent function.
148 # self._logger.log("_emerge() called with: package='%s', binary='%s', binary_only='%s', grp_install='%s'" % (package, str(binary), str(binary_only), str(self._install_profile.get_grp_install())))
149 # now short-circuit for GRP
150 if self._install_profile.get_grp_install():
151 cmd="emerge -k " + package
152 # now normal installs
153 else:
154 if binary_only:
155 cmd="emerge -K " + package
156 elif binary:
157 cmd="emerge -k " + package
158 else:
159 cmd="emerge " + package
160
161 self._logger.log("Calling emerge: "+cmd)
162 return GLIUtility.spawn(cmd, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True)
163
164 ##
165 # Private Function. Will edit a config file and insert a value or two overwriting the previous value
166 # (actually it only just comments out the old one)
167 # @param filename file to be edited
168 # @param newvalues a dictionary of VARIABLE:VALUE pairs
169 # @param delimeter='=' what is between the key and the value
170 # @param quotes_around_value=True whether there are quotes around the value or not (ex. "local" vs. localhost)
171 # @param only_value=False Ignore the keys and output only a value.
172 # @param create_file=True Create the file if it does not exist.
173 def _edit_config(self, filename, newvalues, delimeter='=', quotes_around_value=True, only_value=False,create_file=True):
174 # don't use 'file' as a normal variable as it conflicts with the __builtin__.file
175 newvalues = newvalues.copy()
176 if self._debug: self._logger.log("DEBUG: _edit_config() called with " + str(newvalues)+" and flags: "+delimeter + "quotes: "+str(quotes_around_value)+" value: "+str(only_value))
177 if GLIUtility.is_file(filename):
178 f = open(filename)
179 contents = f.readlines()
180 f.close()
181 elif create_file:
182 contents = []
183 else:
184 raise GLIException("NoSuchFileError", 'notice','_edit_config',filename + ' does not exist!')
185
186 for key in newvalues.keys():
187 newline = ""
188 if key == "SPACER":
189 newline = "\n"
190 elif key == "COMMENT":
191 newline = '# ' + newvalues[key] + "\n"
192 elif newvalues[key] == "##comment##" or newvalues[key] == "##commented##":
193 newline = '#' + key + delimeter + '""' + "\n"
194 else:
195 if quotes_around_value:
196 newvalues[key] = '"' + newvalues[key] + '"'
197 #Only the printing of values is required.
198 if only_value:
199 newline = newvalues[key] + "\n"
200 else:
201 newline = key + delimeter + newvalues[key] + "\n"
202 add_at_line = len(contents)
203 for i in range(len(contents)):
204 if newline == contents[i]:
205 break
206 if contents[i].startswith(key + delimeter):
207 contents[i] = "#" + contents[i]
208 add_at_line = i + 1
209 else:
210 contents.insert(add_at_line, newline)
211 if self._debug: self._logger.log("DEBUG: Contents of file "+filename+": "+str(contents))
212 f = open(filename,'w')
213 f.writelines(contents)
214 f.flush()
215 f.close()
216 self._logger.log("Edited Config file "+filename)
217
218 ##
219 # Configures /etc/fstab on the new envorinment
220 def _configure_fstab(self):
221 newfstab = ""
222 mounts = self._install_profile.get_mounts()
223 for mount in mounts:
224 if mount['type'] == "linux-swap":
225 newfstab += mount['devnode']+"\t none swap sw 0 0\n"
226 continue #Stop here and move on.
227 if not mount['mountopts'].strip(): mount['mountopts'] = "defaults"
228 if mount['mountpoint']:
229 if not GLIUtility.is_file(self._chroot_dir+mount['mountpoint']):
230 if self._debug: self._logger.log("DEBUG: making mountpoint: "+mount['mountpoint']) exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mount['mountpoint'])
231 if not GLIUtility.exitsuccess(exitstatus):
232 raise GLIException("MkdirError", 'fatal','configure_fstab', "Making the mount point failed!")
233 newfstab += mount['devnode']+"\t "+mount['mountpoint']+"\t "+mount['type']+"\t "+mount['mountopts']+"\t\t "
234 #Now figure out the last 2 numbers depending on the mountpoint.
235 if mount['mountpoint'] == "/boot":
236 newfstab += "1 2\n"
237 elif mount['mountpoint'] == "/":
238 newfstab += "0 1\n"
239 else:
240 newfstab += "0 0\n"
241
242 newfstab += "none /proc proc defaults 0 0\n"
243 newfstab += "none /dev/shm tmpfs defaults 0 0\n"
244 if GLIUtility.is_device("/dev/cdroms/cdrom0"):
245 newfstab += "/dev/cdroms/cdrom0 /mnt/cdrom auto noauto,user 0 0\n"
246
247 for netmount in self._install_profile.get_network_mounts():
248 if netmount['type'] == "nfs":
249 newfstab += netmount['host'] + ":" + netmount['export'] + "\t" + netmount['mountpoint'] + "\tnfs\t" + netmount['mountopts'] + "\t0 0\n"
250
251 file_name = self._chroot_dir + "/etc/fstab"
252 try:
253 if self._debug: self._logger.log("DEBUG: backing up original fstab")
254 shutil.move(file_name, file_name + ".OLDdefault")
255 except:
256 self._logger.log("ERROR: could not backup original fstab.")
257 if self._debug: self._logger.log("DEBUG: Contents of new fstab: "+newfstab)
258 f = open(file_name, 'w')
259 f.writelines(newfstab)
260 f.close()
261 self._logger.log("fstab configured.")
262
263 #####################################################################
264 #####################################################################
265 ########################## Installation Functions ###################
266 ##
267 # Will grab partition info from the profile and mount all partitions with a specified mountpoint (and swap too)
268 def mount_local_partitions(self):
269 mounts = self._install_profile.get_mounts()
270 for mount in mounts:
271 self._wait_for_device_node(mount['devnode'])
272 mountpoint = mount['mountpoint']
273 mountopts = mount['mountopts']
274 partition_type = mount['type']
275 if not mountpoint:
276 continue #Skip this. Probably should be an error. but should NEVER happen.
277 if mountopts:
278 mountopts = "-o " + mountopts + " "
279 if partition_type == "linux-swap":
280 ret = GLIUtility.spawn("swapon " + mount['devnode'])
281 if not GLIUtility.exitsuccess(ret):
282 self._logger.log("ERROR! : Could not activate swap (" + mount['devnode'] + ")!")
283 else:
284 self._swap_devices.append(mount['devnode'])
285 elif partition_type:
286 if partition_type == "fat32" or partition_type == "fat16":
287 partition_type = "vfat"
288 partition_type = "-t " + partition_type + " "
289 else: #Unknown partition type!!!
290 self._logger.log("ERROR! : Unknown partition type for mountpoint %s which is device %s" % (mountpoint, mount['devnode'])
291 parts_to_mount[mountpoint] = (mountopts, partition_type, mount['devnode'])
292
293 sorted_list = parts_to_mount.keys()
294 sorted_list.sort()
295
296 if not GLIUtility.is_file(self._chroot_dir):
297 if self._debug: self._logger.log("DEBUG: making the chroot dir")
298 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir)
299 if not GLIUtility.exitsuccess(exitstatus):
300 raise GLIException("MkdirError", 'fatal','mount_local_partitions', "Making the ROOT mount point failed!")
301 else:
302 self._logger.log("Created root mount point")
303 for mountpoint in sorted_list:
304 mountopts = parts_to_mount[mountpoint][0]
305 partition_type = parts_to_mount[mountpoint][1]
306 partition = parts_to_mount[mountpoint][2]
307 if not GLIUtility.is_file(self._chroot_dir + mountpoint):
308 if self._debug: self._logger.log("DEBUG: making mountpoint: "+mountpoint)
309 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint)
310 if not GLIUtility.exitsuccess(exitstatus):
311 raise GLIException("MkdirError", 'fatal','mount_local_partitions', "Making the mount point failed!")
312 else:
313 self._logger.log("Created mountpoint " + mountpoint)
314 if self._debug: self._logger.log("DEBUG: running mount "+ partition_type + mountopts + partition + " " + self._chroot_dir + mountpoint)
315 ret = GLIUtility.spawn("mount " + partition_type + mountopts + partition + " " + self._chroot_dir + mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
316 if not GLIUtility.exitsuccess(ret):
317 raise GLIException("MountError", 'fatal','mount_local_partitions','Could not mount a partition')
318 else:
319 self._mounted_devices.append(mountpoint)
320 # double check in /proc/mounts
321 # This current code doesn't work and needs to be fixed, because there is a case that it is needed for - robbat2
322 #ret, output = GLIUtility.spawn('awk \'$2 == "%s" { print "Found" }\' /proc/mounts | head -n1' % (self._chroot_dir + mountpoint), display_on_tty8=True, return_output=True)
323 #if output.strip() != "Found":
324 # raise GLIException("MountError", 'fatal','mount_local_partitions','Could not mount a partition (failed in double-check)')
325 self._logger.log("Mounted mountpoint: " + mountpoint)
326
327 ##
328 # Mounts all network shares to the local machine
329 def mount_network_shares(self):
330 """
331 <agaffney> it'll be much easier than mount_local_partitions
332 <agaffney> make sure /etc/init.d/portmap is started
333 <agaffney> then mount each one: mount -t nfs -o <mountopts> <host>:<export> <mountpoint>
334 """
335 nfsmounts = self._install_profile.get_network_mounts()
336 for netmount in nfsmounts:
337 if netmount['type'] == "NFS" or netmount['type'] == "nfs":
338 mountopts = netmount['mountopts']
339 if mountopts:
340 mountopts = "-o " + mountopts
341 host = netmount['host']
342 export = netmount['export']
343 mountpoint = netmount['mountpoint']
344 if not GLIUtility.is_file(self._chroot_dir + mountpoint):
345 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint)
346 if not GLIUtility.exitsuccess(exitstatus):
347 raise GLIException("MkdirError", 'fatal','mount_network_shares', "Making the mount point failed!")
348 else:
349 if self._debug: self._logger.log("DEBUG: mounting nfs mount")
350 ret = GLIUtility.spawn("mount -t nfs " + mountopts + " " + host + ":" + export + " " + self._chroot_dir + mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
351 if not GLIUtility.exitsuccess(ret):
352 raise GLIException("MountError", 'fatal','mount_network_shares','Could not mount an NFS partition')
353 else:
354 self._logger.log("Mounted netmount at mountpoint: " + mountpoint)
355 self._mounted_devices.append(mountpoint)
356 else:
357 self._logger.log("Netmount type " + netmount['type'] + " not supported...skipping " + netmount['mountpoint'])
358
359 ##
360 # Unpacks the stage tarball that has been specified in the profile (it better be there!)
361 def unpack_stage_tarball(self):
362 if not os.path.isdir(self._chroot_dir):
363 if self._debug: self._logger.log("DEBUG: making the chroot dir:"+self._chroot_dir)
364 os.makedirs(self._chroot_dir)
365 if self._install_profile.get_install_stage() == 3 and self._install_profile.get_dynamic_stage3():
366 # stage3 generation code here
367 if not GLIUtility.is_file("/usr/livecd/systempkgs.txt"):
368 raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Required file /usr/livecd/systempkgs.txt does not exist")
369 try:
370 syspkgs = open("/usr/livecd/systempkgs.txt", "r")
371 systempkgs = syspkgs.readlines()
372 syspkgs.close()
373 except:
374 raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Could not open /usr/livecd/systempkgs.txt")
375
376 # Pre-create /lib (and possibly /lib32 and /lib64)
377 for libdir in ("/lib", "/usr/lib"):
378 if os.path.islink(libdir) and os.readlink(libdir).endswith("64"):
379 if self._debug: self._logger.log("DEBUG: unpack_stage_tarball(): precreating " + libdir + "64 dir and " + libdir + " -> lib64 symlink because glibc/portage sucks")
380 if not GLIUtility.exitsuccess(GLIUtility.spawn("mkdir -p " + self._chroot_dir + libdir + "64 && ln -sf lib64 " + self._chroot_dir + libdir)):
381 raise GLIException("CreateStage3Error", "fatal", "unpack_stage_tarball", "Could not precreate " + libdir + "64 dir and " + libdir + " -> lib64 symlink")
382
383 syspkglen = len(systempkgs)
384 for i, pkg in enumerate(systempkgs):
385 pkg = pkg.strip()
386 self.notify_frontend("progress", (float(i) / (syspkglen+1), "Copying " + pkg + " (" + str(i+1) + "/" + str(syspkglen) + ")"))
387 self._portage.copy_pkg_to_chroot(pkg, True, ignore_missing=True)
388 self.notify_frontend("progress", (float(syspkglen) / (syspkglen+1), "Finishing"))
389 GLIUtility.spawn("cp /etc/make.conf " + self._chroot_dir + "/etc/make.conf")
390 GLIUtility.spawn("ln -s `readlink /etc/make.profile` " + self._chroot_dir + "/etc/make.profile")
391 GLIUtility.spawn("cp -f /etc/inittab.old " + self._chroot_dir + "/etc/inittab")
392
393 # Nasty, nasty, nasty hack because vapier is a tool
394 for tmpfile in ("/etc/passwd", "/etc/group", "/etc/shadow"):
395 GLIUtility.spawn("grep -ve '^gentoo' " + tmpfile + " > " + self._chroot_dir + tmpfile)
396
397 chrootscript = r"""
398 #!/bin/bash
399
400 source /etc/make.conf
401 export LDPATH="/usr/lib/gcc-lib/${CHOST}/$(cd /usr/lib/gcc-lib/${CHOST} && ls -1 | head -n 1)"
402
403 ldconfig $LDPATH
404 gcc-config 1
405 env-update
406 source /etc/profile
407 modules-update
408 [ -f /usr/bin/binutils-config ] && binutils-config 1
409 source /etc/profile
410 #mount -t proc none /proc
411 #cd /dev
412 #/sbin/MAKEDEV generic-i386
413 #umount /proc
414 [ -f /lib/udev-state/devices.tar.bz2 ] && tar -C /dev -xjf /lib/udev-state/devices.tar.bz2
415 """
416 script = open(self._chroot_dir + "/tmp/extrastuff.sh", "w")
417 script.write(chrootscript)
418 script.close()
419 GLIUtility.spawn("chmod 755 /tmp/extrastuff.sh && /tmp/extrastuff.sh", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
420 GLIUtility.spawn("rm -rf /var/tmp/portage/* /usr/portage /tmp/*", chroot=self._chroot_dir)
421 self.notify_frontend("progress", (1, "Done"))
422 self._logger.log("Stage3 was generated successfully")
423 else:
424 self._logger.log("Fetching and unpacking tarball: "+self._install_profile.get_stage_tarball_uri())
425 GLIUtility.fetch_and_unpack_tarball(self._install_profile.get_stage_tarball_uri(), self._chroot_dir, temp_directory=self._chroot_dir, keep_permissions=True, cc=self._cc)
426 self._logger.log(self._install_profile.get_stage_tarball_uri()+" was fetched and unpacked.")
427
428 ##
429 # This runs etc-update and then re-overwrites the files by running the configure_*'s to keep our values.
430 def update_config_files(self):
431 "Runs etc-update (overwriting all config files), then re-configures the modified ones"
432 # Run etc-update overwriting all config files
433 if self._debug: self._logger.log("DEBUG: update_config_files(): running: "+'echo "-5" | chroot '+self._chroot_dir+' etc-update')
434 status = GLIUtility.spawn('echo "-5" | chroot '+self._chroot_dir+' etc-update', display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
435 if not GLIUtility.exitsuccess(status):
436 self._logger.log("ERROR! : Could not update the config files!")
437 else:
438 self._configure_fstab()
439 etc_files = self._install_profile.get_etc_files()
440 for etc_file in etc_files:
441 # Skip entries with blank filenames
442 if not etc_file: continue
443 if self._debug: self._logger.log("DEBUG: update_config_files(): updating config file: "+etc_file)
444 if isinstance(etc_files[etc_file], dict):
445 self._edit_config(self._chroot_dir + "/etc/" + etc_file, etc_files[etc_file])
446 else:
447 for entry in etc_files[etc_file]:
448 # Skip blank entries
449 if not entry: continue
450 self._edit_config(self._chroot_dir + "/etc/" + etc_file, { "0": entry }, only_value=True)
451 self._logger.log("Config files updated using etc-update. make.conf/fstab/rc.conf restored.")
452
453 ##
454 # Configures the new /etc/make.conf
455 def configure_make_conf(self):
456 # Get make.conf options
457 make_conf = self._install_profile.get_make_conf()
458
459 # For each configuration option...
460 filename = self._chroot_dir + "/etc/make.conf"
461 # self._edit_config(filename, {"COMMENT": "GLI additions ===>"})
462 for key in make_conf.keys():
463 # Add/Edit it into make.conf
464 self._edit_config(filename, {key: make_conf[key]})
465 # self._edit_config(filename, {"COMMENT": "<=== End GLI additions"})
466
467 self._logger.log("Make.conf configured")
468 # now make any directories that emerge needs, otherwise it will fail
469 # this must take place before ANY calls to emerge.
470 # otherwise emerge will fail (for PORTAGE_TMPDIR anyway)
471 # defaults first
472 # this really should use portageq or something.
473 PKGDIR = '/usr/portage/packages'
474 PORTAGE_TMPDIR = '/var/tmp'
475 PORT_LOGDIR = None
476 PORTDIR_OVERLAY = None
477 # now other stuff
478 if 'PKGDIR' in make_conf: PKGDIR = make_conf['PKGDIR']
479 if 'PORTAGE_TMPDIR' in make_conf: PORTAGE_TMPDIR = make_conf['PORTAGE_TMPDIR']
480 if 'PORT_LOGDIR' in make_conf: PORT_LOGDIR = make_conf['PORT_LOGDIR']
481 if 'PORTDIR_OVERLAY' in make_conf: PORTDIR_OVERLAY = make_conf['PORTDIR_OVERLAY']
482 if self._debug: self._logger.log("DEBUG: making PKGDIR if necessary: "+PKGDIR)
483 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PKGDIR, logfile=self._compile_logfile, append_log=True)
484 if self._debug: self._logger.log("DEBUG: making PORTAGE_TMPDIR if necessary: "+PORTAGE_TMPDIR)
485 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTAGE_TMPDIR, logfile=self._compile_logfile, append_log=True)
486 if PORT_LOGDIR != None:
487 if self._debug: self._logger.log("DEBUG: making PORT_LOGDIR if necessary: "+PORT_LOGDIR)
488 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORT_LOGDIR, logfile=self._compile_logfile, append_log=True)
489 if PORTDIR_OVERLAY != None:
490 if self._debug: self._logger.log("DEBUG: making PORTDIR_OVERLAY if necessary "+PORTDIR_OVERLAY)
491 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTDIR_OVERLAY, logfile=self._compile_logfile, append_log=True)
492
493 ##
494 # Prepares the Chroot environment by copying /etc/resolv.conf and mounting proc and dev
495 def prepare_chroot(self):
496 # Copy resolv.conf to new env
497 try:
498 if self._debug: self._logger.log("DEBUG: copying /etc/resolv.conf over.")
499 shutil.copy("/etc/resolv.conf", self._chroot_dir + "/etc/resolv.conf")
500 except:
501 pass
502 if self._debug: self._logger.log("DEBUG: mounting proc")
503 ret = GLIUtility.spawn("mount -t proc none "+self._chroot_dir+"/proc")
504 if not GLIUtility.exitsuccess(ret):
505 raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount /proc')
506 else:
507 self._mounted_devices.append("/proc")
508 bind_mounts = [ '/dev' ]
509 uname = os.uname()
510 if uname[0] == 'Linux' and uname[2].split('.')[1] == '6':
511 bind_mounts.append('/sys')
512 if self._debug: self._logger.log("DEBUG: bind-mounting " + ", ".join(bind_mounts))
513 for mount in bind_mounts:
514 ret = GLIUtility.spawn('mount -o bind %s %s%s' % (mount,self._chroot_dir,mount))
515 if not GLIUtility.exitsuccess(ret):
516 raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount '+mount)
517 else:
518 self._mounted_devices.append(mount)
519 if self._debug: self._logger.log("DEBUG: copying logfile to new system!")
520 GLIUtility.spawn("mv " + self._compile_logfile + " " + self._chroot_dir + self._compile_logfile + " && ln -s " + self._chroot_dir + self._compile_logfile + " " + self._compile_logfile)
521 self._logger.log("Chroot environment ready.")
522
523 ##
524 # This will get/update the portage tree. If you want to snapshot or mount /usr/portage use "custom".
525 def install_portage_tree(self):
526 # Check the type of portage tree fetching we'll do
527 # If it is custom, follow the path to the custom tarball and unpack it
528
529 # This is a hack to copy the LiveCD's rsync into the chroot since it has the sigmask patch
530 if self._debug: self._logger.log("DEBUG: Doing the hack where we copy the LiveCD's rsync into the chroot since it has the sigmask patch")
531 GLIUtility.spawn("cp -a /usr/bin/rsync " + self._chroot_dir + "/usr/bin/rsync")
532 GLIUtility.spawn("cp -a /usr/lib/libpopt* " + self._chroot_dir + "/usr/lib")
533
534 sync_type = self._install_profile.get_portage_tree_sync_type()
535 if sync_type == "snapshot" or sync_type == "custom": # Until this is finalized
536
537 # Get portage tree info
538 portage_tree_snapshot_uri = self._install_profile.get_portage_tree_snapshot_uri()
539 if portage_tree_snapshot_uri:
540 # Fetch and unpack the tarball
541 if self._debug: self._logger.log("DEBUG: grabbing custom snapshot uri: "+portage_tree_snapshot_uri)
542 GLIUtility.fetch_and_unpack_tarball(portage_tree_snapshot_uri, self._chroot_dir + "/usr/", self._chroot_dir + "/", cc=self._cc)
543 if GLIUtility.is_file("/usr/livecd/metadata.tar.bz2"):
544 GLIUtility.fetch_and_unpack_tarball("/usr/livecd/metadata.tar.bz2", self._chroot_dir + "/", self._chroot_dir + "/", cc=self._cc)
545 self._logger.log("Portage tree install was custom.")
546 elif sync_type == "sync":
547 if self._debug: self._logger.log("DEBUG: starting emerge sync")
548 exitstatus = GLIUtility.spawn("emerge sync", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
549 if not GLIUtility.exitsuccess(exitstatus):
550 self._logger.log("ERROR! Could not sync the portage tree using emerge sync. Falling back to emerge-webrsync as a backup.")
551 sync_type = "webrsync"
552 else:
553 self._logger.log("Portage tree sync'd")
554 # If the type is webrsync, then run emerge-webrsync
555 elif sync_type == "webrsync":
556 if self._debug: self._logger.log("DEBUG: starting emerge webrsync")
557 exitstatus = GLIUtility.spawn("emerge-webrsync", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
558 if not GLIUtility.exitsuccess(exitstatus):
559 raise GLIException("EmergeWebRsyncError", 'fatal','install_portage_tree', "Failed to retrieve portage tree using webrsync!")
560 self._logger.log("Portage tree sync'd using webrsync")
561 # Otherwise, spit out a message because its probably a bad thing.
562 else:
563 self._logger.log("NOTICE! No valid portage tree sync method was selected. This will most likely result in a failed installation unless the tree is mounted.")
564
565
566 ##
567 # Stage 1 install -- bootstraping the system
568 # If we are doing a stage 1 install, then bootstrap
569 def stage1(self):
570 if self._install_profile.get_install_stage() == 1:
571 self._logger.mark()
572 self._logger.log("Starting bootstrap.")
573 pkgs = self._get_packages_to_emerge("/usr/portage/scripts/bootstrap.sh --pretend")
574 if self._debug: self._logger.log("DEBUG: Packages to emerge: "+str(pkgs)+". Now running bootstrap.sh")
575 exitstatus = GLIUtility.spawn("env-update && source /etc/profile && /usr/portage/scripts/bootstrap.sh", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
576 if not GLIUtility.exitsuccess(exitstatus):
577 raise GLIException("Stage1Error", 'fatal','stage1', "Bootstrapping failed!")
578 self._logger.log("Bootstrap complete.")
579
580 ##
581 # Stage 2 install -- emerge -e system
582 # If we are doing a stage 1 or 2 install, then emerge system
583 def stage2(self):
584 if self._install_profile.get_install_stage() in [ 1, 2 ]:
585 self._logger.mark()
586 self._logger.log("Starting emerge system.")
587 pkgs = self._get_packages_to_emerge("emerge -p system") #currently quite the useless
588 if self._debug: self._logger.log("DEBUG: Packages to emerge: "+str(pkgs)+"/ Now running emerge --emptytree system")
589 # exitstatus = self._emerge("--emptytree system")
590 exitstatus = GLIUtility.spawn("env-update && source /etc/profile && emerge -e system", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
591 if not GLIUtility.exitsuccess(exitstatus):
592 raise GLIException("Stage2Error", 'fatal','stage2', "Building the system failed!")
593 self._logger.log("Emerge system complete.")
594
595 ##
596 # Sets the root password
597 def set_root_password(self):
598 if self._debug: self._logger.log("DEBUG: set_root_password(): running: "+ 'echo \'root:' + self._install_profile.get_root_pass_hash() + '\' | chroot '+self._chroot_dir+' chpasswd -e')
599 status = GLIUtility.spawn('echo \'root:' + self._install_profile.get_root_pass_hash() + '\' | chroot '+self._chroot_dir+' chpasswd -e')
600 if not GLIUtility.exitsuccess(status):
601 raise GLIException("SetRootPasswordError", 'fatal', 'set_root_password', "Failure to set root password!")
602 self._logger.log("Root Password set on the new system.")
603
604 ##
605 # Sets the timezone for the new environment
606 def set_timezone(self):
607
608 # Set symlink
609 if os.access(self._chroot_dir + "/etc/localtime", os.W_OK):
610 if self._debug: self._logger.log("DEBUG: /etc/localtime already exists, removing it so it can be symlinked")
611 GLIUtility.spawn("rm "+self._chroot_dir + "/etc/localtime")
612 if self._debug: self._logger.log("DEBUG: running ln -s ../usr/share/zoneinfo/" + self._install_profile.get_time_zone() + " /etc/localtime")
613 GLIUtility.spawn("ln -s ../usr/share/zoneinfo/" + self._install_profile.get_time_zone() + " /etc/localtime", chroot=self._chroot_dir)
614 if not (self._install_profile.get_time_zone() == "UTC"):
615 if self._debug: self._logger.log("DEBUG: timezone was not UTC, setting CLOCK to local. This may be overwritten later.")
616 self._edit_config(self._chroot_dir + "/etc/conf.d/clock", {"CLOCK":"local"})
617 self._logger.log("Timezone set.")
618
619 ##
620 # Fetches desired kernel sources, unless you're using a livecd-kernel in which case it does freaky stuff.
621 def emerge_kernel_sources(self):
622 self._logger.log("Starting emerge_kernel")
623 kernel_pkg = self._install_profile.get_kernel_source_pkg()
624 # if kernel_pkg:
625 # Special case, no kernel installed
626 if kernel_pkg == "none":
627 return
628 # Special case, livecd kernel
629 elif kernel_pkg == "livecd-kernel":
630 if self._debug: self._logger.log("DEBUG: starting livecd-kernel setup")
631 self.notify_frontend("progress", (0, "Copying livecd-kernel to chroot"))
632 self._portage.copy_pkg_to_chroot(self._portage.get_best_version_vdb("livecd-kernel"))
633 self.notify_frontend("progress", (1, "Done copying livecd-kernel to chroot"))
634
635 exitstatus = self._portage.emerge("coldplug")
636 self._logger.log("Coldplug emerged. Now they should be added to the boot runlevel.")
637 self._add_to_runlevel("coldplug", runlevel="boot")
638
639 if self._install_profile.get_kernel_bootsplash():
640 self._logger.log("Bootsplash enabled for livecd-kernel...this is currently broken, so we're skipping the package install")
641 # self._logger.log("Bootsplash enabled...emerging necessary packages")
642 # self._portage.emerge(["splashutils", "splash-themes-livecd"])
643
644 # Extra modules from kernelpkgs.txt...disabled until I can figure out why it sucks
645 # try:
646 # kernpkgs = open("/usr/livecd/kernelpkgs.txt", "r")
647 # pkgs = ""
648 # for line in kernpkgs.readlines():
649 # pkgs += line.strip() + " "
650 # kernpkgs.close()
651 # except:
652 # raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not read kernelpkgs.txt")
653 # exitstatus = self._emerge(pkgs)
654 # if not GLIUtility.exitsuccess(exitstatus):
655 # raise GLIException("EmergeExtraKernelModulesError", 'fatal','build_kernel', "Could not emerge extra kernel packages")
656 # self._logger.log("Extra kernel packages emerged.")
657
658 # normal case
659 else:
660 exitstatus = self._portage.emerge(kernel_pkg)
661 # if not GLIUtility.exitsuccess(exitstatus):
662 # raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not retrieve kernel sources!")
663 try:
664 os.stat(self._chroot_dir + "/usr/src/linux")
665 except:
666 kernels = os.listdir(self._chroot_dir+"/usr/src")
667 if self._debug: self._logger.log("DEBUG: no /usr/src/linux found. found kernels: "+kernels)
668 found_a_kernel = False
669 counter = 0
670 while not found_a_kernel:
671 if (len(kernels[counter]) > 6) and (kernels[counter][0:6]=="linux-"):
672 if self._debug: self._logger.log("DEBUG: found one. linking it. running: ln -s /usr/src/"+kernels[counter]+ " /usr/src/linux in the chroot.")
673 exitstatus = GLIUtility.spawn("ln -s /usr/src/"+kernels[counter]+ " /usr/src/linux",chroot=self._chroot_dir)
674 if not GLIUtility.exitsuccess(exitstatus):
675 raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not make a /usr/src/linux symlink")
676 found_a_kernel = True
677 else:
678 counter = counter + 1
679 self._logger.log("Kernel sources:"+kernel_pkg+" emerged and /usr/src/linux symlinked.")
680
681 ##
682 # Builds the kernel using genkernel or regularly if given a custom .config file in the profile
683 def build_kernel(self):
684 self._logger.mark()
685 self._logger.log("Starting build_kernel")
686
687 build_mode = self._install_profile.get_kernel_build_method()
688
689 # No building necessary if using the LiveCD's kernel/initrd
690 # or using the 'none' kernel bypass
691 if self._install_profile.get_kernel_source_pkg() in ["livecd-kernel","none"]:
692 if self._debug: self._logger.log("DEBUG: using "+self._install_profile.get_kernel_source_pkg()+ " so skipping this function.")
693 return
694 # Get the uri to the kernel config
695 kernel_config_uri = self._install_profile.get_kernel_config_uri()
696
697 # is there an easier way to do this?
698 if self._debug: self._logger.log("DEBUG: running command: awk '/^PATCHLEVEL/{print $3}' /usr/src/linux/Makefile in chroot.")
699 ret, kernel_major = GLIUtility.spawn("awk '/^PATCHLEVEL/{print $3}' /usr/src/linux/Makefile",chroot=self._chroot_dir,return_output=True)
700 # 6 == 2.6 kernel, 4 == 2.4 kernel
701 kernel_major = int(kernel_major)
702 if self._debug: self._logger.log("DEBUG: kernel major version is: "+str(kernel_major))
703 #Copy the kernel .config to the proper location in /usr/src/linux
704 if kernel_config_uri != '':
705 try:
706 if self._debug: self._logger.log("DEBUG: grabbing kernel config from "+kernel_config_uri+" and putting it in "+self._chroot_dir + "/var/tmp/kernel_config")
707 GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/var/tmp/kernel_config")
708 except:
709 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not copy kernel config!")
710
711 # the && stuff is important so that we can catch any errors.
712 kernel_compile_script = "#!/bin/bash\n"
713 kernel_compile_script += "cp /var/tmp/kernel_config /usr/src/linux/.config && "
714 kernel_compile_script += "cd /usr/src/linux && "
715 # required for 2.[01234] etc kernels
716 if kernel_major in [0,1,2,3,4]:
717 kernel_compile_script += " yes 'n' | make oldconfig && make symlinks && make dep"
718 # not strictly needed, but recommended by upstream
719 else: #elif kernel_major in [5,6]:
720 kernel_compile_script += "make prepare"
721
722 # bypass to install a kernel, but not compile it
723 if build_mode == "none":
724 return
725 # this mode is used to install kernel sources, and have then configured
726 # but not actually build the kernel. This is needed for netboot
727 # situations when you have packages that require kernel sources
728 # to build.
729 elif build_mode == "prepare-only":
730 if self._debug: self._logger.log("DEBUG: writing kernel script with contents: "+kernel_compile_script)
731 f = open(self._chroot_dir+"/var/tmp/kernel_script", 'w')
732 f.writelines(kernel_compile_script)
733 f.close()
734 #Build the kernel
735 if self._debug: self._logger.log("DEBUG: running: chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script")
736 exitstatus1 = GLIUtility.spawn("chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script")
737 if self._debug: self._logger.log("DEBUG: running: /var/tmp/kernel_script in chroot.")
738 exitstatus2 = GLIUtility.spawn("/var/tmp/kernel_script", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
739 if not GLIUtility.exitsuccess(exitstatus1):
740 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not handle prepare-only build! died on chmod.")
741 if not GLIUtility.exitsuccess(exitstatus2):
742 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not handle prepare-only build! died on running of kernel script.")
743 #i'm sure i'm forgetting something here.
744 #cleanup
745 exitstatus = GLIUtility.spawn("rm -f "+self._chroot_dir+"/var/tmp/kernel_script "+self._chroot_dir+"/var/tmp/kernel_config")
746 #it's not important if this fails.
747 self._logger.log("prepare-only build complete")
748 # Genkernel mode, including custom kernel_config. Initrd always on.
749 elif build_mode == "genkernel":
750 if self._debug: self._logger.log("DEBUG: build_kernel(): starting emerge genkernel")
751 exitstatus = self._portage.emerge("genkernel")
752 # if not GLIUtility.exitsuccess(exitstatus):
753 # raise GLIException("EmergeGenKernelError", 'fatal','build_kernel', "Could not emerge genkernel!")
754 self._logger.log("Genkernel emerged. Beginning kernel compile.")
755 # Null the genkernel_options
756 genkernel_options = ""
757
758 # If the uri for the kernel config is not null, then
759 if kernel_config_uri != "":
760 if self._debug: self._logger.log("DEBUG: build_kernel(): getting kernel config "+kernel_config_uri)
761 GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/var/tmp/kernel_config")
762 genkernel_options = genkernel_options + " --kernel-config=/var/tmp/kernel_config"
763
764 # Decide whether to use bootsplash or not
765 if self._install_profile.get_kernel_bootsplash():
766 genkernel_options = genkernel_options + " --gensplash"
767 else:
768 genkernel_options = genkernel_options + " --no-gensplash"
769 # Run genkernel in chroot
770 #print "genkernel all " + genkernel_options
771 if self._debug: self._logger.log("DEBUG: build_kernel(): running: genkernel all " + genkernel_options + " in chroot.")
772 exitstatus = GLIUtility.spawn("genkernel all " + genkernel_options, chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
773 if not GLIUtility.exitsuccess(exitstatus):
774 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build kernel!")
775
776 # exitstatus = self._emerge("hotplug")
777 # if not GLIUtility.exitsuccess(exitstatus):
778 # raise GLIException("EmergeHotplugError", 'fatal','build_kernel', "Could not emerge hotplug!")
779 # self._logger.log("Hotplug emerged.")
780 exitstatus = self._portage.emerge("coldplug")
781 # if not GLIUtility.exitsuccess(exitstatus):
782 # raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not emerge coldplug!")
783 self._logger.log("Coldplug emerged. Now they should be added to the default runlevel.")
784
785 # self._add_to_runlevel("hotplug")
786 self._add_to_runlevel("coldplug", runlevel="boot")
787
788 if self._install_profile.get_kernel_bootsplash():
789 self._logger.log("Bootsplash enabled...emerging necessary packages")
790 self._portage.emerge(["splashutils", "splash-themes-livecd"])
791
792 self._logger.log("Genkernel complete.")
793 elif build_mode == "custom": #CUSTOM CONFIG
794
795 kernel_compile_script += " && make && make modules && make modules_install"
796
797 #Ok now that it's built, copy it to /boot/kernel-* for bootloader code to find it
798 if self._client_configuration.get_architecture_template() == "x86":
799 kernel_compile_script += " && cp /usr/src/linux/arch/i386/boot/bzImage /boot/kernel-custom\n"
800 elif self._client_configuration.get_architecture_template() == "amd64":
801 kernel_compile_script += " && cp /usr/src/linux/arch/x86_64/boot/bzImage /boot/kernel-custom\n"
802 elif self._client_configuration.get_architecture_template() == "ppc":
803 kernel_compile_script += " && cp /usr/src/linux/vmlinux /boot/kernel-custom\n"
804 if self._debug: self._logger.log("DEBUG: build_kernel(): writing custom kernel script: "+kernel_compile_script)
805 f = open(self._chroot_dir+"/var/tmp/kernel_script", 'w')
806 f.writelines(kernel_compile_script)
807 f.close()
808 #Build the kernel
809 if self._debug: self._logger.log("DEBUG: build_kernel(): running: chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script")
810 exitstatus1 = GLIUtility.spawn("chmod u+x "+self._chroot_dir+"/var/tmp/kernel_script")
811 if self._debug: self._logger.log("DEBUG: build_kernel(): running: /var/tmp/kernel_script in chroot")
812 exitstatus2 = GLIUtility.spawn("/var/tmp/kernel_script", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
813 if not GLIUtility.exitsuccess(exitstatus1):
814 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build custom kernel! died on chmod.")
815 if not GLIUtility.exitsuccess(exitstatus2):
816 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build custom kernel! died on running of kernel script.")
817
818 #i'm sure i'm forgetting something here.
819 #cleanup
820 exitstatus = GLIUtility.spawn("rm -f "+self._chroot_dir+"/var/tmp/kernel_script "+self._chroot_dir+"/var/tmp/kernel_config")
821 #it's not important if this fails.
822
823 if self._install_profile.get_kernel_bootsplash():
824 self._logger.log("Bootsplash enabled...emerging necessary packages")
825 self._portage.emerge(["splashutils", "splash-themes-livecd"])
826
827 self._logger.log("Custom kernel complete")
828
829 ##
830 # Installs and starts up distccd if the user has it set, so that it will get used for the rest of the install
831 def install_distcc(self):
832 if self._install_profile.get_install_distcc():
833 if self._debug: self._logger.log("DEBUG: install_distcc(): we ARE installing distcc")
834 if self._debug: self._logger.log("DEBUG: install_distcc(): running: USE='-*' emerge --nodeps sys-devel/distcc in chroot.")
835 exitstatus = GLIUtility.spawn("USE='-*' emerge --nodeps sys-devel/distcc", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
836 if not GLIUtility.exitsuccess(exitstatus):
837 self._logger.log("ERROR! : Could not emerge distcc!")
838 else:
839 self._logger.log("distcc emerged.")
840
841 ##
842 # Installs mail MTA. Does not put into runlevel, as this is not simple with MTAs.
843 def install_mta(self):
844 # Get MTA info
845 mta_pkg = self._install_profile.get_mta_pkg()
846 if mta_pkg:
847 # Emerge MTA
848 if self._debug: self._logger.log("DEBUG: install_mta(): installing mta: "+mta_pkg)
849 exitstatus = self._portage.emerge(mta_pkg)
850 # if not GLIUtility.exitsuccess(exitstatus):
851 # raise GLIException("MTAError", 'fatal','install_mta', "Could not emerge " + mta_pkg + "!")
852 self._logger.log("MTA installed: "+mta_pkg)
853 else:
854 installpackages = self._install_profile.get_install_packages()
855 if installpackages:
856 for pkg in installpackages:
857 if pkg in ['esmtp', 'exim', 'msmtp', 'nbsmtp', 'nullmailer', 'sendmail', 'ssmtp', 'xmail']:
858 self._logger.log("Found an mta in the package list: "+pkg+". Installing early.")
859 exitstatus = self._portage.emerge(pkg)
860 self._logger.log("MTA installed.")
861 break # We only want to install one
862
863 ##
864 # Installs and sets up logging daemon on the new system. adds to runlevel too.
865 def install_logging_daemon(self):
866 # Get loggin daemon info
867 logging_daemon_pkg = self._install_profile.get_logging_daemon_pkg()
868 if logging_daemon_pkg:
869 # Emerge Logging Daemon
870 if self._debug: self._logger.log("DEBUG: install_logging_daemon: emerging "+logging_daemon_pkg)
871 exitstatus = self._portage.emerge(logging_daemon_pkg)
872 # if not GLIUtility.exitsuccess(exitstatus):
873 # raise GLIException("LoggingDaemonError", 'fatal','install_logging_daemon', "Could not emerge " + logging_daemon_pkg + "!")
874
875 # Add Logging Daemon to default runlevel
876 # After we find the name of it's initscript
877 # This current code is a hack, and should be better.
878 initscript = logging_daemon_pkg[(logging_daemon_pkg.find('/')+1):]
879 if self._debug: self._logger.log("DEBUG: install_logging_daemon: adding "+initscript+" to runlevel")
880 self._add_to_runlevel(initscript)
881 self._logger.log("Logging daemon installed: "+logging_daemon_pkg)
882 ##
883 # Installs and sets up cron package.
884 def install_cron_daemon(self):
885 # Get cron daemon info
886 cron_daemon_pkg = self._install_profile.get_cron_daemon_pkg()
887 if cron_daemon_pkg:
888 if cron_daemon_pkg == "none":
889 self._logger.log("Skipping installation of cron daemon")
890 else:
891 # Emerge Cron Daemon
892 if self._debug: self._logger.log("DEBUG: install_cron_daemon: emerging "+cron_daemon_pkg)
893 exitstatus = self._portage.emerge(cron_daemon_pkg)
894 # if not GLIUtility.exitsuccess(exitstatus):
895 # raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Could not emerge " + cron_daemon_pkg + "!")
896
897 # Add Cron Daemon to default runlevel
898 # After we find the name of it's initscript
899 # This current code is a hack, and should be better.
900 initscript = cron_daemon_pkg[(cron_daemon_pkg.find('/')+1):]
901 if self._debug: self._logger.log("DEBUG: install_cron_daemon: adding "+initscript+" to runlevel")
902 self._add_to_runlevel(initscript)
903
904 # If the Cron Daemon is not vixie-cron, run crontab
905 if "vixie-cron" not in cron_daemon_pkg:
906 if self._debug: self._logger.log("DEBUG: install_cron_daemon: running: crontab /etc/crontab in chroot.")
907 exitstatus = GLIUtility.spawn("crontab /etc/crontab", chroot=self._chroot_dir, display_on_tty8=True)
908 if not GLIUtility.exitsuccess(exitstatus):
909 raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Failure making crontab!")
910 self._logger.log("Cron daemon installed and configured: "+cron_daemon_pkg)
911
912 ##
913 # This will parse the partitions looking for types that require fstools and emerge them if found.
914 def install_filesystem_tools(self):
915 "Installs and sets up fstools"
916 # Get the list of file system tools to be installed
917 parts = self._install_profile.get_partition_tables()
918 # don't use an array, use a set instead
919 filesystem_types = []
920 for device in parts:
921 tmp_partitions = parts[device] #.get_install_profile_structure()
922 for partition in tmp_partitions: #.get_ordered_partition_list():
923 partition_type = tmp_partitions[partition]['type'].lower()
924 if tmp_partitions[partition]['mountpoint'] and partition_type not in filesystem_types:
925 filesystem_types.append(partition_type)
926
927 package_list = []
928 for filesystem in filesystem_types:
929 if filesystem == 'xfs':
930 package_list.append('sys-fs/xfsprogs')
931 elif filesystem == 'reiserfs':
932 package_list.append('sys-fs/reiserfsprogs')
933 elif filesystem == 'jfs':
934 package_list.append('sys-fs/jfsutils')
935 elif filesystem == 'ntfs':
936 package_list.append('sys-fs/ntfsprogs')
937 elif filesystem in ['fat','vfat', 'msdos', 'umsdos']:
938 package_list.append('sys-fs/dosfstools')
939 elif filesystem == 'hfs':
940 # should check with the PPC guys on this
941 package_list.append('sys-fs/hfsutils')
942 package_list.append('sys-fs/hfsplusutils')
943 #else:
944 # should be code here for every FS type!
945 failed_list = []
946 for package in package_list:
947 if self._debug: self._logger.log("DEBUG: install_filesystem_tools(): emerging "+package)
948 exitstatus = self._portage.emerge(package)
949 # if not GLIUtility.exitsuccess(exitstatus):
950 # self._logger.log("ERROR! : Could not emerge "+package+"!")
951 # failed_list.append(package)
952 # else:
953 self._logger.log("FileSystemTool "+package+" was emerged successfully.")
954 # error checking is important!
955 if len(failed_list) > 0:
956 raise GLIException("InstallFileSystemToolsError", 'warning', 'install_filesystem_tools', "Could not emerge " + failed_list + "!")
957
958
959 ##
960 # Installs and sets up pcmcia-cs if selected in the profile IS THIS USED AT ALL????
961 def install_pcmcia_cs(self):
962 if self._debug: self._logger.log("DEBUG: install_pcmcia_cs(): emerging pcmcia-cs")
963 exitstatus = self._portage.emerge("pcmcia-cs")
964 # if not GLIUtility.exitsuccess(exitstatus):
965 # self._logger.log("ERROR! : Could not emerge pcmcia-cs!")
966
967 # Add pcmcia-cs to the default runlevel
968 # else:
969 self._add_to_runlevel('pcmcia')
970 self._logger.log("PCMCIA_CS emerged and configured.")
971
972
973 ##
974 # Sets up the network for the first boot
975 def setup_network_post(self):
976 if self._debug: self._logger.log("DEBUG: setup_network_post(): starting network configuration")
977 # Get hostname, domainname and nisdomainname
978 hostname = self._install_profile.get_hostname()
979 domainname = self._install_profile.get_domainname()
980 nisdomainname = self._install_profile.get_nisdomainname()
981
982 # Write the hostname to the hostname file
983 #open(self._chroot_dir + "/etc/hostname", "w").write(hostname + "\n")
984 self._edit_config(self._chroot_dir + "/etc/conf.d/hostname", {"HOSTNAME": hostname})
985
986 # Write the domainname to the nisdomainname file
987 if domainname:
988 #open(self._chroot_dir + "/etc/dnsdomainname", "w").write(domainname + "\n")
989 self._edit_config(self._chroot_dir + "/etc/conf.d/domainname", {"DNSDOMAIN": domainname})
990 self._add_to_runlevel("domainname")
991
992 # Write the nisdomainname to the nisdomainname file
993 if nisdomainname:
994 #open(self._chroot_dir + "/etc/nisdomainname", "w").write(nisdomainname + "\n")
995 self._edit_config(self._chroot_dir + "/etc/conf.d/domainname", {"NISDOMAIN": nisdomainname})
996 self._add_to_runlevel("domainname")
997
998 #
999 # EDIT THE /ETC/HOSTS FILE
1000 #
1001
1002 # The address we are editing is 127.0.0.1
1003 hosts_ip = "127.0.0.1"
1004
1005 # If the hostname is localhost
1006 if hostname == "localhost":
1007 # If a domainname is set
1008 if domainname:
1009 hosts_line = hostname + "." + domainname + "\t" + hostname
1010 else:
1011 hosts_line = hostname
1012 # If the hostname is not localhost
1013 else:
1014 # If a domainname is set
1015 if domainname:
1016 hosts_line = hostname + "." + domainname + "\t" + hostname + "\tlocalhost"
1017 else:
1018 hosts_line = "localhost\t" + hostname
1019
1020 # Write to file
1021 self._edit_config(self._chroot_dir + "/etc/hosts", {hosts_ip: hosts_line}, delimeter='\t', quotes_around_value=False)
1022
1023 #
1024 # SET DEFAULT GATEWAY
1025 #
1026
1027 # Get default gateway
1028 default_gateway = self._install_profile.get_default_gateway()
1029
1030 # If the default gateway exists, add it
1031 if default_gateway:
1032 default_gateway_string = '( "default via ' + default_gateway[1] + '" )'
1033 if self._debug: self._logger.log("DEBUG: setup_network_post(): found gateway. adding to confing. "+default_gateway_string)
1034 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"routes_"+default_gateway[0]: default_gateway_string}, quotes_around_value=False)
1035
1036 #
1037 # SET RESOLV INFO
1038 #
1039
1040 # Get dns servers
1041 dns_servers = self._install_profile.get_dns_servers()
1042
1043 # Clear the list
1044 resolv_output = []
1045
1046 # If dns servers are set
1047 if dns_servers:
1048
1049
1050 # Parse each dns server
1051 for dns_server in dns_servers:
1052 # Add the server to the output
1053 resolv_output.append("nameserver " + dns_server +"\n")
1054
1055 # If the domainname is set, then also output it
1056 if domainname:
1057 resolv_output.append("search " + domainname + "\n")
1058
1059 # Output to file
1060 if self._debug: self._logger.log("DEBUG: setup_network_post(): writing resolv.conf with contents: " + str(resolv_output))
1061 resolve_conf = open(self._chroot_dir + "/etc/resolv.conf", "w")
1062 resolve_conf.writelines(resolv_output)
1063 resolve_conf.close()
1064
1065 #
1066 # PARSE INTERFACES
1067 #
1068
1069 # Fetch interfaces
1070 interfaces = self._install_profile.get_network_interfaces()
1071 emerge_dhcp = False
1072 # Parse each interface
1073 for interface in interfaces.keys():
1074 if self._debug: self._logger.log("DEBUG: setup_network_post(): configuring interface: "+ interface)
1075 # Set what kind of interface it is
1076 interface_type = interface[:3]
1077
1078 # Check to see if there is a startup script for this interface, if there isn't link to the proper script
1079 try:
1080 os.stat(self._chroot_dir + "/etc/init.d/net." + interface)
1081 except:
1082 if self._debug: self._logger.log("DEBUG: setup_network_post(): /etc/init.d/net." + interface + " didn't exist, symlinking it.")
1083 os.symlink("net." + interface_type + "0", self._chroot_dir + "/etc/init.d/net." + interface)
1084
1085 # If we are going to load the network at boot...
1086 #if interfaces[interface][2]: #THIS FEATURE NO LONGER EXISTS
1087
1088 # Add it to the default runlevel
1089 if self._debug: self._logger.log("DEBUG: setup_network_post(): adding net."+interface+" to runlevel.")
1090 self._add_to_runlevel("net."+interface) # moved a bit <-- for indentation
1091
1092 #
1093 # ETHERNET
1094 #
1095 if interface_type == "eth":
1096
1097 #
1098 # STATIC IP
1099 #
1100 # If the post-install device info is not None, then it is a static ip addy
1101 if interfaces[interface][0] != "dhcp":
1102 ip = interfaces[interface][0]
1103 broadcast = interfaces[interface][1]
1104 netmask = interfaces[interface][2]
1105 # aliases = interfaces[interface][1][3]
1106 # alias_ips = []
1107 # alias_broadcasts = []
1108 # alias_netmasks = []
1109
1110 # Write the static ip config to /etc/conf.d/net
1111 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"iface_" + interface: ip + " broadcast " + broadcast + " netmask " + netmask})
1112
1113 # If aliases are set
1114 # if aliases:
1115
1116 # Parse aliases to format alias info
1117 # for alias in aliases:
1118 # alias_ips.append(alias[0])
1119 # alias_broadcasts.append(alias[1])
1120 # alias_netmasks.append(allias[2])
1121
1122 # Once the alias info has been gathered, then write it out
1123 # Alias ips first
1124 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "alias_" + interface, string.join(alias_ips))
1125 # Alias broadcasts next
1126 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "broadcast_" + interface, string.join(alias_broadcasts))
1127 # Alias netmasks last
1128 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "netmask_" + interface, string.join(alias_netmasks))
1129
1130 #
1131 # DHCP IP
1132 #
1133 else:
1134 dhcpcd_options = interfaces[interface][1]
1135 if not dhcpcd_options:
1136 dhcpcd_options = ""
1137 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"iface_" + interface: "dhcp", "dhcpcd_" + interface: dhcpcd_options})
1138 emerge_dhcp = True
1139 if emerge_dhcp:
1140 if self._debug: self._logger.log("DEBUG: setup_network_post(): emerging dhcpcd.")
1141 exitstatus = self._portage.emerge("dhcpcd")
1142 # if not GLIUtility.exitsuccess(exitstatus):
1143 # self._logger.log("ERROR! : Could not emerge dhcpcd!")
1144 # else:
1145 self._logger.log("dhcpcd emerged.")
1146
1147
1148 ##
1149 # This is a stub function to be done by the individual arch. I don't think it's even needed here.
1150 # but it's nice having it just incase.
1151 def install_bootloader(self):
1152 "THIS FUNCTION MUST BE DONE BY THE INDIVIDUAL ARCH"
1153 pass
1154
1155 ##
1156 # Sets up the new users for the system
1157 def set_users(self):
1158 # Loop for each user
1159 for user in self._install_profile.get_users():
1160
1161 # Get values from the tuple
1162 username = user[0]
1163 password_hash = user[1]
1164 groups = user[2]
1165 shell = user[3]
1166 home_dir = user[4]
1167 uid = user[5]
1168 comment = user[6]
1169
1170 options = [ "-m", "-p '" + password_hash + "'" ]
1171
1172 # If the groups are specified
1173 if groups:
1174
1175 # If just one group is listed as a string, make it a list
1176 if groups == str:
1177 groups = [ groups ]
1178
1179 # If only 1 group is listed
1180 if len(groups) == 1:
1181 options.append("-G " + groups[0])
1182
1183 # If there is more than one group
1184 elif len(groups) > 1:
1185 options.append('-G "' + string.join(groups, ",") + '"')
1186
1187 # Attempt to add the group (will return success when group exists)
1188 for group in groups:
1189 if not group: continue
1190 # Add the user
1191 if self._debug: self._logger.log("DEBUG: set_users(): adding user to groups with (in chroot): "+'groupadd -f ' + group)
1192 exitstatus = GLIUtility.spawn('groupadd -f ' + group, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True)
1193 if not GLIUtility.exitsuccess(exitstatus):
1194 self._logger.log("ERROR! : Failure to add group " + group+" and it wasn't that the group already exists!")
1195
1196
1197 # If a shell is specified
1198 if shell:
1199 options.append("-s " + shell)
1200
1201 # If a home dir is specified
1202 if home_dir:
1203 options.append("-d " + home_dir)
1204
1205 # If a UID is specified
1206 if uid:
1207 options.append("-u " + str(uid))
1208
1209 # If a comment is specified
1210 if comment:
1211 options.append('-c "' + comment + '"')
1212
1213 # Add the user
1214 if self._debug: self._logger.log("DEBUG: set_users(): adding user with (in chroot): "+'useradd ' + string.join(options) + ' ' + username)
1215 exitstatus = GLIUtility.spawn('useradd ' + string.join(options) + ' ' + username, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True)
1216 if not GLIUtility.exitsuccess(exitstatus):
1217 self._logger.log("ERROR! : Failure to add user " + username)
1218 # raise GLIException("AddUserError", 'warning', 'set_users', "Failure to add user " + username)
1219 else:
1220 self._logger.log("User " + username + " was added.")
1221
1222 ##
1223 # Installs a list of packages specified in the profile. Will install any extra software!
1224 # In the future this function will lead to better things. It may even wipe your ass for you.
1225 def install_packages(self):
1226 installpackages = self._install_profile.get_install_packages()
1227 if installpackages:
1228 # pkglist = self._portage.get_deps(" ".join(installpackages))
1229 # if self._debug: self._logger.log("install_packages(): pkglist is " + str(pkglist))
1230 # for i, pkg in enumerate(pkglist):
1231 # if self._debug: self._logger.log("install_packages(): processing package " + pkg)
1232 # self.notify_frontend("progress", (float(i) / len(pkglist), "Emerging " + pkg + " (" + str(i) + "/" + str(len(pkglist)) + ")"))
1233 # if not self._portage.get_best_version_vdb("=" + pkg):
1234 # status = self._emerge("=" + pkg)
1235 # if not GLIUtility.exitsuccess(status):
1236 # raise GLIException("ExtraPackagesError", "fatal", "install_packages", "Could not emerge " + pkg + "!")
1237 # else:
1238 # try:
1239 # self._portage.copy_pkg_to_chroot(pkg)
1240 # except:
1241 # raise GLIException("ExtraPackagesError", "fatal", "install_packages", "Could not emerge " + pkg + "!")
1242 self._portage.emerge(installpackages)
1243
1244 if GLIUtility.is_file(self._chroot_dir + "/etc/X11"):
1245 # Copy the xorg.conf from the LiveCD if they installed xorg-x11
1246 exitstatus = GLIUtility.spawn("cp /etc/X11/xorg.conf " + self._chroot_dir + "/etc/X11/xorg.conf")
1247 if not GLIUtility.exitsuccess(exitstatus):
1248 self._logger.log("Could NOT copy the xorg configuration from the livecd to the new system!")
1249 else:
1250 self._logger.log("xorg.conf copied to new system. X should be ready to roll!")
1251 if GLIUtility.is_file(self._chroot_dir + "/etc/X11/gdm/gdm.conf"):
1252 GLIUtility.spawn("cp -f /etc/X11/gdm/gdm.conf.old " + self._chroot_dir + "/etc/X11/gdm/gdm.conf")
1253 if GLIUtility.is_file(self._chroot_dir + "/etc/X11/gdm/custom.conf"):
1254 GLIUtility.spawn("cp -f /etc/X11/gdm/custom.conf.old " + self._chroot_dir + "/etc/X11/gdm/custom.conf")
1255
1256 ##
1257 # Will set the list of services to runlevel default. This is a temporary solution!
1258 def set_services(self):
1259 services = self._install_profile.get_services()
1260 for service in services:
1261 if service:
1262 self._add_to_runlevel(service)
1263
1264
1265 ##
1266 # Will execute any arbritraially defined script here for post-install customization.
1267 def run_post_install_script(self):
1268 if self._install_profile.get_post_install_script_uri():
1269 try:
1270 if self._debug: self._logger.log("DEBUG: run_post_install_script(): getting script: "+self._install_profile.get_post_install_script_uri())
1271 GLIUtility.get_uri(self._install_profile.get_post_install_script_uri(), self._chroot_dir + "/var/tmp/post-install")
1272 if self._debug: self._logger.log("DEBUG: run_post_install_script(): running: chmod a+x /var/tmp/post-install && /var/tmp/post-install in chroot")
1273 GLIUtility.spawn("chmod a+x /var/tmp/post-install && /var/tmp/post-install", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
1274 except:
1275 raise GLIException("RunPostInstallScriptError", 'fatal', 'run_post_install_script', "Failed to retrieve and/or execute post-install script")
1276
1277
1278 ##
1279 # This function will handle the various cleanup tasks as well as unmounting the filesystems for reboot.
1280 def finishing_cleanup(self):
1281 #These are temporary until I come up with a nicer idea.
1282 #get rid of the compile_output file so the symlink doesn't get screwed up.
1283
1284 #we copy the log over to the new system.
1285 install_logfile = self._client_configuration.get_log_file()
1286 try:
1287 if self._debug: self._logger.log("DEBUG: finishing_cleanup(): copying logfile over to new system's root.")
1288 shutil.copy(install_logfile, self._chroot_dir + install_logfile)
1289 except:
1290 if self._debug: self._logger.log("DEBUG: finishing_cleanup(): ERROR! could not copy logfile over to /root.")
1291 #Now we're done logging as far as the new system is concerned.
1292 GLIUtility.spawn("cp /tmp/installprofile.xml " + self._chroot_dir + "/root/installprofile.xml")
1293 GLIUtility.spawn("cp /tmp/clientconfiguration.xml " + self._chroot_dir + "/root/clientconfiguration.xml")
1294
1295 #Unmount mounted fileystems in preparation for reboot
1296 #mounts = GLIUtility.spawn(r"mount | sed -e 's:^.\+ on \(.\+\) type .\+$:\1:' | grep -e '^" + self._chroot_dir + "' | sort -r", return_output=True)[1].split("\n")
1297 mounted_devices = self._mounted_devices
1298 mounted_devices.sort()
1299 mounted_devices.reverse()
1300 for mount in mounted_devices:
1301 if self._debug: self._logger.log("DEBUG: finishing_cleanup(): running: umount -l " + mount)
1302 ret = GLIUtility.spawn("umount -l " + self._chroot_dir + mount)
1303 if not GLIUtility.exitsuccess(ret):
1304 self._logger.log("ERROR! : Could not unmount mountpoint %s" % mount)
1305
1306 # now turn off all swap as well.
1307 # we need to find the swap devices
1308 for swap_device in self._swap_devices:
1309 if self._debug: self._logger.log("DEBUG: finishing_cleanup(): running: swapoff "+swap_device)
1310 ret = GLIUtility.spawn("swapoff "+swap_device)
1311 if not GLIUtility.exitsuccess(ret):
1312 self._logger.log("ERROR! : Could not deactivate swap ("+swap_device+")!")
1313
1314 GLIUtility.spawn("rm /tmp/compile_output.log && rm " + install_logfile)
1315
1316
1317
1318 ##
1319 # This function should only be called in the event of an install failure. It performs
1320 # general cleanup to prepare the system for another installer run.
1321 def install_failed_cleanup(self):
1322 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): gathering mounts to unmount")
1323 #mounts = GLIUtility.spawn(r"mount | sed -e 's:^.\+ on \(.\+\) type .\+$:\1:' | grep -e '^" + self._chroot_dir + "' | sort -r", return_output=True)[1].split("\n")
1324 mounted_devices = self._mounted_devices
1325 mounted_devices.sort()
1326 mounted_devices.reverse()
1327 for mount in mounted_devices:
1328 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: umount -l " + mount)
1329 ret = GLIUtility.spawn("umount -l " + self._chroot_dir + mount)
1330 if not GLIUtility.exitsuccess(ret):
1331 self._logger.log("ERROR! : Could not unmount mountpoint %s" % mount)
1332
1333 # now turn off all swap as well.
1334 # we need to find the swap devices
1335 for swap_device in self._swap_devices:
1336 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: swapoff "+swap_device)
1337 ret = GLIUtility.spawn("swapoff "+swap_device)
1338 if not GLIUtility.exitsuccess(ret):
1339 self._logger.log("ERROR! : Could not deactivate swap ("+swap_device+")!")
1340
1341 if self._debug: self._logger.log("DEBUG: install_failed_cleanup(): running: cp /tmp/compile_output.log /tmp/compile_output.log.failed then removing /tmp/compile_output.log")
1342 GLIUtility.spawn("mv " + self._compile_logfile + " " + self._compile_logfile + ".failed")
1343 # GLIUtility.spawn("rm /tmp/compile_output.log")
1344 GLIUtility.spawn("mv " + self._client_configuration.get_log_file() + " " + self._client_configuration.get_log_file() + ".failed")
1345 # GLIUtility.spawn("rm /var/log/installer.log")
1346
1347 ######################################################
1348 ######### Partitioning Code ##########################
1349
1350
1351 def _sectors_to_megabytes(self, sectors, sector_bytes=512):
1352 return float((float(sectors) * sector_bytes)/ float(MEGABYTE))
1353
1354 def _wait_for_device_node(self, devnode):
1355 if GLIUtility.is_file("/sbin/udevsettle"):
1356 GLIUtility.spawn("/sbin/udevsettle")
1357 if not GLIUtility.is_file(devnode):
1358 GLIUtility.spawn("/sbin/udevsettle")
1359 else:
1360 for i in range(0, 10):
1361 if GLIUtility.is_file(devnode):
1362 break
1363 time.sleep(1)
1364 time.sleep(1)
1365 for i in range(0, 10):
1366 if GLIUtility.is_file(devnode):
1367 break
1368 time.sleep(1)
1369
1370 def _add_partition(self, disk, start, end, type, fs, name="", strict_start=False, strict_end=False):
1371 if self._debug: self._logger.log("_add_partition(): type=%s, fstype=%s" % (type, fs))
1372 types = { 'primary': parted.PARTITION_PRIMARY, 'extended': parted.PARTITION_EXTENDED, 'logical': parted.PARTITION_LOGICAL }
1373 fsTypes = {}
1374 fs_type = parted.file_system_type_get_next ()
1375 while fs_type:
1376 fsTypes[fs_type.name] = fs_type
1377 fs_type = parted.file_system_type_get_next (fs_type)
1378 fstype = None
1379 if fs == "apple_bootstrap":
1380 fs = "hfs"
1381 if fs: fstype = fsTypes[fs]
1382 newpart = disk.partition_new(types[type], fstype, start, end)
1383 constraint = disk.dev.constraint_any()
1384 if strict_start:
1385 constraint.start_range.set_start(start)
1386 constraint.start_range.set_end(start)
1387 constraint.end_range.set_start(end)
1388 if strict_end:
1389 constraint.start_range.set_start(start)
1390 constraint.end_range.set_start(end)
1391 constraint.end_range.set_end(end)
1392 disk.add_partition(newpart, constraint)
1393 if self._debug: self._logger.log("_add_partition(): partition added")
1394
1395 def _delete_partition(self, parted_disk, minor):
1396 try:
1397 parted_disk.delete_partition(parted_disk.get_partition(minor))
1398 except:
1399 self._logger.log("_delete_partition(): could not delete partition...ignoring (for now)")
1400
1401 def _check_table_changed(self, oldparts, newparts):
1402 for part in newparts:
1403 oldpart = oldparts[part]
1404 newpart = newparts[part]
1405 if not newparts[part]['origminor'] or not oldparts.get_partition(part):
1406 return True
1407 if oldpart['type'] == newpart['type'] and long(oldpart['mb']) == long(newpart['mb']) and not newpart['resized'] and not newpart['format']:
1408 continue
1409 else:
1410 return True
1411 return False
1412
1413 def _check_table_layout_changed(self, oldparts, newparts):
1414 # This function is similar to the above function except it will see it as un-changed even if a partition is just being reformatted
1415 for part in newparts:
1416 oldpart = oldparts[part]
1417 newpart = newparts[part]
1418 if not newparts[part]['origminor'] or not oldparts.get_partition(part):
1419 return True
1420 if oldpart['type'] == newpart['type'] and long(oldpart['mb']) == long(newpart['mb']) and not newpart['resized']:
1421 continue
1422 else:
1423 return True
1424 return False
1425
1426 def _find_existing_in_new(self, oldminor, newparts):
1427 for part in newparts:
1428 if newparts[part]['origminor'] == oldminor:
1429 return part
1430 return 0
1431
1432 def _check_keeping_any_existing(self, newparts):
1433 for part in newparts:
1434 if newparts[part]['origminor']: return True
1435 return False
1436
1437 def _find_next_partition(self, curminor, parts):
1438 foundmyself = False
1439 for part in parts:
1440 if not part == curminor and not foundmyself: continue
1441 if part == curminor:
1442 foundmyself = True
1443 continue
1444 if foundmyself:
1445 return part
1446 return 0
1447
1448 def _find_current_minor_for_part(self, device, start):
1449 tmp_oldparts = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
1450 tmp_oldparts.set_partitions_from_disk()
1451 for tmp_oldpart in tmp_oldparts:
1452 self._logger.log("_find_current_minor_for_part(): looking at minor %s...start sector is %s...looking for %s" % (str(tmp_oldpart), str(tmp_oldparts[tmp_oldpart]['start']), str(start)))
1453 if tmp_oldparts[tmp_oldpart]['start'] == start:
1454 return tmp_oldparts[tmp_oldpart]['minor']
1455 else:
1456 raise GLIException("PartitionResizeError", 'fatal', '_find_current_minor_for_part', "Could not determine the new devnode for partition starting at sector " + str(start))
1457
1458 def _partition_delete_step(self, parted_disk, oldparts, newparts):
1459 self._logger.log("_partition_delete_step(): Deleting partitions that aren't being resized")
1460 for oldpart in list(oldparts)[::-1]:
1461 tmppart_old = oldparts[oldpart]
1462 if oldparts.get_disklabel() != "mac" and tmppart_old['type'] == "free": continue
1463 if tmppart_old['type'] == "extended":
1464 # Iterate through logicals to see if any are being resized
1465 self._logger.log("_partition_delete_step(): logicals for extended part %d: %s" % (tmppart_old['minor'], str(tmppart_old.get_logicals())))
1466 for logpart in tmppart_old.get_logicals():
1467 newminor = self._find_existing_in_new(logpart, newparts)
1468 self._logger.log("_partition_delete_step(): newminor is " + str(newminor))
1469 if newminor and newparts[newminor]['resized']:
1470 self._logger.log(" Logical partition " + str(logpart) + " to be resized...can't delete extended")
1471 break
1472 else:
1473 self._logger.log(" No logical partitions are being resized...deleting extended")
1474 self._delete_partition(parted_disk, oldpart)
1475 else:
1476 newminor = self._find_existing_in_new(oldpart, newparts)
1477 if newminor and not newparts[newminor]['format']:
1478 if newparts[newminor]['resized']:
1479 self._logger.log(" Ignoring old minor " + str(oldpart) + " to resize later")
1480 continue
1481 else:
1482 self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated later")
1483 else:
1484 self._logger.log(" No match in new layout for old minor " + str(oldpart) + "...deleting")
1485 self._delete_partition(parted_disk, oldpart)
1486 parted_disk.commit()
1487
1488 def _partition_resize_step(self, parted_disk, device, oldparts, newparts):
1489 self._logger.log("_partition_resize_step(): Resizing partitions")
1490 device_sectors = newparts.get_num_sectors()
1491 for oldpart in oldparts:
1492 tmppart_old = oldparts[oldpart]
1493 newminor = self._find_existing_in_new(oldpart, newparts)
1494 if not newminor or not newparts[newminor]['resized'] or newparts[newminor]['type'] in ("extended", "free"):
1495 continue
1496 tmppart_new = newparts[newminor]
1497 type = tmppart_new['type']
1498 start = tmppart_new['start']
1499 end = start + (long(tmppart_new['mb']) * MEGABYTE / 512) - 1
1500
1501 # Make sure calculated end sector doesn't overlap start sector of next partition
1502 nextminor = self._find_next_partition(newminor, newparts)
1503 if nextminor:
1504 if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
1505 self._logger.log(" End sector for growing partition overlaps with start of next partition...fixing")
1506 end = newparts[nextminor]['start'] - 1
1507
1508 # cap to end of device
1509 if end >= device_sectors:
1510 end = device_sectors - 1
1511
1512 total_sectors = end - start + 1
1513 total_bytes = long(total_sectors) * 512
1514
1515 # Delete partition and recreate at same start point with new size if growing
1516 if tmppart_new['mb'] > tmppart_old['mb']:
1517 curminor = self._find_current_minor_for_part(device, start)
1518 self._delete_partition(parted_disk, curminor)
1519 if tmppart_new.is_logical():
1520 tmptype = "logical"
1521 else:
1522 tmptype = "primary"
1523 self._add_partition(parted_disk, start, end, tmptype, tmppart_new['type'], strict_start=True)
1524 parted_disk.commit()
1525
1526 curminor = self._find_current_minor_for_part(device, start)
1527 devnode = device + str(curminor)
1528
1529 self._wait_for_device_node(devnode)
1530
1531 if type in ("ext2", "ext3"):
1532 resizecmd = "resize2fs %s %sK" % (devnode, str(int((total_bytes - (2 * MEGABYTE)) / 1024)))
1533 self._logger.log("_partition_resize_step(): running: " + resizecmd)
1534 ret = GLIUtility.spawn(resizecmd, logfile=self._compile_logfile, append_log=True)
1535 if not GLIUtility.exitsuccess(ret):
1536 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize ext2/3 filesystem on " + devnode)
1537 elif type == "ntfs":
1538 ret = GLIUtility.spawn("yes | ntfsresize -v --size " + str(total_bytes) + " " + devnode, logfile=self._compile_logfile, append_log=True)
1539 if not GLIUtility.exitsuccess(ret):
1540 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize NTFS filesystem on " + devnode)
1541 elif type in ("linux-swap", "fat32", "fat16"):
1542 parted_fs = parted_disk.get_partition(curminor).geom.file_system_open()
1543 resize_constraint = parted_fs.get_resize_constraint()
1544 if total_sectors < resize_constraint.min_size or start != resize_constraint.start_range.start:
1545 raise GLIException("PartitionError", 'fatal', 'partition', "New size specified for " + devnode + " is not within allowed boundaries (blame parted)")
1546 new_geom = resize_constraint.start_range.duplicate()
1547 new_geom.set_start(start)
1548 new_geom.set_end(end)
1549 try:
1550 parted_fs.resize(new_geom)
1551 except:
1552 raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize " + devnode)
1553 self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated in next pass")
1554 # self._delete_partition(parted_disk, oldpart)
1555 parted_disk.delete_all()
1556 parted_disk.commit()
1557
1558 def _partition_recreate_step(self, parted_disk, newparts):
1559 self._logger.log("_partition_recreate_step(): Recreating partitions")
1560 start = 0
1561 end = 0
1562 extended_start = 0
1563 extended_end = 0
1564 device_sectors = newparts.get_num_sectors()
1565 self._logger.log(" Drive has " + str(device_sectors) + " sectors")
1566 for part in newparts:
1567 strict_start = False
1568 strict_end = False
1569 newpart = newparts[part]
1570 self._logger.log(" Partition " + str(part) + " has " + str(newpart['mb']) + "MB")
1571 if newpart['start']:
1572 self._logger.log(" Old start sector " + str(newpart['start']) + " retrieved")
1573 if start != newpart['start']:
1574 self._logger.log(" Retrieved start sector is not the same as the calculated next start sector (usually not an issue)")
1575 start = newpart['start']
1576 strict_start = True
1577 else:
1578 if newpart.is_logical() and start > extended_end:
1579 start = extended_start + 1
1580 self._logger.log(" Start sector calculated to be " + str(start))
1581 if extended_end and not newpart.is_logical() and start <= extended_end:
1582 self._logger.log(" Start sector for primary is less than the end sector for previous extended")
1583 start = extended_end + 1
1584 if newpart['end']:
1585 self._logger.log(" Old end sector " + str(newpart['end']) + " retrieved")
1586 end = newpart['end']
1587 part_sectors = end - start + 1
1588 strict_end = True
1589 else:
1590 part_sectors = long(newpart['mb']) * MEGABYTE / 512
1591 end = start + part_sectors
1592 if newpart.is_logical() and end > extended_end:
1593 end = extended_end
1594 self._logger.log(" End sector calculated to be " + str(end))
1595 # Make sure end doesn't overlap next partition's existing start sector
1596 nextminor = self._find_next_partition(newpart, newparts)
1597 if nextminor:
1598 if newparts[nextminor]['start'] and end >= newparts[nextminor]['start']:
1599 self._logger.log(" End sector for partition overlaps with start of next partition...fixing")
1600 end = newparts[nextminor]['start'] - 1
1601 strict_end = True
1602 # cap to end of device
1603 if end >= device_sectors:
1604 end = device_sectors - 1
1605 # now the actual creation
1606 if newpart['type'] == "free":
1607 if newparts.get_disklabel() == "mac":
1608 # Create a dummy partition to be removed later because parted sucks
1609 self._logger.log(" Adding dummy partition to fool parted " + str(part) + " from " + str(start) + " to " + str(end))
1610 self._add_partition(parted_disk, start, end, "primary", "ext2", "free", strict_start=strict_start, strict_end=strict_end)
1611 elif newpart['type'] == "extended":
1612 self._logger.log(" Adding extended partition " + str(part) + " from " + str(start) + " to " + str(end))
1613 self._add_partition(parted_disk, start, end, "extended", "", strict_start=strict_start, strict_end=strict_end)
1614 extended_start = start
1615 extended_end = end
1616 elif not newpart.is_logical():
1617 self._logger.log(" Adding primary partition " + str(part) + " from " + str(start) + " to " + str(end))
1618 self._add_partition(parted_disk, start, end, "primary", newpart['type'], strict_start=strict_start, strict_end=strict_end)
1619 elif newpart.is_logical():
1620 if start >= extended_end:
1621 start = extended_start + 1
1622 end = start + part_sectors
1623 if nextminor and not newparts[nextminor].is_logical() and end > extended_end:
1624 end = extended_end
1625 self._logger.log(" Adding logical partition " + str(part) + " from " + str(start) + " to " + str(end))
1626 self._add_partition(parted_disk, start, end, "logical", newpart['type'], strict_start=strict_start, strict_end=strict_end)
1627 if self._debug: self._logger.log("partition(): flags: " + str(newpart['flags']))
1628 for flag in newpart['flags']:
1629 if parted_disk.get_partition(part).is_flag_available(flag):
1630 parted_disk.get_partition(part).set_flag(flag, True)
1631 if newpart['name'] and parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
1632 parted_disk.set_name(newpart['name'])
1633 # write to disk
1634 if self._debug: self._logger.log("partition(): committing change to disk")
1635 parted_disk.commit()
1636 if self._debug: self._logger.log("partition(): committed change to disk")
1637 start = end + 1
1638
1639 def _partition_format_step(self, parted_disk, device, newparts):
1640 self._logger.log("_partition_format_step(): Formatting new partitions")
1641 for part in newparts:
1642 newpart = newparts[part]
1643 devnode = newpart['devnode']
1644 # This little hack is necessary because parted sucks goat nuts
1645 if newparts.get_disklabel() == "mac" and newpart['type'] == "free":
1646 self._delete_partition(parted_disk, newpart)
1647 continue
1648 if newpart['format'] and newpart['type'] not in ('extended', 'free'):
1649 # devnode = device + str(int(part))
1650 if self._debug: self._logger.log("_partition_format_step(): devnode is %s in formatting code" % devnode)
1651 # if you need a special command and
1652 # some base options, place it here.
1653 format_cmds = { 'linux-swap': "mkswap", 'fat16': "mkfs.vfat -F 16", 'fat32': "mkfs.vfat -F 32",
1654 'ntfs': "mkntfs", 'xfs': "mkfs.xfs -f", 'jfs': "mkfs.jfs -f",
1655 'reiserfs': "mkfs.reiserfs -f", 'ext2': "mkfs.ext2", 'ext3': "mkfs.ext3",
1656 'hfs': "hformat", 'apple_bootstrap': "hformat"
1657 }
1658 if newpart['type'] in format_cmds:
1659 cmdname = format_cmds[newpart['type']]
1660 else: # this should catch everything else
1661 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Unknown partition type " + newpart['type'])
1662 # sleep a bit first
1663 time.sleep(1)
1664 for tries in range(10):
1665 cmd = "%s %s %s" % (cmdname, newpart['mkfsopts'], devnode)
1666 self._logger.log(" Formatting partition %s as %s with: %s" % (str(part),newpart['type'],cmd))
1667 ret = GLIUtility.spawn(cmd, logfile=self._compile_logfile, append_log=True)
1668 if not GLIUtility.exitsuccess(ret):
1669 self._logger.log("Try %d failed formatting partition %s...waiting 5 seconds" % (tries+1, devnode))
1670 time.sleep(5)
1671 else:
1672 break
1673 else:
1674 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Could not create %s filesystem on %s" % (newpart['type'], devnode))
1675
1676 def partition(self):
1677 """
1678 TODO:
1679 skip fixed partitions in all passes (in GLISD maybe?)
1680 """
1681 parts_old = {}
1682 parts_new = self._install_profile.get_partition_tables()
1683 for device in GLIStorageDevice.detect_devices():
1684 parts_old[device] = GLIStorageDevice.Device(device, arch=self._client_configuration.get_architecture_template())
1685 parts_old[device].set_partitions_from_disk()
1686
1687 self.notify_frontend("progress", (0, "Examining partitioning data"))
1688 total_steps = float(len(parts_new) * 4) # 4 for the number of passes over each device
1689 cur_progress = 0
1690 for device in parts_new:
1691 # Skip this device in parts_new if device isn't detected on current system
1692 if not device in parts_old:
1693 self._logger.log("There is no physical device " + device + " detected to match the entry in the install profile...skipping")
1694 continue
1695
1696 # This just makes things simpler in the code
1697 newparts = parts_new[device]
1698 oldparts = parts_old[device]
1699
1700 # Check to see if the old and new partition table structures are the same...skip if they are
1701 if not self._check_table_changed(oldparts, newparts):
1702 self._logger.log("Partition table for " + device + " is unchanged...skipping")
1703 continue
1704
1705 self._logger.log("partition(): Processing " + device + "...")
1706
1707 # Commit ritual sepuku if there are any mounted filesystems on this device
1708 if GLIUtility.spawn("mount | grep '^" + device + "'", return_output=True)[1].strip():
1709 raise GLIException("PartitionsMountedError", 'fatal', 'partition', "Cannot partition " + device + " due to filesystems being mounted")
1710
1711 # We also can't handle "unknown" partitions
1712 for part in newparts:
1713 if newparts[part]['type'] == "unknown":
1714 raise GLIException("UnknownPartitionTypeError", 'fatal', 'partition', "Refusing to partition this drive due to the presence of an unknown type of partition")
1715
1716 # Create pyparted objects for this device
1717 parted_dev = parted.PedDevice.get(device)
1718 try:
1719 parted_disk = parted.PedDisk.new(parted_dev)
1720 except:
1721 if self._debug: self._logger.log("partition(): could not load existing disklabel...creating new one")
1722 parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get((newparts.get_disklabel() or GLIStorageDevice.archinfo[self._architecture_name])))
1723
1724 # Iterate through new partitions and check for 'origminor' and 'format' == False
1725 for part in newparts:
1726 tmppart_new = newparts[part]
1727 if not tmppart_new['origminor'] or tmppart_new['format']: continue
1728 if not tmppart_new['origminor'] in oldparts:
1729 raise GLIException("MissingPartitionsError", 'fatal', 'partition', "Cannot find the existing partition that a new one refers to. This is not a bug. This is in fact your (the user's) fault. You should not reuse the installprofile.xml from a previous install that started the partitioning step.")
1730 tmppart_old = oldparts[tmppart_new['origminor']]
1731 if parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
1732 tmppart_new['name'] = tmppart_old['name']
1733 tmppart_new['flags'] = tmppart_old['flags']
1734 if tmppart_new['resized']:
1735 # Partition is being resized in the new layout
1736 self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + " and it being resized...saving start sector " + str(tmppart_old['start']))
1737 tmppart_new['start'] = tmppart_old['start']
1738 tmppart_new['end'] = 0
1739 else:
1740 # Partition is untouched in the new layout
1741 self._logger.log(" Partition " + str(part) + " has origminor " + str(tmppart_new['origminor']) + "...saving start sector " + str(tmppart_old['start']) + " and end sector " + str(tmppart_old['end']))
1742 tmppart_new['start'] = tmppart_old['start']
1743 tmppart_new['end'] = tmppart_old['end']
1744
1745 if self._check_table_layout_changed(parts_old[device], parts_new[device]):
1746 # First pass to delete old partitions that aren't resized
1747 self.notify_frontend("progress", (cur_progress / total_steps, "Deleting partitioning that aren't being resized for " + device))
1748 cur_progress += 1
1749 self._partition_delete_step(parted_disk, oldparts, newparts)
1750
1751 # Second pass to resize old partitions that need to be resized
1752 self.notify_frontend("progress", (cur_progress / total_steps, "Resizing remaining partitions for " + device))
1753 cur_progress += 1
1754 self._partition_resize_step(parted_disk, device, oldparts, newparts)
1755
1756 # Wiping disk and creating blank disklabel
1757 try:
1758 parted_disk = parted_dev.disk_new_fresh(parted.disk_type_get(newparts.get_disklabel()))
1759 parted_disk.commit()
1760 except:
1761 raise GLIException("DiskLabelCreationError", 'fatal', 'partition', "Could not create a blank disklabel!")
1762
1763 # Third pass to create new partition table
1764 self.notify_frontend("progress", (cur_progress / total_steps, "Recreating partition table for " + device))
1765 cur_progress += 1
1766 self._partition_recreate_step(parted_disk, newparts)
1767 else:
1768 cur_progress += 3
1769
1770 # Fourth pass to format partitions
1771 self.notify_frontend("progress", (cur_progress / total_steps, "Formatting partitions for " + device))
1772 cur_progress += 1
1773 self._partition_format_step(parted_disk, device, newparts)
1774
1775 # All done for this device
1776 self.notify_frontend("progress", (cur_progress / total_steps, "Done with partitioning for " + device))
1777 cur_progress += 1

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20