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