3 # Copyright (c) 2003 Intel Corporation
6 # Copyright (c) 2004-2007 The Trustees of Princeton University
8 # expected /proc/partitions format
16 from Exceptions import *
19 import BootServerRequest
23 def Run(vars, upgrade, log):
25 Download core + extensions bootstrapfs tarballs and install on the hard drive
27 the upgrade boolean is True when we are upgrading a node root install while
28 preserving its slice contents; in that case we just perform extra cleanup
29 before unwrapping the bootstrapfs
30 this is because the running system may have extraneous files
31 that is to say, files that are *not* present in the bootstrapfs
32 and that can impact/clobber the resulting upgrade
34 Expect the following variables from the store:
35 SYSIMG_PATH the path where the system image will be mounted
36 PARTITIONS dictionary of generic part. types (root/swap)
37 and their associated devices.
38 NODE_ID the id of this machine
40 Sets the following variables:
41 TEMP_BOOTCD_PATH where the boot cd is remounted in the temp
43 ROOT_MOUNTED set to 1 when the the base logical volumes
47 log.write("\n\nStep: Install: bootstrapfs tarball (upgrade={}).\n".format(upgrade))
49 # make sure we have the variables we need
51 SYSIMG_PATH = vars["SYSIMG_PATH"]
53 raise ValueError("SYSIMG_PATH")
55 PARTITIONS = vars["PARTITIONS"]
56 if PARTITIONS == None:
57 raise ValueError("PARTITIONS")
59 NODE_ID = vars["NODE_ID"]
61 raise ValueError("NODE_ID")
63 VERSION = vars['VERSION'] or 'unknown'
65 except KeyError as var:
66 raise BootManagerException("Missing variable in vars: {}\n".format(var))
67 except ValueError as var:
68 raise BootManagerException("Variable in vars, shouldn't be: {}\n".format(var))
72 # make sure the required partitions exist
73 val = PARTITIONS["root"]
74 val = PARTITIONS["swap"]
75 val = PARTITIONS["vservers"]
76 except KeyError as part:
77 log.write("Missing partition in PARTITIONS: {}\n".format(part))
80 bs_request = BootServerRequest.BootServerRequest(vars)
82 # in upgrade mode, since we skip InstallPartitionDisks
85 log.write("Upgrade mode init : Scanning for devices\n")
86 systeminfo.get_block_devices_dict(vars, log)
87 utils.sysexec_noerr("vgscan --mknodes", log)
89 # debugging info - show in either mode
90 utils.display_disks_status(PARTITIONS, "In InstallBootstrapFS", log)
92 utils.breakpoint("we need to make /dev/mapper/* appear")
94 log.write("turning on swap space\n")
95 utils.sysexec("swapon {}".format(PARTITIONS["swap"]), log)
97 # make sure the sysimg dir is present
98 utils.makedirs(SYSIMG_PATH)
100 log.write("mounting root file system\n")
101 utils.sysexec("mount -t ext3 {} {}".format(PARTITIONS["root"], SYSIMG_PATH), log)
103 fstype = 'ext3' if vars['virt']=='vs' else 'btrfs'
105 one_partition = vars['ONE_PARTITION']=='1'
107 if (not one_partition):
108 log.write("mounting vserver partition in root file system (type {})\n".format(fstype))
109 utils.makedirs(SYSIMG_PATH + "/vservers")
110 utils.sysexec("mount -t {} {} {}/vservers"\
111 .format(fstype, PARTITIONS["vservers"], SYSIMG_PATH), log)
113 if vars['virt']=='lxc':
114 # NOTE: btrfs quota is supported from version: >= btrfs-progs-0.20 (f18+)
115 # older versions will not recongize the 'quota' command.
116 log.write("Enabling btrfs quota on {}/vservers\n".format(SYSIMG_PATH))
117 utils.sysexec_noerr("btrfs quota enable {}/vservers".format(SYSIMG_PATH))
119 vars['ROOT_MOUNTED'] = 1
121 # this is now retrieved in GetAndUpdateNodeDetails
122 nodefamily = vars['nodefamily']
123 extensions = vars['extensions']
125 # in upgrade mode: we need to cleanup the disk to make
126 # it safe to just untar the new bootstrapfs tarball again
127 # on top of the hard drive
129 CleanupSysimgBeforeUpgrade(SYSIMG_PATH, nodefamily, log)
131 # the 'plain' option is for tests mostly
132 plain = vars['plain']
134 download_suffix = ".tar"
135 uncompress_option = ""
136 log.write("Using plain bootstrapfs images\n")
138 download_suffix = ".tar.bz2"
139 uncompress_option = "-j"
140 log.write("Using compressed bootstrapfs images\n")
142 log.write ("Using nodefamily={}\n".format(nodefamily))
144 log.write("Installing only core software\n")
146 log.write("Requested extensions {}\n".format(extensions))
148 bootstrapfs_names = [ nodefamily ] + extensions
150 for name in bootstrapfs_names:
151 tarball = "bootstrapfs-{}{}".format(name, download_suffix)
152 source_file = "/boot/{}".format(tarball)
153 dest_file = "{}/{}".format(SYSIMG_PATH, tarball)
155 source_hash_file = "/boot/{}.sha1sum".format(tarball)
156 dest_hash_file = "{}/{}.sha1sum".format(SYSIMG_PATH, tarball)
158 time_beg = time.time()
159 log.write("downloading {}\n".format(source_file))
160 # 30 is the connect timeout, 14400 is the max transfer time in
162 result = bs_request.DownloadFile(source_file, None, None,
165 time_end = time.time()
166 duration = int(time_end - time_beg)
167 log.write("Done downloading ({} seconds)\n".format(duration))
169 # Download SHA1 checksum file
170 log.write("downloading sha1sum for {}\n".format(source_file))
171 result = bs_request.DownloadFile(source_hash_file, None, None,
172 1, 1, dest_hash_file,
175 log.write("verifying sha1sum for {}\n".format(source_file))
176 if not utils.check_file_hash(dest_file, dest_hash_file):
177 raise BootManagerException(
178 "FATAL: SHA1 checksum does not match between {} and {}"\
179 .format(source_file, source_hash_file))
182 time_beg = time.time()
183 log.write("extracting {} in {}\n".format(dest_file, SYSIMG_PATH))
184 result = utils.sysexec("tar -C {} -xpf {} {}".format(SYSIMG_PATH, dest_file, uncompress_option), log)
185 time_end = time.time()
186 duration = int(time_end - time_beg)
187 log.write("Done extracting ({} seconds)\n".format(duration))
188 utils.removefile(dest_file)
190 # the main tarball is required
191 if name == nodefamily:
192 raise BootManagerException(
193 "FATAL: Unable to download main tarball {} from server."\
194 .format(source_file))
195 # for extensions, just issue a warning
197 log.write("WARNING: tarball for extension {} not found\n".format(name))
199 # copy resolv.conf from the base system into our temp dir
200 # so DNS lookups work correctly while we are chrooted
201 log.write("Copying resolv.conf to temp dir\n")
202 utils.sysexec("cp /etc/resolv.conf {}/etc/".format(SYSIMG_PATH), log)
204 # Copy the boot server certificate(s) and GPG public key to
205 # /usr/boot in the temp dir.
206 log.write("Copying boot server certificates and public key\n")
208 if os.path.exists("/usr/boot"):
209 # do nothing in case of upgrade
210 if not os.path.exists(SYSIMG_PATH + "/usr/boot"):
211 utils.makedirs(SYSIMG_PATH + "/usr")
212 shutil.copytree("/usr/boot", SYSIMG_PATH + "/usr/boot")
213 elif os.path.exists("/usr/bootme"):
214 # do nothing in case of upgrade
215 if not os.path.exists(SYSIMG_PATH + "/usr/bootme"):
216 utils.makedirs(SYSIMG_PATH + "/usr/boot")
217 boot_server = file("/usr/bootme/BOOTSERVER").readline().strip()
218 shutil.copy("/usr/bootme/cacert/" + boot_server + "/cacert.pem",
219 SYSIMG_PATH + "/usr/boot/cacert.pem")
220 file(SYSIMG_PATH + "/usr/boot/boot_server", "w").write(boot_server)
221 shutil.copy("/usr/bootme/pubring.gpg", SYSIMG_PATH + "/usr/boot/pubring.gpg")
223 # For backward compatibility
224 if os.path.exists("/usr/bootme"):
225 # do nothing in case of upgrade
226 if not os.path.exists(SYSIMG_PATH + "/mnt/cdrom/bootme"):
227 utils.makedirs(SYSIMG_PATH + "/mnt/cdrom")
228 shutil.copytree("/usr/bootme", SYSIMG_PATH + "/mnt/cdrom/bootme")
230 # ONE_PARTITION => new distribution type
231 if (vars['ONE_PARTITION'] != '1'):
232 # Import the GPG key into the RPM database so that RPMS can be verified
233 utils.makedirs(SYSIMG_PATH + "/etc/pki/rpm-gpg")
234 utils.sysexec("gpg --homedir=/root --export --armor"
235 " --no-default-keyring --keyring {}/usr/boot/pubring.gpg"
236 " > {}/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab".format(SYSIMG_PATH, SYSIMG_PATH), log)
237 utils.sysexec_chroot(SYSIMG_PATH, "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab", log)
239 # keep a log on the installed hdd
240 stamp = file(SYSIMG_PATH + "/bm-install.txt", 'a')
241 now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
242 stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
243 stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
244 # do not modify this, the upgrade code uses this line for checking compatibility
245 stamp.write("Using nodefamily {}\n".format(nodefamily))
251 def CleanupSysimgBeforeUpgrade(sysimg, target_nodefamily, log):
262 target_pldistro, target_fcdistro, target_arch = target_nodefamily.split('-')
264 # minimal check : not all configurations are possible...
266 installed_pldistro, installed_fcdistro, installed_arch = None, None, None
267 installed_virt = None
268 prefix = "Using nodefamily "
270 with open("{}/bm-install.txt".format(sysimg)) as infile:
272 if line.startswith(prefix):
273 installed_nodefamily = line.replace(prefix,"").strip()
274 installed_pldistro, installed_fcdistro, installed_arch = installed_nodefamily.split('-')
275 # do not break here, bm-install is additive, we want the last one..
276 with open("{}/etc/planetlab/virt".format(sysimg)) as infile:
277 installed_virt = infile.read().strip()
278 except Exception as e:
280 raise BootManagerException("Could not retrieve data about previous installation - cannot upgrade")
282 # moving from vservers to lxc also means another filesystem
283 # so plain reinstall is the only option
284 if installed_virt != 'lxc':
285 raise BootManagerException("Can only upgrade nodes running lxc containers (vservers not supported)")
287 # changing arch is not reasonable either
288 if target_arch != installed_arch:
289 raise BootManagerException("Cannot upgrade from arch={} to arch={}"
290 .format(installed_arch, target_arch))
292 if target_pldistro != installed_pldistro:
293 log.write("\nWARNING: upgrading across pldistros {} to {} - might not work well..\n"
294 .format(installed_pldistro, target_pldistro))
296 # otherwise at this point we do not do any more advanced checking
297 log.write("\n\nPseudo step CleanupSysimgBeforeUpgrade : cleaning up hard drive\n")
299 for area in areas_to_cleanup:
300 utils.sysexec("rm -rf {}/{}".format(sysimg, area))