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)
88 utils.sysexec_noerr("vgchange -ay", log)
90 # debugging info - show in either mode
91 utils.display_disks_status(PARTITIONS, "In InstallBootstrapFS", log)
93 utils.breakpoint("we need to make /dev/mapper/* appear")
95 log.write("turning on swap space\n")
96 utils.sysexec("swapon {}".format(PARTITIONS["swap"]), log)
98 # make sure the sysimg dir is present
99 utils.makedirs(SYSIMG_PATH)
101 log.write("mounting root file system\n")
102 utils.sysexec("mount -t ext3 {} {}".format(PARTITIONS["root"], SYSIMG_PATH), log)
104 fstype = 'ext3' if vars['virt']=='vs' else 'btrfs'
106 one_partition = vars['ONE_PARTITION']=='1'
108 if (not one_partition):
109 log.write("mounting vserver partition in root file system (type {})\n".format(fstype))
110 utils.makedirs(SYSIMG_PATH + "/vservers")
111 utils.sysexec("mount -t {} {} {}/vservers"\
112 .format(fstype, PARTITIONS["vservers"], SYSIMG_PATH), log)
114 if vars['virt']=='lxc':
115 # NOTE: btrfs quota is supported from version: >= btrfs-progs-0.20 (f18+)
116 # older versions will not recongize the 'quota' command.
117 log.write("Enabling btrfs quota on {}/vservers\n".format(SYSIMG_PATH))
118 utils.sysexec_noerr("btrfs quota enable {}/vservers".format(SYSIMG_PATH))
120 vars['ROOT_MOUNTED'] = 1
122 # this is now retrieved in GetAndUpdateNodeDetails
123 nodefamily = vars['nodefamily']
124 extensions = vars['extensions']
126 # in upgrade mode: we need to cleanup the disk to make
127 # it safe to just untar the new bootstrapfs tarball again
128 # on top of the hard drive
130 CleanupSysimgBeforeUpgrade(SYSIMG_PATH, nodefamily, log)
132 # the 'plain' option is for tests mostly
133 plain = vars['plain']
135 download_suffix = ".tar"
136 uncompress_option = ""
137 log.write("Using plain bootstrapfs images\n")
139 download_suffix = ".tar.bz2"
140 uncompress_option = "-j"
141 log.write("Using compressed bootstrapfs images\n")
143 log.write ("Using nodefamily={}\n".format(nodefamily))
145 log.write("Installing only core software\n")
147 log.write("Requested extensions {}\n".format(extensions))
149 bootstrapfs_names = [ nodefamily ] + extensions
151 for name in bootstrapfs_names:
152 tarball = "bootstrapfs-{}{}".format(name, download_suffix)
153 source_file = "/boot/{}".format(tarball)
154 dest_file = "{}/{}".format(SYSIMG_PATH, tarball)
156 source_hash_file = "/boot/{}.sha1sum".format(tarball)
157 dest_hash_file = "{}/{}.sha1sum".format(SYSIMG_PATH, tarball)
159 time_beg = time.time()
160 log.write("downloading {}\n".format(source_file))
161 # 30 is the connect timeout, 14400 is the max transfer time in
163 result = bs_request.DownloadFile(source_file, None, None,
166 time_end = time.time()
167 duration = int(time_end - time_beg)
168 log.write("Done downloading ({} seconds)\n".format(duration))
170 # Download SHA1 checksum file
171 log.write("downloading sha1sum for {}\n".format(source_file))
172 result = bs_request.DownloadFile(source_hash_file, None, None,
173 1, 1, dest_hash_file,
176 log.write("verifying sha1sum for {}\n".format(source_file))
177 if not utils.check_file_hash(dest_file, dest_hash_file):
178 raise BootManagerException(
179 "FATAL: SHA1 checksum does not match between {} and {}"\
180 .format(source_file, source_hash_file))
183 time_beg = time.time()
184 log.write("extracting {} in {}\n".format(dest_file, SYSIMG_PATH))
185 result = utils.sysexec("tar -C {} -xpf {} {}".format(SYSIMG_PATH, dest_file, uncompress_option), log)
186 time_end = time.time()
187 duration = int(time_end - time_beg)
188 log.write("Done extracting ({} seconds)\n".format(duration))
189 utils.removefile(dest_file)
191 # the main tarball is required
192 if name == nodefamily:
193 raise BootManagerException(
194 "FATAL: Unable to download main tarball {} from server."\
195 .format(source_file))
196 # for extensions, just issue a warning
198 log.write("WARNING: tarball for extension {} not found\n".format(name))
200 # copy resolv.conf from the base system into our temp dir
201 # so DNS lookups work correctly while we are chrooted
202 log.write("Copying resolv.conf to temp dir\n")
203 utils.sysexec("cp /etc/resolv.conf {}/etc/".format(SYSIMG_PATH), log)
205 # Copy the boot server certificate(s) and GPG public key to
206 # /usr/boot in the temp dir.
207 log.write("Copying boot server certificates and public key\n")
209 if os.path.exists("/usr/boot"):
210 # do nothing in case of upgrade
211 if not os.path.exists(SYSIMG_PATH + "/usr/boot"):
212 utils.makedirs(SYSIMG_PATH + "/usr")
213 shutil.copytree("/usr/boot", SYSIMG_PATH + "/usr/boot")
214 elif os.path.exists("/usr/bootme"):
215 # do nothing in case of upgrade
216 if not os.path.exists(SYSIMG_PATH + "/usr/bootme"):
217 utils.makedirs(SYSIMG_PATH + "/usr/boot")
218 boot_server = file("/usr/bootme/BOOTSERVER").readline().strip()
219 shutil.copy("/usr/bootme/cacert/" + boot_server + "/cacert.pem",
220 SYSIMG_PATH + "/usr/boot/cacert.pem")
221 file(SYSIMG_PATH + "/usr/boot/boot_server", "w").write(boot_server)
222 shutil.copy("/usr/bootme/pubring.gpg", SYSIMG_PATH + "/usr/boot/pubring.gpg")
224 # For backward compatibility
225 if os.path.exists("/usr/bootme"):
226 # do nothing in case of upgrade
227 if not os.path.exists(SYSIMG_PATH + "/mnt/cdrom/bootme"):
228 utils.makedirs(SYSIMG_PATH + "/mnt/cdrom")
229 shutil.copytree("/usr/bootme", SYSIMG_PATH + "/mnt/cdrom/bootme")
231 # ONE_PARTITION => new distribution type
232 if (vars['ONE_PARTITION'] != '1'):
233 # Import the GPG key into the RPM database so that RPMS can be verified
234 utils.makedirs(SYSIMG_PATH + "/etc/pki/rpm-gpg")
235 utils.sysexec("gpg --homedir=/root --export --armor"
236 " --no-default-keyring --keyring {}/usr/boot/pubring.gpg"
237 " > {}/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab".format(SYSIMG_PATH, SYSIMG_PATH), log)
238 utils.sysexec_chroot(SYSIMG_PATH, "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab", log)
240 # keep a log on the installed hdd
241 stamp = file(SYSIMG_PATH + "/bm-install.txt", 'a')
242 now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
243 stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
244 stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
245 # do not modify this, the upgrade code uses this line for checking compatibility
246 stamp.write("Using nodefamily {}\n".format(nodefamily))
252 def CleanupSysimgBeforeUpgrade(sysimg, target_nodefamily, log):
263 target_pldistro, target_fcdistro, target_arch = target_nodefamily.split('-')
265 # minimal check : not all configurations are possible...
267 installed_pldistro, installed_fcdistro, installed_arch = None, None, None
268 installed_virt = None
269 prefix = "Using nodefamily "
271 with open("{}/bm-install.txt".format(sysimg)) as infile:
273 if line.startswith(prefix):
274 installed_nodefamily = line.replace(prefix,"").strip()
275 installed_pldistro, installed_fcdistro, installed_arch = installed_nodefamily.split('-')
276 # do not break here, bm-install is additive, we want the last one..
277 with open("{}/etc/planetlab/virt".format(sysimg)) as infile:
278 installed_virt = infile.read().strip()
279 except Exception as e:
280 traceback.print_exc()
281 raise BootManagerException("Could not retrieve data about previous installation - cannot upgrade")
283 # moving from vservers to lxc also means another filesystem
284 # so plain reinstall is the only option
285 if installed_virt != 'lxc':
286 message = """Can only upgrade nodes already running lxc containers
287 a node running vservers has its /vservers/ partition formatted as ext3
288 and we need btrfs to move to containers
289 your only option here is reinstall"""
290 raise BootManagerException(message)
292 # changing arch is not reasonable either
293 if target_arch != installed_arch:
294 raise BootManagerException("Cannot upgrade from arch={} to arch={}"
295 .format(installed_arch, target_arch))
297 if target_pldistro != installed_pldistro:
298 log.write("\nWARNING: upgrading across pldistros {} to {} - might not work well..\n"
299 .format(installed_pldistro, target_pldistro))
301 # otherwise at this point we do not do any more advanced checking
302 log.write("\n\nPseudo step CleanupSysimgBeforeUpgrade : cleaning up hard drive\n")
304 for area in areas_to_cleanup:
305 utils.sysexec("rm -rf {}/{}".format(sysimg, area))