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 log.write("In InstallBootstrapFS : DEVICES status BEG\n")
91 utils.sysexec_noerr("vgdisplay", log)
92 utils.sysexec_noerr("pvdisplay", log)
93 for name, path in PARTITIONS.items():
94 log.write("PARTITIONS[{}]={}\n".format(name,path))
95 utils.sysexec_noerr("ls -l {}".format(path), log)
96 log.write("In InstallBootstrapFS : DEVICES status END\n")
98 log.write("turning on swap space\n")
99 utils.sysexec("swapon {}".format(PARTITIONS["swap"]), log)
101 # make sure the sysimg dir is present
102 utils.makedirs(SYSIMG_PATH)
104 log.write("mounting root file system\n")
105 utils.sysexec("mount -t ext3 {} {}".format(PARTITIONS["root"], SYSIMG_PATH), log)
107 fstype = 'ext3' if vars['virt']=='vs' else 'btrfs'
109 one_partition = vars['ONE_PARTITION']=='1'
111 if (not one_partition):
112 log.write("mounting vserver partition in root file system (type {})\n".format(fstype))
113 utils.makedirs(SYSIMG_PATH + "/vservers")
114 utils.sysexec("mount -t {} {} {}/vservers"\
115 .format(fstype, PARTITIONS["vservers"], SYSIMG_PATH), log)
117 if vars['virt']=='lxc':
118 # NOTE: btrfs quota is supported from version: >= btrfs-progs-0.20 (f18+)
119 # older versions will not recongize the 'quota' command.
120 log.write("Enabling btrfs quota on {}/vservers\n".format(SYSIMG_PATH))
121 utils.sysexec_noerr("btrfs quota enable {}/vservers".format(SYSIMG_PATH))
123 vars['ROOT_MOUNTED'] = 1
125 # this is now retrieved in GetAndUpdateNodeDetails
126 nodefamily = vars['nodefamily']
127 extensions = vars['extensions']
129 # in upgrade mode: we need to cleanup the disk to make
130 # it safe to just untar the new bootstrapfs tarball again
131 # on top of the hard drive
133 CleanupSysimgBeforeUpgrade(SYSIMG_PATH, nodefamily, log)
135 # the 'plain' option is for tests mostly
136 plain = vars['plain']
138 download_suffix = ".tar"
139 uncompress_option = ""
140 log.write("Using plain bootstrapfs images\n")
142 download_suffix = ".tar.bz2"
143 uncompress_option = "-j"
144 log.write("Using compressed bootstrapfs images\n")
146 log.write ("Using nodefamily={}\n".format(nodefamily))
148 log.write("Installing only core software\n")
150 log.write("Requested extensions {}\n".format(extensions))
152 bootstrapfs_names = [ nodefamily ] + extensions
154 for name in bootstrapfs_names:
155 tarball = "bootstrapfs-{}{}".format(name, download_suffix)
156 source_file = "/boot/{}".format(tarball)
157 dest_file = "{}/{}".format(SYSIMG_PATH, tarball)
159 source_hash_file = "/boot/{}.sha1sum".format(tarball)
160 dest_hash_file = "{}/{}.sha1sum".format(SYSIMG_PATH, tarball)
162 time_beg = time.time()
163 log.write("downloading {}\n".format(source_file))
164 # 30 is the connect timeout, 14400 is the max transfer time in
166 result = bs_request.DownloadFile(source_file, None, None,
169 time_end = time.time()
170 duration = int(time_end - time_beg)
171 log.write("Done downloading ({} seconds)\n".format(duration))
173 # Download SHA1 checksum file
174 log.write("downloading sha1sum for {}\n".format(source_file))
175 result = bs_request.DownloadFile(source_hash_file, None, None,
176 1, 1, dest_hash_file,
179 log.write("verifying sha1sum for {}\n".format(source_file))
180 if not utils.check_file_hash(dest_file, dest_hash_file):
181 raise BootManagerException(
182 "FATAL: SHA1 checksum does not match between {} and {}"\
183 .format(source_file, source_hash_file))
186 time_beg = time.time()
187 log.write("extracting {} in {}\n".format(dest_file, SYSIMG_PATH))
188 result = utils.sysexec("tar -C {} -xpf {} {}".format(SYSIMG_PATH, dest_file, uncompress_option), log)
189 time_end = time.time()
190 duration = int(time_end - time_beg)
191 log.write("Done extracting ({} seconds)\n".format(duration))
192 utils.removefile(dest_file)
194 # the main tarball is required
195 if name == nodefamily:
196 raise BootManagerException(
197 "FATAL: Unable to download main tarball {} from server."\
198 .format(source_file))
199 # for extensions, just issue a warning
201 log.write("WARNING: tarball for extension {} not found\n".format(name))
203 # copy resolv.conf from the base system into our temp dir
204 # so DNS lookups work correctly while we are chrooted
205 log.write("Copying resolv.conf to temp dir\n")
206 utils.sysexec("cp /etc/resolv.conf {}/etc/".format(SYSIMG_PATH), log)
208 # Copy the boot server certificate(s) and GPG public key to
209 # /usr/boot in the temp dir.
210 log.write("Copying boot server certificates and public key\n")
212 if os.path.exists("/usr/boot"):
213 # do nothing in case of upgrade
214 if not os.path.exists(SYSIMG_PATH + "/usr/boot"):
215 utils.makedirs(SYSIMG_PATH + "/usr")
216 shutil.copytree("/usr/boot", SYSIMG_PATH + "/usr/boot")
217 elif os.path.exists("/usr/bootme"):
218 # do nothing in case of upgrade
219 if not os.path.exists(SYSIMG_PATH + "/usr/bootme"):
220 utils.makedirs(SYSIMG_PATH + "/usr/boot")
221 boot_server = file("/usr/bootme/BOOTSERVER").readline().strip()
222 shutil.copy("/usr/bootme/cacert/" + boot_server + "/cacert.pem",
223 SYSIMG_PATH + "/usr/boot/cacert.pem")
224 file(SYSIMG_PATH + "/usr/boot/boot_server", "w").write(boot_server)
225 shutil.copy("/usr/bootme/pubring.gpg", SYSIMG_PATH + "/usr/boot/pubring.gpg")
227 # For backward compatibility
228 if os.path.exists("/usr/bootme"):
229 # do nothing in case of upgrade
230 if not os.path.exists(SYSIMG_PATH + "/mnt/cdrom/bootme"):
231 utils.makedirs(SYSIMG_PATH + "/mnt/cdrom")
232 shutil.copytree("/usr/bootme", SYSIMG_PATH + "/mnt/cdrom/bootme")
234 # ONE_PARTITION => new distribution type
235 if (vars['ONE_PARTITION'] != '1'):
236 # Import the GPG key into the RPM database so that RPMS can be verified
237 utils.makedirs(SYSIMG_PATH + "/etc/pki/rpm-gpg")
238 utils.sysexec("gpg --homedir=/root --export --armor"
239 " --no-default-keyring --keyring {}/usr/boot/pubring.gpg"
240 " > {}/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab".format(SYSIMG_PATH, SYSIMG_PATH), log)
241 utils.sysexec_chroot(SYSIMG_PATH, "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab", log)
243 # keep a log on the installed hdd
244 stamp = file(SYSIMG_PATH + "/bm-install.txt", 'a')
245 now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
246 stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
247 stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
248 # do not modify this, the upgrade code uses this line for checking compatibility
249 stamp.write("Using nodefamily {}\n".format(nodefamily))
255 def CleanupSysimgBeforeUpgrade(sysimg, target_nodefamily, log):
266 target_pldistro, target_fcdistro, target_arch = target_nodefamily.split('-')
268 # minimal check : not all configurations are possible...
270 installed_pldistro, installed_fcdistro, installed_arch = None, None, None
271 installed_virt = None
272 prefix = "Using nodefamily "
274 with open("{}/bm-install.txt".format(sysimg)) as infile:
276 if line.startswith(prefix):
277 installed_nodefamily = line.replace(prefix,"").strip()
278 installed_pldistro, installed_fcdistro, installed_arch = installed_nodefamily.split('-')
279 # do not break here, bm-install is additive, we want the last one..
280 with open("{}/etc/planetlab/virt".format(sysimg)) as infile:
281 installed_virt = infile.read().strip()
282 except Exception as e:
284 raise BootManagerException("Could not retrieve data about previous installation - cannot upgrade")
286 # moving from vservers to lxc also means another filesystem
287 # so plain reinstall is the only option
288 if installed_virt != 'lxc':
289 raise BootManagerException("Can only upgrade nodes running lxc containers (vservers not supported)")
291 # changing arch is not reasonable either
292 if target_arch != installed_arch:
293 raise BootManagerException("Cannot upgrade from arch={} to arch={}"
294 .format(installed_arch, target_arch))
296 if target_pldistro != installed_pldistro:
297 log.write("\nWARNING: upgrading across pldistros {} to {} - might not work well..\n"
298 .format(installed_pldistro, target_pldistro))
300 # otherwise at this point we do not do any more advanced checking
301 log.write("\n\nPseudo step CleanupSysimgBeforeUpgrade : cleaning up hard drive\n")
303 for area in areas_to_cleanup:
304 utils.sysexec("rm -rf {}/{}".format(sysimg, area))