/[gli]/trunk/src/GLIArchitectureTemplate.py
Gentoo

Contents of /trunk/src/GLIArchitectureTemplate.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 648 - (show annotations) (download) (as text)
Sat Jun 11 07:51:55 2005 UTC (15 years, 5 months ago) by robbat2
File MIME type: text/x-python
File size: 44772 byte(s)
Refactor code using spawn to reduce duplication.

1 """
2 Gentoo Linux Installer
3
4 $Id: GLIArchitectureTemplate.py,v 1.123 2005/06/11 07:51:55 robbat2 Exp $
5 Copyright 2005 Gentoo Technologies Inc.
6
7 The ArchitectureTemplate is largely meant to be an abstract class and an
8 interface (yes, it is both at the same time!). The purpose of this is to create
9 subclasses that populate all the methods with working methods for that architecture.
10 The only definitions that are filled in here are architecture independent.
11
12 """
13
14 import GLIUtility, GLILogger, os, string, sys, shutil, re
15 from GLIException import *
16
17 class ArchitectureTemplate:
18 ##
19 # Initialization of the ArchitectureTemplate. Called from some other arch template.
20 # @param selfconfiguration=None A Client Configuration
21 # @param install_profile=None An Install Profile
22 # @param client_controller=None Client Controller. not same as configuration.
23 def __init__(self,configuration=None, install_profile=None, client_controller=None):
24 self._client_configuration = configuration
25 self._install_profile = install_profile
26 self._cc = client_controller
27
28 # This will get used a lot, so it's probably
29 # better to store it in a variable than to call
30 # this method 100000 times.
31 self._chroot_dir = self._client_configuration.get_root_mount_point()
32 self._logger = GLILogger.Logger(self._client_configuration.get_log_file())
33 self._compile_logfile = "/tmp/compile_output.log"
34
35 # These must be filled in by the subclass. _steps is a list of
36 # functions, that will carry out the installation. They must be
37 # in order.
38 #
39 # For example, self._steps might be: [preinstall, stage1, stage2, stage3, postinstall],
40 # where each entry is a function (with no arguments) that carries out the desired actions.
41 # Of course, steps will be different depending on the install_profile
42
43 self._architecture_name = "generic"
44 self._install_steps = [
45 (self.partition, "Partition"),
46 (self.mount_local_partitions, "Mount local partitions"),
47 (self.mount_network_shares, "Mount network (NFS) shares"),
48 (self.unpack_stage_tarball, "Unpack stage tarball"),
49 (self.configure_make_conf, "Configure /etc/make.conf"),
50 (self.prepare_chroot, "Preparing chroot"),
51 (self.install_portage_tree, "Portage tree voodoo"),
52 (self.stage1, "Performing bootstrap"),
53 (self.stage2, "Performing 'emerge system'"),
54 (self.set_root_password, "Set the root password"),
55 (self.set_timezone, "Setting timezone"),
56 (self.emerge_kernel_sources, "Emerge kernel sources"),
57 (self.build_kernel, "Building kernel"),
58 (self.install_logging_daemon, "Logger"),
59 (self.install_cron_daemon, "Cron daemon"),
60 (self.install_filesystem_tools, "Installing filesystem tools"),
61 (self.setup_network_post, "Configuring post-install networking"),
62 (self.install_bootloader, "Configuring and installing bootloader"),
63 (self.update_config_files, "Updating config files"),
64 (self.configure_rc_conf, "Updating /etc/rc.conf"),
65 (self.set_services, "Setting up services for startup"),
66 (self.set_users, "Add additional users."),
67 (self.install_packages, "Installing additional packages."),
68 (self.run_post_install_script, "Running custom post-install script"),
69 (self.finishing_cleanup, "Cleanup and unmounting local filesystems.")
70 ]
71
72
73 ##
74 # Returns the steps and their comments in an array
75 def get_install_steps(self):
76 return self._install_steps
77
78 ##
79 # Tells the frontend something
80 # @param type type of data
81 # @param data the data itself. usually a number.
82 def notify_frontend(self, type, data):
83 self._cc.addNotification(type, data)
84
85 # It is possible to override these methods in each Arch Template.
86 # It might be necessary to do so, if the arch needs something 'weird'.
87
88 ##
89 # Private function to add a /etc/init.d/ script to the given runlevel in the chroot environement
90 # @param script_name the script to be added
91 # @param runlevel="default" the runlevel to add to
92 def _add_to_runlevel(self, script_name, runlevel="default"):
93
94 status = GLIUtility.spawn("rc-update add " + script_name + " " + runlevel, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True)
95 if not GLIUtility.exitsuccess(status):
96 raise GLIException("RunlevelAddError", 'fatal', '_add_to_runlevel', "Failure adding " + script_name + " to runlevel " + runlevel + "!")
97 self._logger.log("Added "+script_name+" to runlevel "+runlevel)
98
99 ##
100 # Private Function. Will return a list of packages to be emerged for a given command. Not yet used.
101 # @param cmd full command to run ('/usr/portage/scripts/bootstrap.sh --pretend' or 'emerge -p system')
102 def _get_packages_to_emerge(self, cmd):
103 return GLIUtility.spawn(cmd + r" | grep -e '\[ebuild' | sed -e 's:\[ebuild .\+ \] ::' -e 's: \[.\+\] ::' -e 's: +$::'", chroot=self._chroot_dir, return_output=True)[1].split("\n")
104
105 ##
106 # Private function. For binary installs it will attempt to quickpkg packages that are on the livecd.
107 # @param package package to be quickpkg'd.
108 def _quickpkg_deps(self, package):
109 # These need to be changed to pull values from the make.conf stuff
110 PKGDIR = "/usr/portage/packages"
111 PORTAGE_TMPDIR = "/var/tmp"
112 make_conf = self._install_profile.get_make_conf()
113 if "PKGDIR" in make_conf and make_conf['PKGDIR']: PKGDIR = make_conf['PKGDIR']
114 if "PORTAGE_TMPDIR" in make_conf and make_conf['PORTAGE_TMPDIR']: PORTAGE_TMPDIR = make_conf['PORTAGE_TMPDIR']
115 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PKGDIR, logfile=self._compile_logfile, append_log=True)
116 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTAGE_TMPDIR, logfile=self._compile_logfile, append_log=True)
117 packages = self._get_packages_to_emerge("emerge -p " + package)
118 for pkg in packages:
119 if not GLIUtility.is_file(self._chroot_dir + PKGDIR + "/All/" + pkg.split('/')[1] + ".tbz2"):
120 ret = GLIUtility.spawn("env PKGDIR='" + self._chroot_dir + PKGDIR + "' PORTAGE_TMPDIR='" + self._chroot_dir + PORTAGE_TMPDIR + "' quickpkg =" + pkg)
121 if ret:
122 # This package couldn't be quickpkg'd. This may be an error in the future
123 pass
124
125 ##
126 # Private Function. Will emerge a given package in the chroot environment.
127 # @param package package to be emerged
128 # @param binary=False defines whether to try a binary emerge (if GRP this gets ignored either way)
129 # @param binary_only=False defines whether to only allow binary emerges.
130 def _emerge(self, package, binary=False, binary_only=False):
131 #Error checking of this function is to be handled by the parent function.
132 if self._install_profile.get_grp_install():
133 self._quickpkg_deps(package)
134 cmd="NOCOLOR=yes emerge -k " + package
135 else:
136 if binary_only:
137 cmd="NOCOLOR=yes emerge -K " + package
138 elif binary:
139 cmd="NOCOLOR=yes emerge -k " + package
140 else:
141 cmd="NOCOLOR=yes emerge " + package
142
143 self._logger.log("Calling emerge: "+cmd)
144 return GLIUtility.spawn(cmd, display_on_tty8=True, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True)
145
146 ##
147 # Private Function. Will edit a config file and insert a value or two overwriting the previous value
148 # (actually it only just comments out the old one)
149 # @param filename file to be edited
150 # @param newvalues a dictionary of VARIABLE:VALUE pairs
151 # @param delimeter='=' what is between the key and the value
152 # @param quotes_around_value=True whether there are quotes around the value or not (ex. "local" vs. localhost)
153 # @param only_value=False Ignore the keys and output only a value.
154 def _edit_config(self, filename, newvalues, delimeter='=', quotes_around_value=True, only_value=False):
155 if not GLIUtility.is_file(filename):
156 raise GLIException("NoSuchFileError", 'notice','_edit_config',filename + ' does not exist!')
157
158 f = open(filename)
159 file = f.readlines()
160 f.close()
161
162 for key in newvalues.keys():
163 regexpr = '^\s*#?\s*' + key + '\s*' + delimeter + '.*$'
164 regexpr = re.compile(regexpr)
165
166 for i in range(0, len(file)):
167 if regexpr.match(file[i]):
168 if not file[i][0] == '#':
169 file[i] = '#' + file[i]
170
171 file.append('\n# Added by GLI\n')
172 commentprefix = ""
173 if key == "SPACER":
174 file.append('\n')
175 elif key == "COMMENT":
176 file.append('# ' + newvalues[key] + '\n')
177 elif newvalues[key] == "##comment##" or newvalues[key] == "##commented##":
178 file.append('#' + key + delimeter + '""' + "\n")
179 else:
180 if quotes_around_value:
181 newvalues[key] = '"' + newvalues[key] + '"'
182 #Only the printing of values is required.
183 if only_value:
184 file.append(newvalues[key] + '\n')
185 else:
186 file.append(key + delimeter + newvalues[key]+'\n')
187
188 f = open(filename,'w')
189 f.writelines(file)
190 f.flush()
191 f.close()
192 self._logger.log("Edited Config file "+filename)
193
194 ##
195 # Stage 1 install -- bootstraping the system
196 # If we are doing a stage 1 install, then bootstrap
197 def stage1(self):
198 if self._install_profile.get_install_stage() == 1:
199 self._logger.mark()
200 self._logger.log("Starting bootstrap.")
201 pkgs = self._get_packages_to_emerge("/usr/portage/scripts/bootstrap.sh --pretend")
202 exitstatus = GLIUtility.spawn("/usr/portage/scripts/bootstrap.sh", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
203 if not GLIUtility.exitsuccess(exitstatus):
204 raise GLIException("Stage1Error", 'fatal','stage1', "Bootstrapping failed!")
205 self._logger.log("Bootstrap complete.")
206
207 ##
208 # Stage 2 install -- emerge -e system
209 # If we are doing a stage 1 or 2 install, then emerge system
210 def stage2(self):
211
212 if self._install_profile.get_install_stage() in [ 1, 2 ]:
213 self._logger.mark()
214 self._logger.log("Starting emerge system.")
215 pkgs = self._get_packages_to_emerge("emerge -p system") #currently quite the useless
216 exitstatus = self._emerge("--emptytree system")
217 if not GLIUtility.exitsuccess(exitstatus):
218 raise GLIException("Stage2Error", 'fatal','stage2', "Building the system failed!")
219 self._logger.log("Emerge system complete.")
220
221 ##
222 # Unpacks the stage tarball that has been specified in the profile (it better be there!)
223 def unpack_stage_tarball(self):
224 if not os.path.isdir(self._chroot_dir):
225 os.makedirs(self._chroot_dir)
226 GLIUtility.fetch_and_unpack_tarball(self._install_profile.get_stage_tarball_uri(), self._chroot_dir, temp_directory=self._chroot_dir, keep_permissions=True)
227 self._logger.log(self._install_profile.get_stage_tarball_uri()+" was unpacked.")
228
229 ##
230 # Prepares the Chroot environment by copying /etc/resolv.conf and mounting proc and dev
231 def prepare_chroot(self):
232 # Copy resolv.conf to new env
233 try:
234 shutil.copy("/etc/resolv.conf", self._chroot_dir + "/etc/resolv.conf")
235 except:
236 pass
237 ret = GLIUtility.spawn("mount -t proc none "+self._chroot_dir+"/proc")
238 if not GLIUtility.exitsuccess(ret):
239 raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount /proc')
240 ret = GLIUtility.spawn("mount -o bind /dev " + self._chroot_dir + "/dev")
241 if not GLIUtility.exitsuccess(ret):
242 raise GLIException("MountError", 'fatal','prepare_chroot','Could not mount /dev')
243 GLIUtility.spawn("mv " + self._compile_logfile + " " + self._chroot_dir + self._compile_logfile + " && ln -s " + self._chroot_dir + self._compile_logfile + " " + self._compile_logfile)
244 self._logger.log("Chroot environment ready.")
245
246 ##
247 # Installs a list of packages specified in the profile. Will install any extra software!
248 # In the future this function will lead to better things. It may even wipe your ass for you.
249 def install_packages(self):
250 installpackages = self._install_profile.get_install_packages()
251 for package in installpackages:
252 self._logger.log("Starting emerge " + package)
253 status = self._emerge(package)
254 if not GLIUtility.exitsuccess(status):
255 self._logger.log("Could not emerge " + package + "!")
256 # raise GLIException("InstallPackagesError", 'warning', 'install_packages', "Could not emerge " + package + "!")
257 else:
258 self._logger.log("Emerged package: " + package)
259
260 ##
261 # Will set the list of services to runlevel default. This is a temporary solution!
262 def set_services(self):
263 services = self._install_profile.get_services()
264 for service in services:
265 status = self._add_to_runlevel(service)
266
267 ##
268 # Will grab partition info from the profile and mount all partitions with a specified mountpoint (and swap too)
269 def mount_local_partitions(self):
270 #{ 1: { 'end': 1999871, 'format': False, 'mb': 0,
271 #'mountopts': '', 'mountpoint': '', 'start': 63, 'type': 'linux-swap'},
272 #2: { 'end': 240121727, 'format': False, 'mb': 0, 'mountopts': '',
273 #'mountpoint': '', 'start': 1999872, 'type': 'ext3'}}
274
275 parts = self._install_profile.get_partition_tables()
276 parts_to_mount = {}
277 for device in parts:
278 tmp_partitions = parts[device].get_install_profile_structure()
279 for partition in tmp_partitions:
280 mountpoint = tmp_partitions[partition]['mountpoint']
281 mountopts = tmp_partitions[partition]['mountopts']
282 minor = str(int(tmp_partitions[partition]['minor']))
283 partition_type = tmp_partitions[partition]['type']
284 if mountpoint:
285 if mountopts:
286 mountopts = "-o "+mountopts+" "
287 if partition_type:
288 partition_type = "-t "+partition_type+" "
289 parts_to_mount[mountpoint]= {0: mountopts, 1: partition_type, 2: minor}
290
291 if partition_type == "linux-swap":
292 ret = GLIUtility.spawn("swapon "+device+minor)
293 if not GLIUtility.exitsuccess(ret):
294 self._logger.log("ERROR! : Could not activate swap!")
295 # raise GLIException("MountError", 'warning','mount_local_partitions','Could not activate swap')
296 sorted_list = []
297 for key in parts_to_mount.keys(): sorted_list.append(key)
298 sorted_list.sort()
299
300 for mountpoint in sorted_list:
301 mountopts = parts_to_mount[mountpoint][0]
302 partition_type = parts_to_mount[mountpoint][1]
303 minor = parts_to_mount[mountpoint][2]
304 if not GLIUtility.is_file(self._chroot_dir+mountpoint):
305 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint)
306 if exitstatus != 0:
307 raise GLIException("MkdirError", 'fatal','mount_local_partitions', "Making the mount point failed!")
308 ret = GLIUtility.spawn("mount "+partition_type+mountopts+device+minor+" "+self._chroot_dir+mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
309 if not GLIUtility.exitsuccess(ret):
310 raise GLIException("MountError", 'fatal','mount_local_partitions','Could not mount a partition')
311 self._logger.log("Mounted mountpoint:"+mountpoint)
312 ##
313 # Mounts all network shares to the local machine
314 def mount_network_shares(self):
315 """
316 <agaffney> it'll be much easier than mount_local_partitions
317 <agaffney> make sure /etc/init.d/portmap is started
318 <agaffney> then mount each one: mount -t nfs -o <mountopts> <host>:<export> <mountpoint>
319 """
320 nfsmounts = self._install_profile.get_network_mounts()
321 for netmount in nfsmounts:
322 if netmount['type'] == "NFS" or netmount['type'] == "nfs":
323 mountopts = netmount['mountopts']
324 if mountopts:
325 mountopts = "-o "+mountopts
326 host = netmount['host']
327 export = netmount['export']
328 mountpoint = netmount['mountpoint']
329 if not GLIUtility.is_file(self._chroot_dir+mountpoint):
330 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint)
331 if exitstatus != 0:
332 raise GLIException("MkdirError", 'fatal','mount_network_shares', "Making the mount point failed!")
333 ret = GLIUtility.spawn("mount -t nfs "+mountopts+" "+host+":"+export+" "+self._chroot_dir+mountpoint, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
334 if not GLIUtility.exitsuccess(ret):
335 raise GLIException("MountError", 'fatal','mount_network_shares','Could not mount an NFS partition')
336 self._logger.log("Mounted netmount at mountpoint:"+mountpoint)
337
338 ##
339 # Gets sources from CD (required for non-network installation)
340 # WARNING: There will no longer be sources on the future livecds. this will have to change!
341 def fetch_sources_from_cd(self):
342 """
343 THIS FUNCTION IS NO LONGER VALID
344 if not GLIUtility.is_file(self._chroot_dir+"/usr/portage/distfiles"):
345 exitstatus = GLIUtility.spawn("mkdir -p /usr/portage/distfiles",chroot=self._chroot_dir)
346 if exitstatus != 0:
347 raise GLIException("MkdirError", 'fatal','install_portage_tree',"Making the distfiles directory failed.")
348 exitstatus = GLIUtility.spawn("cp /mnt/cdrom/distfiles/* "+self._chroot_dir+"/usr/portage/distfiles/", display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
349 if exitstatus != 0:
350 raise GLIException("PortageError", 'fatal','install_portage_tree',"Failed to copy the distfiles to the new system")
351 """
352 self._logger.log("Distfiles copied from cd. NOT!")
353
354 ##
355 # Configures the new /etc/make.conf
356 def configure_make_conf(self):
357 # Get make.conf options
358 options = self._install_profile.get_make_conf()
359
360 # For each configuration option...
361 for key in options.keys():
362
363 # Add/Edit it into make.conf
364 self._edit_config(self._chroot_dir + "/etc/make.conf", {key: options[key]})
365 self._logger.log("Make.conf configured")
366
367 ##
368 # This will get/update the portage tree. If you want to snapshot or mount /usr/portage use "custom".
369 def install_portage_tree(self):
370 # Check the type of portage tree fetching we'll do
371 # If it is custom, follow the path to the custom tarball and unpack it
372
373 # This is a hack to copy the LiveCD's rsync into the chroot since it has the sigmask patch
374 GLIUtility.spawn("cp -a /usr/bin/rsync " + self._chroot_dir + "/usr/bin/rsync")
375 GLIUtility.spawn("cp -a /usr/lib/libpopt* " + self._chroot_dir + "/usr/lib")
376
377 if self._install_profile.get_portage_tree_sync_type() == "snapshot" or self._install_profile.get_portage_tree_sync_type() == "custom": # Until this is finalized
378
379 # Get portage tree info
380 portage_tree_snapshot_uri = self._install_profile.get_portage_tree_snapshot_uri()
381 if portage_tree_snapshot_uri:
382 # Fetch and unpack the tarball
383 GLIUtility.fetch_and_unpack_tarball(portage_tree_snapshot_uri, self._chroot_dir + "/usr/", self._chroot_dir + "/")
384 self._logger.log("Portage tree install was custom.")
385 # If the type is webrsync, then run emerge-webrsync
386 elif self._install_profile.get_portage_tree_sync_type() == "webrsync":
387 exitstatus = GLIUtility.spawn("emerge-webrsync", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
388 if exitstatus != 0:
389 raise GLIException("EmergeWebRsyncError", 'fatal','install_portage_tre', "Failed to retrieve portage tree!")
390 self._logger.log("Portage tree sync'd using webrsync")
391 # Otherwise, just run emerge sync
392 else:
393 exitstatus = self._emerge("sync")
394 if exitstatus != 0:
395 raise GLIException("EmergeSyncError", 'fatal','install_portage_tree', "Failed to retrieve portage tree!")
396 self._logger.log("Portage tree sync'd")
397
398 ##
399 # Sets the timezone for the new environment
400 def set_timezone(self):
401
402 # Set symlink
403 if os.access(self._chroot_dir + "/etc/localtime", os.W_OK):
404 GLIUtility.spawn("rm "+self._chroot_dir + "/etc/localtime")
405 GLIUtility.spawn("ln -s ../usr/share/zoneinfo/" + self._install_profile.get_time_zone() + " /etc/localtime", chroot=self._chroot_dir)
406 if not (self._install_profile.get_time_zone() == "UTC"):
407 self._edit_config(self._chroot_dir + "/etc/rc.conf", {"CLOCK":"local"})
408 self._logger.log("Timezone set.")
409
410 ##
411 # Configures /etc/fstab on the new envorinment
412 def configure_fstab(self):
413 newfstab = ""
414 parts = self._install_profile.get_partition_tables()
415 for device in parts:
416 tmp_partitions = parts[device].get_install_profile_structure()
417 for partition in tmp_partitions:
418 mountpoint = tmp_partitions[partition]['mountpoint']
419 minor = str(int(tmp_partitions[partition]['minor']))
420 partition_type = tmp_partitions[partition]['type']
421 mountopts = tmp_partitions[partition]['mountopts']
422 if not mountopts.strip(): mountopts = "defaults"
423 if mountpoint:
424 if not GLIUtility.is_file(self._chroot_dir+mountpoint):
425 exitstatus = GLIUtility.spawn("mkdir -p " + self._chroot_dir + mountpoint)
426 if exitstatus != 0:
427 raise GLIException("MkdirError", 'fatal','configure_fstab', "Making the mount point failed!")
428 newfstab += device+minor+"\t "+mountpoint+"\t "+partition_type+"\t "+mountopts+"\t\t "
429 if mountpoint == "/boot":
430 newfstab += "1 2\n"
431 elif mountpoint == "/":
432 newfstab += "0 1\n"
433 else:
434 newfstab += "0 0\n"
435 if partition_type == "linux-swap":
436 newfstab += device+minor+"\t none swap sw 0 0\n"
437 newfstab += "none /proc proc defaults 0 0\n"
438 newfstab += "none /dev/shm tmpfs defaults 0 0\n"
439 if GLIUtility.is_device("/dev/cdroms/cdrom0"):
440 newfstab += "/dev/cdroms/cdrom0 /mnt/cdrom auto noauto,user 0 0\n"
441
442 for netmount in self._install_profile.get_network_mounts():
443 if netmount['type'] == "nfs":
444 newfstab += netmount['host'] + ":" + netmount['export'] + "\t" + netmount['mountpoint'] + "\tnfs\t" + netmount['mountopts'] + "\t0 0\n"
445
446 file_name = self._chroot_dir + "/etc/fstab"
447 try:
448 shutil.move(file_name, file_name + ".OLDdefault")
449 except:
450 pass
451 f = open(file_name, 'w')
452 f.writelines(newfstab)
453 f.close()
454 self._logger.log("fstab configured.")
455
456 ##
457 # Fetches desired kernel sources, unless you're using a livecd-kernel in which case it does freaky stuff.
458 def emerge_kernel_sources(self):
459 self._logger.log("Starting emerge_kernel")
460 kernel_pkg = self._install_profile.get_kernel_source_pkg()
461 # if kernel_pkg:
462 if kernel_pkg == "livecd-kernel":
463 PKGDIR = "/usr/portage/packages"
464 PORTAGE_TMPDIR = "/var/tmp"
465 make_conf = self._install_profile.get_make_conf()
466 if "PKGDIR" in make_conf: PKGDIR = make_conf['PKGDIR']
467 if "PORTAGE_TMPDIR" in make_conf: PORTAGE_TMPDIR = make_conf['PORTAGE_TMPDIR']
468 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PKGDIR, logfile=self._compile_logfile, append_log=True)
469 GLIUtility.spawn("mkdir -p " + self._chroot_dir + PORTAGE_TMPDIR, logfile=self._compile_logfile, append_log=True)
470 ret = GLIUtility.spawn("env PKGDIR=" + self._chroot_dir + PKGDIR + " PORTAGE_TMPDIR=" + self._chroot_dir + PORTAGE_TMPDIR + " quickpkg livecd-kernel")
471 ret = GLIUtility.spawn("env PKGDIR=" + PKGDIR + " emerge -K sys-kernel/livecd-kernel", chroot=self._chroot_dir)
472
473 #these are the hotplug/coldplug steps from build_kernel copied over here. they will NOT be run there.
474 exitstatus = self._emerge("hotplug")
475 if exitstatus != 0:
476 raise GLIException("EmergeHotplugError", 'fatal','build_kernel', "Could not emerge hotplug!")
477 self._logger.log("Hotplug emerged.")
478 exitstatus = self._emerge("coldplug")
479 if exitstatus != 0:
480 raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not emerge coldplug!")
481 self._logger.log("Coldplug emerged. Now they should be added to the default runlevel.")
482
483 self._add_to_runlevel("hotplug")
484 self._add_to_runlevel("coldplug", runlevel="boot")
485 else:
486 exitstatus = self._emerge(kernel_pkg)
487 if exitstatus != 0:
488 raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not retrieve kernel sources!")
489 try:
490 os.stat(self._chroot_dir + "/usr/src/linux")
491 except:
492 kernels = os.listdir(self._chroot_dir+"/usr/src")
493 found_a_kernel = False
494 counter = 0
495 while not found_a_kernel:
496 if kernels[counter][0:6]=="linux-":
497 exitstatus = GLIUtility.spawn("ln -s /usr/src/"+kernels[counter]+ " /usr/src/linux",chroot=self._chroot_dir)
498 if exitstatus != 0:
499 raise GLIException("EmergeKernelSourcesError", 'fatal','emerge_kernel_sources',"Could not make a /usr/src/linux symlink")
500 found_a_kernel = True
501 else:
502 counter = counter + 1
503 self._logger.log("Kernel sources:"+kernel_pkg+" emerged and /usr/src/linux symlinked.")
504
505 ##
506 # Builds the kernel using genkernel or regularly if given a custom .config file in the profile
507 def build_kernel(self):
508 self._logger.mark()
509 self._logger.log("Starting build_kernel")
510 # No building necessary if using the LiveCD's kernel/initrd
511 if self._install_profile.get_kernel_source_pkg() == "livecd-kernel": return
512 # Get the uri to the kernel config
513 kernel_config_uri = self._install_profile.get_kernel_config_uri()
514 if kernel_config_uri == "": #use genkernel if no specific config
515
516 exitstatus = self._emerge("genkernel")
517 if exitstatus != 0:
518 raise GLIException("EmergeGenKernelError", 'fatal','build_kernel', "Could not emerge genkernel!")
519 self._logger.log("Genkernel emerged. Beginning kernel compile.")
520 # Null the genkernel_options
521 genkernel_options = ""
522
523 # If the uri for the kernel config is not null, then
524 if kernel_config_uri != "":
525 GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/root/kernel_config")
526 genkernel_options = genkernel_options + " --kernel-config=/root/kernel_config"
527
528 # Decide whether to use bootsplash or not
529 if self._install_profile.get_kernel_bootsplash():
530 genkernel_options = genkernel_options + " --bootsplash"
531 else:
532 genkernel_options = genkernel_options + " --no-bootsplash"
533 # Run genkernel in chroot
534 #print "genkernel all " + genkernel_options
535 exitstatus = GLIUtility.spawn("genkernel all " + genkernel_options, chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
536 if exitstatus != 0:
537 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build kernel!")
538
539 exitstatus = self._emerge("hotplug")
540 if exitstatus != 0:
541 raise GLIException("EmergeHotplugError", 'fatal','build_kernel', "Could not emerge hotplug!")
542 self._logger.log("Hotplug emerged.")
543 exitstatus = self._emerge("coldplug")
544 if exitstatus != 0:
545 raise GLIException("EmergeColdplugError", 'fatal','build_kernel', "Could not emerge coldplug!")
546 self._logger.log("Coldplug emerged. Now they should be added to the default runlevel.")
547
548 self._add_to_runlevel("hotplug")
549 self._add_to_runlevel("coldplug", runlevel="boot")
550 self._logger.log("Genkernel complete.")
551 else: #CUSTOM CONFIG
552 #Copy the kernel .config to the proper location in /usr/src/linux
553 try:
554 GLIUtility.get_uri(kernel_config_uri, self._chroot_dir + "/root/kernel_config")
555 except:
556 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not copy kernel config!")
557
558 kernel_compile_script = "#!/bin/bash\n"
559 kernel_compile_script += "cp /root/kernel_config /usr/src/linux/.config\n"
560 kernel_compile_script += "cd /usr/src/linux\n"
561 kernel_compile_script += "make \nmake modules_install \n"
562
563 #Ok now that it's built, copy it to /boot/kernel-* for bootloader code to find it
564 if self._client_configuration.get_architecture_template() == "x86":
565 kernel_compile_script += "cp /usr/src/linux/arch/i386/boot/bzImage /boot/kernel-custom\n"
566 f = open(self._chroot_dir+"/root/kernel_script", 'w')
567 f.writelines(kernel_compile_script)
568 f.close()
569 #Build the kernel
570 exitstatus1 = GLIUtility.spawn("chmod u+x "+self._chroot_dir+"/root/kernel_script")
571 exitstatus2 = GLIUtility.spawn("/root/kernel_script", chroot=self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
572 if (exitstatus1 != 0) or (exitstatus2 != 0):
573 raise GLIException("KernelBuildError", 'fatal', 'build_kernel', "Could not build custom kernel!")
574
575 #i'm sure i'm forgetting something here.
576 #cleanup
577 exitstatus = GLIUtility.spawn("rm "+self._chroot_dir+"/root/kernel_script")
578 #it's not important if this fails.
579 self._logger.log("Custom kernel complete")
580
581 ##
582 # Installs and sets up logging daemon on the new system. adds to runlevel too.
583 def install_logging_daemon(self):
584
585 # Get loggin daemon info
586 logging_daemon_pkg = self._install_profile.get_logging_daemon_pkg()
587 if logging_daemon_pkg:
588 # Emerge Logging Daemon
589 exitstatus = self._emerge(logging_daemon_pkg)
590 if exitstatus != 0:
591 raise GLIException("LoggingDaemonError", 'fatal','install_logging_daemon', "Could not emerge " + logging_daemon_pkg + "!")
592
593 # Add Logging Daemon to default runlevel
594 self._add_to_runlevel(logging_daemon_pkg)
595 self._logger.log("Logging daemon installed: "+logging_daemon_pkg)
596 ##
597 # Installs and sets up cron package.
598 def install_cron_daemon(self):
599
600 # Get cron daemon info
601 cron_daemon_pkg = self._install_profile.get_cron_daemon_pkg()
602 if cron_daemon_pkg:
603 # Emerge Cron Daemon
604 exitstatus = self._emerge(cron_daemon_pkg)
605 if exitstatus != 0:
606 raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Could not emerge " + cron_daemon_pkg + "!")
607
608 # Add Cron Daemon to default runlevel
609 self._add_to_runlevel(cron_daemon_pkg)
610
611 # If the Cron Daemon is not vixie-cron, run crontab
612 if cron_daemon_pkg != "vixie-cron":
613 exitstatus = GLIUtility.spawn("crontab /etc/crontab", chroot=self._chroot_dir, display_on_tty8=True)
614 if exitstatus != 0:
615 raise GLIException("CronDaemonError", 'fatal', 'install_cron_daemon', "Failure making crontab!")
616 self._logger.log("Cron daemon installed and configured: "+cron_daemon_pkg)
617 ##
618 # This will parse the partitions looking for types that require fstools and emerge them if found.
619 def install_filesystem_tools(self):
620 "Installs and sets up fstools"
621 # Get the list of file system tools to be installed
622 parts = self._install_profile.get_partition_tables()
623 filesystem_tools = []
624 for device in parts:
625 tmp_partitions = parts[device].get_install_profile_structure()
626 for partition in tmp_partitions:
627 partition_type = tmp_partitions[partition]['type']
628 if partition_type not in filesystem_tools:
629 filesystem_tools.append(partition_type)
630 for filesystem in filesystem_tools:
631 if filesystem.lower() == "xfs":
632 exitstatus = self._emerge("xfsprogs")
633 if exitstatus != 0:
634 self._logger.log("ERROR! : Could not emerge xfsprogs!")
635 else:
636 self._logger.log("FileSystemTool xfsprogs was emerged successfully.")
637 if filesystem.lower() == "reiserfs":
638 exitstatus = self._emerge("reiserfsprogs")
639 if exitstatus != 0:
640 self._logger.log("ERROR! : Could not emerge reiserfsprogs!")
641 else:
642 self._logger.log("FileSystemTool reiserfsprogs was emerged successfully.")
643 if filesystem.lower() == "jfs":
644 exitstatus = self._emerge("jfsutils")
645 if exitstatus != 0:
646 self._logger.log("ERROR! : Could not emerge jfsutils!")
647 else:
648 self._logger.log("FileSystemTool jfsutils was emerged successfully.")
649
650 ##
651 # Installs rp-pppoe but does not configure it. This function is quite the unknown.
652 def install_rp_pppoe(self):
653
654 # If user wants us to install rp-pppoe, then do so
655 if self._install_profile.get_install_rp_pppoe():
656 exitstatus = self._emerge("rp-pppoe")
657 if exitstatus != 0:
658 self._logger.log("ERROR! : Could not emerge rp-pppoe!")
659 # raise GLIException("RP_PPPOEError", 'warning', 'install_rp_pppoe', "Could not emerge rp-pppoe!")
660 else:
661 self._logger.log("rp-pppoe emerged but not set up.")
662 # Should we add a section here to automatically configure rp-pppoe?
663 # I think it should go into the setup_network_post section
664 # What do you guys think? <-- said by unknown. samyron or npmcallum
665
666 ##
667 # Installs and sets up pcmcia-cs if selected in the profile
668 def install_pcmcia_cs(self):
669 # If user wants us to install pcmcia-cs, then do so
670 if self._install_profile.get_install_pcmcia_cs():
671 exitstatus = self._emerge("pcmcia-cs")
672 if exitstatus != 0:
673 self._logger.log("ERROR! : Could not emerge pcmcia-cs!")
674 # raise GLIException("PCMCIA_CSError", 'warning', 'install_pcmcia_cs', "Could not emerge pcmcia-cs!")
675
676 # Add pcmcia-cs to the default runlevel
677 else:
678 self._add_to_runlevel(pcmcia)
679 self._logger.log("PCMCIA_CS emerged and configured.")
680
681 ##
682 # This runs etc-update and then re-overwrites the files by running the configure_*'s to keep our values.
683 def update_config_files(self):
684 "Runs etc-update (overwriting all config files), then re-configures the modified ones"
685 # Run etc-update overwriting all config files
686 status = GLIUtility.spawn('echo "-5" | chroot '+self._chroot_dir+' etc-update', display_on_tty8=True)
687 if not GLIUtility.exitsuccess(status):
688 self._logger.log("ERROR! : Could not update the config files!")
689 # raise GLIException("EtcUpdateError", 'warning', 'update_config_files', "Could not update config files!")
690 else:
691 self.configure_make_conf()
692 self.configure_fstab()
693 self.configure_rc_conf()
694 self._logger.log("Config files updated using etc-update. make.conf/fstab/rc.conf restored.")
695
696 ##
697 # Configures /etc/rc.conf
698 def configure_rc_conf(self):
699
700 # Get make.conf options
701 options = self._install_profile.get_rc_conf()
702
703 # For each configuration option...
704 for key in options.keys():
705
706 # Add/Edit it into rc.conf
707 self._edit_config(self._chroot_dir + "/etc/rc.conf", {key: options[key]})
708 self._logger.log("rc.conf configured.")
709
710 ##
711 # Sets up the network for the first boot
712 def setup_network_post(self):
713
714 # Get hostname, domainname and nisdomainname
715 hostname = self._install_profile.get_hostname()
716 domainname = self._install_profile.get_domainname()
717 nisdomainname = self._install_profile.get_nisdomainname()
718
719 # Write the hostname to the hostname file
720 open(self._chroot_dir + "/etc/hostname", "w").write(hostname + "\n")
721
722 # Write the domainname to the nisdomainname file
723 if domainname:
724 open(self._chroot_dir + "/etc/dnsdomainname", "w").write(domainname + "\n")
725 self._add_to_runlevel("domainname")
726
727 # Write the nisdomainname to the nisdomainname file
728 if nisdomainname:
729 open(self._chroot_dir + "/etc/nisdomainname", "w").write(nisdomainname + "\n")
730 self._add_to_runlevel("domainname")
731
732 #
733 # EDIT THE /ETC/HOSTS FILE
734 #
735
736 # The address we are editing is 127.0.0.1
737 hosts_ip = "127.0.0.1"
738
739 # If the hostname is localhost
740 if hostname == "localhost":
741 # If a domainname is set
742 if domainname:
743 hosts_line = hostname + "." + domainname + "\t" + hostname
744 else:
745 hosts_line = hostname
746 # If the hostname is not localhost
747 else:
748 # If a domainname is set
749 if domainname:
750 hosts_line = hostname + "." + domainname + "\t" + hostname + "\tlocalhost"
751 else:
752 hosts_line = "localhost\t" + hostname
753
754 # Write to file
755 self._edit_config(self._chroot_dir + "/etc/hosts", {hosts_ip: hosts_line}, delimeter='\t', quotes_around_value=False)
756
757 #
758 # SET DEFAULT GATEWAY
759 #
760
761 # Get default gateway
762 default_gateway = self._install_profile.get_default_gateway()
763
764 # If the default gateway exists, add it
765 if default_gateway:
766 default_gateway_string = default_gateway[0] + "/" + default_gateway[1]
767 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"gateway": default_gateway_string})
768
769 #
770 # SET RESOLV INFO
771 #
772
773 # Get dns servers
774 dns_servers = self._install_profile.get_dns_servers()
775
776 # Clear the list
777 resolv_output = []
778
779 # If dns servers are set
780 if dns_servers:
781
782
783 # Parse each dns server
784 for dns_server in dns_servers:
785 # Add the server to the output
786 resolv_output.append("nameserver " + dns_server +"\n")
787
788 # If the domainname is set, then also output it
789 if domainname:
790 resolv_output.append("search " + domainname + "\n")
791
792 # Output to file
793 resolve_conf = open(self._chroot_dir + "/etc/resolv.conf", "w")
794 resolve_conf.writelines(resolv_output)
795 resolve_conf.close()
796
797 #
798 # PARSE INTERFACES
799 #
800
801 # Fetch interfaces
802 interfaces = self._install_profile.get_network_interfaces()
803 emerge_dhcp = False
804 # Parse each interface
805 for interface in interfaces.keys():
806
807 # Set what kind of interface it is
808 interface_type = interface[:3]
809
810 # Check to see if there is a startup script for this interface, if there isn't link to the proper script
811 try:
812 os.stat(self._chroot_dir + "/etc/init.d/net." + interface)
813 except:
814 os.symlink(self._chroot_dir + "/etc/init.d/net." + interface_type + "0", self._chroot_dir + "/etc/init.d/net." + interface)
815
816 # If we are going to load the network at boot...
817 #if interfaces[interface][2]: #THIS FEATURE NO LONGER EXISTS
818
819 # Add it to the default runlevel
820 self._add_to_runlevel("net."+interface) # moved a bit <-- for indentation
821
822 #
823 # ETHERNET
824 #
825 if interface_type == "eth":
826
827 #
828 # STATIC IP
829 #
830 # If the post-install device info is not None, then it is a static ip addy
831 if interfaces[interface][1]:
832 ip = interfaces[interface][0]
833 broadcast = interfaces[interface][1]
834 netmask = interfaces[interface][2]
835 # aliases = interfaces[interface][1][3]
836 # alias_ips = []
837 # alias_broadcasts = []
838 # alias_netmasks = []
839
840 # Write the static ip config to /etc/conf.d/net
841 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"iface_" + interface: ip + " broadcast " + broadcast + " netmask " + netmask})
842
843 # If aliases are set
844 # if aliases:
845
846 # Parse aliases to format alias info
847 # for alias in aliases:
848 # alias_ips.append(alias[0])
849 # alias_broadcasts.append(alias[1])
850 # alias_netmasks.append(allias[2])
851
852 # Once the alias info has been gathered, then write it out
853 # Alias ips first
854 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "alias_" + interface, string.join(alias_ips))
855 # Alias broadcasts next
856 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "broadcast_" + interface, string.join(alias_broadcasts))
857 # Alias netmasks last
858 # self._edit_config(self._chroot_dir + "/etc/conf.d/net", "netmask_" + interface, string.join(alias_netmasks))
859
860 #
861 # DHCP IP
862 #
863 else:
864 self._edit_config(self._chroot_dir + "/etc/conf.d/net", {"iface_" + interface: "dhcp"})
865 emerge_dhcp = True
866 if emerge_dhcp:
867 exitstatus = self._emerge("dhcpcd")
868 if exitstatus != 0:
869 self._logger.log("ERROR! : Could not emerge dhcpcd!")
870 else:
871 self._logger.log("dhcpcd emerged.")
872
873 ##
874 # Sets the root password
875 def set_root_password(self):
876 status = GLIUtility.spawn('echo \'root:' + self._install_profile.get_root_pass_hash() + '\' | chroot '+self._chroot_dir+' chpasswd -e')
877 if not GLIUtility.exitsuccess(status):
878 raise GLIException("SetRootPasswordError", 'fatal', 'set_root_password', "Failure to set root password!")
879 self._logger.log("Root Password set on the new system.")
880
881 ##
882 # Sets up the new users for the system
883 def set_users(self):
884 # Loop for each user
885 for user in self._install_profile.get_users():
886
887 # Get values from the tuple
888 username = user[0]
889 password_hash = user[1]
890 groups = user[2]
891 shell = user[3]
892 home_dir = user[4]
893 uid = user[5]
894 comment = user[6]
895
896 options = [ "-m", "-p '" + password_hash + "'" ]
897
898 # If the groups are specified
899 if groups:
900
901 # If just one group is listed as a string, make it a list
902 if groups == str:
903 groups = [ groups ]
904
905 # If only 1 group is listed
906 if len(groups) == 1:
907 options.append("-G " + groups[0])
908
909 # If there is more than one group
910 elif len(groups) > 1:
911 options.append('-G "' + string.join(groups, ",") + '"')
912
913 # Attempt to add the group (will return success when group exists)
914 for group in groups:
915 # Add the user
916 exitstatus = GLIUtility.spawn('groupadd -f ' + group, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True)
917 if not GLIUtility.exitsuccess(exitstatus):
918 self._logger.log("ERROR! : Failure to add group " + group+" and it wasn't that the group already exists!")
919
920
921 # If a shell is specified
922 if shell:
923 options.append("-s " + shell)
924
925 # If a home dir is specified
926 if home_dir:
927 options.append("-d " + home_dir)
928
929 # If a UID is specified
930 if uid:
931 options.append("-u " + str(uid))
932
933 # If a comment is specified
934 if comment:
935 options.append('-c "' + comment + '"')
936
937 # Add the user
938 exitstatus = GLIUtility.spawn('useradd ' + string.join(options) + ' ' + username, chroot=self._chroot_dir, logfile=self._compile_logfile, append_log=True, display_on_tty8=True)
939 if not GLIUtility.exitsuccess(exitstatus):
940 self._logger.log("ERROR! : Failure to add user " + username)
941 # raise GLIException("AddUserError", 'warning', 'set_users', "Failure to add user " + username)
942 else:
943 self._logger.log("User "+username+"was added.")
944
945 ##
946 # This function will handle the various cleanup tasks as well as unmounting the filesystems for reboot.
947 def finishing_cleanup(self):
948 #These are temporary until I come up with a nicer idea.
949 #get rid of the compile_output file so the symlink doesn't get screwed up.
950
951 #we copy the log over to the new system.
952 install_logfile = self._client_configuration.get_log_file()
953 try:
954 shutil.copy(install_logfile, self._chroot_dir + install_logfile)
955 except:
956 pass
957 #Now we're done logging as far as the new system is concerned.
958 GLIUtility.spawn("cp /tmp/installprofile.xml " + self._chroot_dir + "/root/installprofile.xml")
959 GLIUtility.spawn("cp /tmp/clientconfiguration.xml " + self._chroot_dir + "/root/clientconfiguration.xml")
960
961 #Unmount mounted fileystems in preparation for reboot
962 mounts = GLIUtility.spawn(r"mount | sed -e 's:^.\+ on \(.\+\) type .\+$:\1:' | grep -e '^" + self._chroot_dir + "' | sort -r", return_output=True)[1].split("\n")
963 for mount in mounts:
964 GLIUtility.spawn("umount -l " + mount)
965
966 #OLD WAY: Unmount the /proc and /dev that we mounted in prepare_chroot
967 #There really isn't a reason to log errors here.
968 #ret = GLIUtility.spawn("umount "+self._chroot_dir+"/proc", display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
969 #ret = GLIUtility.spawn("umount "+self._chroot_dir+"/dev", display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
970 #temp hack to unmount the new root.
971 #ret = GLIUtility.spawn("umount "+self._chroot_dir, display_on_tty8=True, logfile=self._compile_logfile, append_log=True)
972 #insert code here to unmount the swap partition, if there is one.
973
974 GLIUtility.spawn("rm /tmp/compile_output.log && rm " + install_logfile)
975
976 ##
977 # This is a stub function to be done by the individual arch. I don't think it's even needed here.
978 # but it's nice having it just incase.
979 def install_bootloader(self):
980 "THIS FUNCTION MUST BE DONE BY THE INDIVIDUAL ARCH"
981 pass
982
983 def run_post_install_script(self):
984 if self._install_profile.get_post_install_script_uri():
985 try:
986 GLIUtility.get_uri(self._install_profile.get_post_install_script_uri(), self._chroot_dir + "/var/tmp/post-install")
987 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)
988 except:
989 raise GLIException("RunPostInstallScriptError", 'fatal', 'run_post_install_script', "Failed to retrieve and/or execute post-install script")
990
991 ##
992 # This function adds user-defined entries to the files in /etc/portage
993 #
994 def set_etc_portage(self):
995 etc_portage = self._install_profile.get_etc_portage()
996
997 # Loop through the required files.
998 for file in etc_portage:
999 contents = enumerate(etc_portage[file])
1000 self._logger.log("Configuring /etc/portage/" + file)
1001 self._edit_config(self._chroot_dir + "/etc/portage/" + file, {"COMMENT": "GLI additions ===>"})
1002
1003 # Set up the contents hash to pass to the config writer.
1004 contents = {}
1005 for key,value in enumerate(etc_portage[file]):
1006 contents[str(key)] = string.strip(value)
1007
1008 # Write out the contents in one go.
1009 self._edit_config(self._chroot_dir + "/etc/portage/" + file, contents, "", False, True)
1010
1011 self._edit_config(self._chroot_dir + "/etc/make.conf", {"COMMENT": "<=== End GLI additions"})
1012 self._logger.log("Finished configuring /etc/portage/" + file)

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20