e43d8247dc2bc87247d8365e715e2f5f6def6fc0
[bootmanager.git] / source / steps / InstallBootstrapFS.py
1 #!/usr/bin/python
2 #
3 # Copyright (c) 2003 Intel Corporation
4 # All rights reserved.
5 #
6 # Copyright (c) 2004-2007 The Trustees of Princeton University
7 # All rights reserved.
8 # expected /proc/partitions format
9
10 import os, string
11 import popen2
12 import shutil
13 import traceback 
14 import time
15
16 from Exceptions import *
17 import utils
18 import BootServerRequest
19 import BootAPI
20
21
22 def Run(vars, log):
23     """
24     Download core + extensions bootstrapfs tarballs and install on the hard drive
25     
26     Expect the following variables from the store:
27     SYSIMG_PATH          the path where the system image will be mounted
28     PARTITIONS           dictionary of generic part. types (root/swap)
29                          and their associated devices.
30     NODE_ID              the id of this machine
31     
32     Sets the following variables:
33     TEMP_BOOTCD_PATH     where the boot cd is remounted in the temp
34                          path
35     ROOT_MOUNTED         set to 1 when the the base logical volumes
36                          are mounted.
37     """
38
39     log.write("\n\nStep: Install: bootstrapfs tarball.\n")
40
41     # make sure we have the variables we need
42     try:
43         SYSIMG_PATH = vars["SYSIMG_PATH"]
44         if SYSIMG_PATH == "":
45             raise ValueError("SYSIMG_PATH")
46
47         PARTITIONS = vars["PARTITIONS"]
48         if PARTITIONS == None:
49             raise ValueError("PARTITIONS")
50
51         NODE_ID = vars["NODE_ID"]
52         if NODE_ID == "":
53             raise ValueError("NODE_ID")
54
55         VERSION = vars['VERSION'] or 'unknown'
56
57     except KeyError as var:
58         raise BootManagerException("Missing variable in vars: {}\n".format(var))
59     except ValueError as var:
60         raise BootManagerException("Variable in vars, shouldn't be: {}\n".format(var))
61
62
63     try:
64         # make sure the required partitions exist
65         val = PARTITIONS["root"]
66         val = PARTITIONS["swap"]
67         val = PARTITIONS["vservers"]
68     except KeyError, part:
69         log.write("Missing partition in PARTITIONS: {}\n".format(part))
70         return 0   
71
72     bs_request = BootServerRequest.BootServerRequest(vars)
73     
74     log.write("turning on swap space\n")
75     utils.sysexec("swapon {}".format(PARTITIONS["swap"]), log)
76
77     # make sure the sysimg dir is present
78     utils.makedirs(SYSIMG_PATH)
79
80     log.write("mounting root file system\n")
81     utils.sysexec("mount -t ext3 {} {}".format(PARTITIONS["root"], SYSIMG_PATH), log)
82
83     fstype = 'ext3' if vars['virt']=='vs' else 'btrfs'
84
85     one_partition = vars['ONE_PARTITION']=='1'
86
87     if (not one_partition):
88         log.write("mounting vserver partition in root file system (type {})\n".format(fstype))
89         utils.makedirs(SYSIMG_PATH + "/vservers")
90         utils.sysexec("mount -t {} {} {}/vservers"\
91                       .format(fstype, PARTITIONS["vservers"], SYSIMG_PATH), log)
92
93         if vars['virt']=='lxc':
94             # NOTE: btrfs quota is supported from version: >= btrfs-progs-0.20 (f18+)
95             #       older versions will not recongize the 'quota' command.
96             log.write("Enabling btrfs quota on {}/vservers\n".format(SYSIMG_PATH))
97             utils.sysexec_noerr("btrfs quota enable {}/vservers".format(SYSIMG_PATH))
98
99     vars['ROOT_MOUNTED'] = 1
100
101     # this is now retrieved in GetAndUpdateNodeDetails
102     nodefamily = vars['nodefamily']
103     extensions = vars['extensions']
104     # the 'plain' option is for tests mostly
105     plain = vars['plain']
106     if plain:
107         download_suffix = ".tar"
108         uncompress_option = ""
109         log.write("Using plain bootstrapfs images\n")
110     else:
111         download_suffix = ".tar.bz2"
112         uncompress_option = "-j"
113         log.write("Using compressed bootstrapfs images\n")
114
115     log.write ("Using nodefamily={}\n".format(nodefamily))
116     if not extensions:
117         log.write("Installing only core software\n")
118     else:
119         log.write("Requested extensions {}\n".format(extensions))
120     
121     bootstrapfs_names = [ nodefamily ] + extensions
122
123     for name in bootstrapfs_names:
124         tarball = "bootstrapfs-{}{}".format(name, download_suffix)
125         source_file = "/boot/{}".format(tarball)
126         dest_file = "{}/{}".format(SYSIMG_PATH, tarball)
127
128         source_hash_file = "/boot/{}.sha1sum".format(tarball)
129         dest_hash_file = "{}/{}.sha1sum".format(SYSIMG_PATH, tarball)
130
131         time_beg = time.time()
132         log.write("downloading {}\n".format(source_file))
133         # 30 is the connect timeout, 14400 is the max transfer time in
134         # seconds (4 hours)
135         result = bs_request.DownloadFile(source_file, None, None,
136                                          1, 1, dest_file,
137                                          30, 14400)
138         time_end = time.time()
139         duration = int(time_end - time_beg)
140         log.write("Done downloading ({} seconds)\n".format(duration))
141         if result:
142             # Download SHA1 checksum file
143             log.write("downloading sha1sum for {}\n".format(source_file))
144             result = bs_request.DownloadFile(source_hash_file, None, None,
145                                          1, 1, dest_hash_file,
146                                          30, 14400)
147  
148             log.write("verifying sha1sum for {}\n".format(source_file))
149             if not utils.check_file_hash(dest_file, dest_hash_file):
150                 raise BootManagerException(
151                     "FATAL: SHA1 checksum does not match between {} and {}"\
152                     .format(source_file, source_hash_file))
153                 
154             
155             time_beg = time.time()
156             log.write("extracting {} in {}\n".format(dest_file, SYSIMG_PATH))
157             result = utils.sysexec("tar -C {} -xpf {} {}".format(SYSIMG_PATH, dest_file, uncompress_option), log)
158             time_end = time.time()
159             duration = int(time_end - time_beg)
160             log.write("Done extracting ({} seconds)\n".format(duration))
161             utils.removefile(dest_file)
162         else:
163             # the main tarball is required
164             if name == nodefamily:
165                 raise BootManagerException(
166                     "FATAL: Unable to download main tarball {} from server."\
167                     .format(source_file))
168             # for extensions, just print a warning
169             else:
170                 log.write("WARNING: tarball for extension {} not found\n".format(name))
171
172     # copy resolv.conf from the base system into our temp dir
173     # so DNS lookups work correctly while we are chrooted
174     log.write("Copying resolv.conf to temp dir\n")
175     utils.sysexec("cp /etc/resolv.conf {}/etc/".format(SYSIMG_PATH), log)
176
177     # Copy the boot server certificate(s) and GPG public key to
178     # /usr/boot in the temp dir.
179     log.write("Copying boot server certificates and public key\n")
180
181     if os.path.exists("/usr/boot"):
182         # do nothing in case of upgrade
183         if not os.path.exists(SYSIMG_PATH + "/usr/boot"):
184             utils.makedirs(SYSIMG_PATH + "/usr")
185             shutil.copytree("/usr/boot", SYSIMG_PATH + "/usr/boot")
186     elif os.path.exists("/usr/bootme"):
187         # do nothing in case of upgrade
188         if not os.path.exists(SYSIMG_PATH + "/usr/bootme"):
189             utils.makedirs(SYSIMG_PATH + "/usr/boot")
190             boot_server = file("/usr/bootme/BOOTSERVER").readline().strip()
191             shutil.copy("/usr/bootme/cacert/" + boot_server + "/cacert.pem",
192                         SYSIMG_PATH + "/usr/boot/cacert.pem")
193             file(SYSIMG_PATH + "/usr/boot/boot_server", "w").write(boot_server)
194             shutil.copy("/usr/bootme/pubring.gpg", SYSIMG_PATH + "/usr/boot/pubring.gpg")
195         
196     # For backward compatibility
197     if os.path.exists("/usr/bootme"):
198         # do nothing in case of upgrade
199         if not os.path.exists(SYSIMG_PATH + "/mnt/cdrom/bootme"):
200             utils.makedirs(SYSIMG_PATH + "/mnt/cdrom")
201             shutil.copytree("/usr/bootme", SYSIMG_PATH + "/mnt/cdrom/bootme")
202
203     # ONE_PARTITION => new distribution type
204     if (vars['ONE_PARTITION'] != '1'):
205         # Import the GPG key into the RPM database so that RPMS can be verified
206         utils.makedirs(SYSIMG_PATH + "/etc/pki/rpm-gpg")
207         utils.sysexec("gpg --homedir=/root --export --armor"
208                       " --no-default-keyring --keyring {}/usr/boot/pubring.gpg"
209                       " > {}/etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab".format(SYSIMG_PATH, SYSIMG_PATH), log)
210         utils.sysexec_chroot(SYSIMG_PATH, "rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-planetlab", log)
211
212     # keep a log on the installed hdd
213     stamp = file(SYSIMG_PATH + "/bm-install.txt", 'a')
214     now = time.strftime("%Y-%b-%d @ %H:%M %Z", time.gmtime())
215     stamp.write("Hard drive installed by BootManager {}\n".format(VERSION))
216     stamp.write("Finished extraction of bootstrapfs on {}\n".format(now))
217     stamp.write("Using nodefamily {}\n".format(nodefamily))
218     stamp.close()
219
220     return 1