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