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 *
18 import BootServerRequest
22 def Run(vars, upgrade, log):
24 Download core + extensions bootstrapfs tarballs and install on the hard drive
26 the upgrade boolean is True when we are upgrading a node root install while
27 preserving its slice contents; in that case we just perform extra cleanup
28 before unwrapping the bootstrapfs
29 this is because the running system may have extraneous files
30 that is to say, files that are *not* present in the bootstrapfs
31 and that can impact/clobber the resulting upgrade
33 Expect the following variables from the store:
34 SYSIMG_PATH the path where the system image will be mounted
35 PARTITIONS dictionary of generic part. types (root/swap)
36 and their associated devices.
37 NODE_ID the id of this machine
39 Sets the following variables:
40 TEMP_BOOTCD_PATH where the boot cd is remounted in the temp
42 ROOT_MOUNTED set to 1 when the the base logical volumes
46 log.write("\n\nStep: Install: bootstrapfs tarball (upgrade={}).\n".format(upgrade))
48 # make sure we have the variables we need
50 SYSIMG_PATH = vars["SYSIMG_PATH"]
52 raise ValueError("SYSIMG_PATH")
54 PARTITIONS = vars["PARTITIONS"]
55 if PARTITIONS == None:
56 raise ValueError("PARTITIONS")
58 NODE_ID = vars["NODE_ID"]
60 raise ValueError("NODE_ID")
62 VERSION = vars['VERSION'] or 'unknown'
64 except KeyError as var:
65 raise BootManagerException("Missing variable in vars: {}\n".format(var))
66 except ValueError as var:
67 raise BootManagerException("Variable in vars, shouldn't be: {}\n".format(var))
71 # make sure the required partitions exist
72 val = PARTITIONS["root"]
73 val = PARTITIONS["swap"]
74 val = PARTITIONS["vservers"]
75 except KeyError as part:
76 log.write("Missing partition in PARTITIONS: {}\n".format(part))
79 bs_request = BootServerRequest.BootServerRequest(vars)
81 # in upgrade mode, since we skip InstallPartitionDisks
84 log.write("Running vgscan for devices (upgrade mode)\n")
85 systeminfo.get_block_devices_dict(vars, log)
86 utils.sysexec_noerr("vgscan", log)
88 log.write("turning on swap space\n")
89 utils.sysexec("swapon {}".format(PARTITIONS["swap"]), log)
91 # make sure the sysimg dir is present
92 utils.makedirs(SYSIMG_PATH)
94 log.write("mounting root file system\n")
95 utils.sysexec("mount -t ext3 {} {}".format(PARTITIONS["root"], SYSIMG_PATH), log)
97 fstype = 'ext3' if vars['virt']=='vs' else 'btrfs'
99 one_partition = vars['ONE_PARTITION']=='1'
101 if (not one_partition):
102 log.write("mounting vserver partition in root file system (type {})\n".format(fstype))
103 utils.makedirs(SYSIMG_PATH + "/vservers")
104 utils.sysexec("mount -t {} {} {}/vservers"\
105 .format(fstype, PARTITIONS["vservers"], SYSIMG_PATH), log)
107 if vars['virt']=='lxc':
108 # NOTE: btrfs quota is supported from version: >= btrfs-progs-0.20 (f18+)
109 # older versions will not recongize the 'quota' command.
110 log.write("Enabling btrfs quota on {}/vservers\n".format(SYSIMG_PATH))
111 utils.sysexec_noerr("btrfs quota enable {}/vservers".format(SYSIMG_PATH))
113 vars['ROOT_MOUNTED'] = 1
115 # this is now retrieved in GetAndUpdateNodeDetails
116 nodefamily = vars['nodefamily']
117 extensions = vars['extensions']
119 # in upgrade mode: we need to cleanup the disk to make
120 # it safe to just untar the new bootstrapfs tarball again
121 # on top of the hard drive
123 CleanupSysimgBeforeUpgrade(SYSIMG_PATH, nodefamily, log)
125 # the 'plain' option is for tests mostly
126 plain = vars['plain']
128 download_suffix = ".tar"
129 uncompress_option = ""
130 log.write("Using plain bootstrapfs images\n")
132 download_suffix = ".tar.bz2"
133 uncompress_option = "-j"
134 log.write("Using compressed bootstrapfs images\n")
136 log.write ("Using nodefamily={}\n".format(nodefamily))
138 log.write("Installing only core software\n")
140 log.write("Requested extensions {}\n".format(extensions))
142 bootstrapfs_names = [ nodefamily ] + extensions
144 for name in bootstrapfs_names:
145 tarball = "bootstrapfs-{}{}".format(name, download_suffix)
146 source_file = "/boot/{}".format(tarball)
147 dest_file = "{}/{}".format(SYSIMG_PATH, tarball)
149 source_hash_file = "/boot/{}.sha1sum".format(tarball)
150 dest_hash_file = "{}/{}.sha1sum".format(SYSIMG_PATH, tarball)
152 time_beg = time.time()
153 log.write("downloading {}\n".format(source_file))
154 # 30 is the connect timeout, 14400 is the max transfer time in
156 result = bs_request.DownloadFile(source_file, None, None,
159 time_end = time.time()
160 duration = int(time_end - time_beg)
161 log.write("Done downloading ({} seconds)\n".format(duration))
163 # Download SHA1 checksum file
164 log.write("downloading sha1sum for {}\n".format(source_file))
165 result = bs_request.DownloadFile(source_hash_file, None, None,
166 1, 1, dest_hash_file,
169 log.write("verifying sha1sum for {}\n".format(source_file))
170 if not utils.check_file_hash(dest_file, dest_hash_file):
171 raise BootManagerException(
172 "FATAL: SHA1 checksum does not match between {} and {}"\
173 .format(source_file, source_hash_file))
176 time_beg = time.time()
177 log.write("extracting {} in {}\n".format(dest_file, SYSIMG_PATH))
178 result = utils.sysexec("tar -C {} -xpf {} {}".format(SYSIMG_PATH, dest_file, uncompress_option), log)
179 time_end = time.time()
180 duration = int(time_end - time_beg)
181 log.write("Done extracting ({} seconds)\n".format(duration))
182 utils.removefile(dest_file)
184 # the main tarball is required
185 if name == nodefamily:
186 raise BootManagerException(
187 "FATAL: Unable to download main tarball {} from server."\
188 .format(source_file))
189 # for extensions, just issue a warning
191 log.write("WARNING: tarball for extension {} not found\n".format(name))
193 # copy resolv.conf from the base system into our temp dir
194 # so DNS lookups work correctly while we are chrooted
195 log.write("Copying resolv.conf to temp dir\n")
196 utils.sysexec("cp /etc/resolv.conf {}/etc/".format(SYSIMG_PATH), log)
198 # Copy the boot server certificate(s) and GPG public key to
199 # /usr/boot in the temp dir.
200 log.write("Copying boot server certificates and public key\n")
202 if os.path.exists("/usr/boot"):
203 # do nothing in case of upgrade
204 if not os.path.exists(SYSIMG_PATH + "/usr/boot"):
205 utils.makedirs(SYSIMG_PATH + "/usr")
206 shutil.copytree("/usr/boot", SYSIMG_PATH + "/usr/boot")
207 elif os.path.exists("/usr/bootme"):
208 # do nothing in case of upgrade
209 if not os.path.exists(SYSIMG_PATH + "/usr/bootme"):
210 utils.makedirs(SYSIMG_PATH + "/usr/boot")
211 boot_server = file("/usr/bootme/BOOTSERVER").readline().strip()
212 shutil.copy("/usr/bootme/cacert/" + boot_server + "/cacert.pem",
213 SYSIMG_PATH + "/usr/boot/cacert.pem")
214 file(SYSIMG_PATH + "/usr/boot/boot_server", "w").write(boot_server)
215 shutil.copy("/usr/bootme/pubring.gpg", SYSIMG_PATH + "/usr/boot/pubring.gpg")
217 # For backward compatibility
218 if os.path.exists("/usr/bootme"):
219 # do nothing in case of upgrade
220 if not os.path.exists(SYSIMG_PATH + "/mnt/cdrom/bootme"):
221 utils.makedirs(SYSIMG_PATH + "/mnt/cdrom")
222 shutil.copytree("/usr/bootme", SYSIMG_PATH + "/mnt/cdrom/bootme")
224 # ONE_PARTITION => new distribution type
225 if (vars['ONE_PARTITION'] != '1'):
226 # Import the GPG key into the RPM database so that RPMS can be verified
227 utils.makedirs(SYSIMG_PATH + "/etc/pki/rpm-gpg")
228 utils.sysexec("gpg --homedir=/root --export --armor"
229 " --no-default-keyring --keyring {}/usr/boot/pubring.gpg"
230 " > {}/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab".format(SYSIMG_PATH, SYSIMG_PATH), log)
231 utils.sysexec_chroot(SYSIMG_PATH, "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab", log)
233 # keep a log on the installed hdd
234 stamp = file(SYSIMG_PATH + "/bm-install.txt", 'a')
235 now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
236 stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
237 stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
238 # do not modify this, the upgrade code uses this line for checking compatibility
239 stamp.write("Using nodefamily {}\n".format(nodefamily))
245 def CleanupSysimgBeforeUpgrade(sysimg, target_nodefamily, log):
254 target_pldistro, target_fcdistro, target_arch = target_nodefamily.split('-')
256 # minimal check : not all configurations are possible...
258 installed_pldistro, installed_fcdistro, installed_arch = None, None, None
259 installed_virt = None
260 prefix = "Using nodefamily "
262 with open("{}/bm-install.txt".format(sysimg)) as infile:
264 if line.startswith(prefix):
265 installed_nodefamily = line.replace(prefix,"").strip()
266 installed_pldistro, installed_fcdistro, installed_arch = installed_nodefamily.split('-')
267 # do not break here, bm-install is additive, we want the last one..
268 with open("{}/etc/planetlab/virt".format(sysimg)) as infile:
269 installed_virt = infile.read().strip()
270 except Exception as e:
272 raise BootManagerException("Could not retrieve data about previous installation - cannot upgrade")
274 # moving from vservers to lxc also means another filesystem
275 # so plain reinstall is the only option
276 if installed_virt != 'lxc':
277 raise BootManagerException("Can only upgrade nodes running lxc containers (vservers not supported)")
279 # changing arch is not reasonable either
280 if target_arch != installed_arch:
281 raise BootManagerException("Cannot upgrade from arch={} to arch={}"
282 .format(installed_arch, target_arch))
284 if target_pldistro != installed_pldistro:
285 log.write("\nWARNING: upgrading across pldistros {} to {} - might not work well..\n"
286 .format(installed_pldistro, target_pldistro))
288 # otherwise at this point we do not do any more advanced checking
289 log.write("\n\nPseudo step CleanupSysimgBeforeUpgrade : cleaning up hard drive\n")
291 for area in areas_to_cleanup:
292 utils.sysexec("rm -rf {}/{}".format(sysimg, area))