01c0683deb54342c6d2684d6a11f36ee04f520aa
[bootmanager.git] / source / steps / InstallPartitionDisks.py
1 #!/usr/bin/python2
2
3 # Copyright (c) 2003 Intel Corporation
4 # All rights reserved.
5 #
6 # Copyright (c) 2004-2006 The Trustees of Princeton University
7 # All rights reserved.
8 # expected /proc/partitions format
9
10 import os, sys
11 import string
12 import popen2
13
14
15 from Exceptions import *
16 import utils
17 import BootServerRequest
18 import compatibility
19
20 import ModelOptions
21
22 def Run( vars, log ):
23     """
24     Setup the block devices for install, partition them w/ LVM
25     
26     Expect the following variables from the store:
27     INSTALL_BLOCK_DEVICES    list of block devices to install onto
28     TEMP_PATH                somewhere to store what we need to run
29     ROOT_SIZE                the size of the root logical volume
30     SWAP_SIZE                the size of the swap partition
31     BOOT_CD_VERSION          A tuple of the current bootcd version
32     """
33
34     log.write( "\n\nStep: Install: partitioning disks.\n" )
35         
36     # make sure we have the variables we need
37     try:
38         TEMP_PATH= vars["TEMP_PATH"]
39         if TEMP_PATH == "":
40             raise ValueError, "TEMP_PATH"
41
42         INSTALL_BLOCK_DEVICES= vars["INSTALL_BLOCK_DEVICES"]
43         if( len(INSTALL_BLOCK_DEVICES) == 0 ):
44             raise ValueError, "INSTALL_BLOCK_DEVICES is empty"
45
46         ROOT_SIZE= vars["ROOT_SIZE"]
47         if ROOT_SIZE == "" or ROOT_SIZE == 0:
48             raise ValueError, "ROOT_SIZE invalid"
49
50         SWAP_SIZE= vars["SWAP_SIZE"]
51         if SWAP_SIZE == "" or SWAP_SIZE == 0:
52             raise ValueError, "SWAP_SIZE invalid"
53
54         BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
55         if BOOT_CD_VERSION == "":
56             raise ValueError, "BOOT_CD_VERSION"
57
58         NODE_MODEL_OPTIONS= vars["NODE_MODEL_OPTIONS"]
59
60         PARTITIONS= vars["PARTITIONS"]
61         if PARTITIONS == None:
62             raise ValueError, "PARTITIONS"
63
64         if NODE_MODEL_OPTIONS & ModelOptions.RAWDISK:
65             VSERVERS_SIZE= "-1"
66             if "VSERVER_SIZE" in vars:
67                 VSERVERS_SIZE= vars["VSERVERS_SIZE"]
68                 if VSERVERS_SIZE == "" or VSERVERS_SIZE == 0:
69                     raise ValueError, "VSERVERS_SIZE"
70
71     except KeyError, var:
72         raise BootManagerException, "Missing variable in vars: %s\n" % var
73     except ValueError, var:
74         raise BootManagerException, "Variable in vars, shouldn't be: %s\n" % var
75
76     bs_request= BootServerRequest.BootServerRequest()
77
78     
79     # old cds need extra utilities to partition disks and setup lvm
80     if BOOT_CD_VERSION[0] == 2:
81         compatibility.setup_partdisks_2x_cd( vars, log )
82
83     # disable swap if its on
84     utils.sysexec_noerr( "swapoff %s" % PARTITIONS["swap"], log )
85
86     # shutdown and remove any lvm groups/volumes
87     utils.sysexec_noerr( "vgscan", log )
88     utils.sysexec_noerr( "vgchange -ay", log )        
89     utils.sysexec_noerr( "lvremove -f %s" % PARTITIONS["root"], log )
90     utils.sysexec_noerr( "lvremove -f %s" % PARTITIONS["swap"], log )
91     utils.sysexec_noerr( "lvremove -f %s" % PARTITIONS["vservers"], log )
92     utils.sysexec_noerr( "vgchange -an", log )
93     utils.sysexec_noerr( "vgremove planetlab", log )
94
95     log.write( "Running vgscan for devices\n" )
96     utils.sysexec_noerr( "vgscan", log )
97     
98     used_devices= []
99
100     for device in sorted(INSTALL_BLOCK_DEVICES):
101
102         if single_partition_device( device, vars, log ):
103             if (len(used_devices) > 0 and
104                 (vars['NODE_MODEL_OPTIONS'] & ModelOptions.RAWDISK)):
105                 log.write( "Running in raw disk mode, not using %s.\n" % device )
106             else:
107                 used_devices.append( device )
108                 log.write( "Successfully initialized %s\n" % device )
109         else:
110             log.write( "Unable to partition %s, not using it.\n" % device )
111             continue
112
113     # list of devices to be used with vgcreate
114     vg_device_list= ""
115
116     # initialize the physical volumes
117     for device in used_devices:
118
119         part_path= get_partition_path_from_device( device, vars, log )
120         
121         if not create_lvm_physical_volume( part_path, vars, log ):
122             raise BootManagerException, "Could not create lvm physical volume " \
123                   "on partition %s" % part_path
124         
125         vg_device_list = vg_device_list + " " + part_path
126
127     # create an lvm volume group
128     utils.sysexec( "vgcreate -s32M planetlab %s" % vg_device_list, log)
129
130     # create swap logical volume
131     utils.sysexec( "lvcreate -L%s -nswap planetlab" % SWAP_SIZE, log )
132
133     # create root logical volume
134     utils.sysexec( "lvcreate -L%s -nroot planetlab" % ROOT_SIZE, log )
135
136     if vars['NODE_MODEL_OPTIONS'] & ModelOptions.RAWDISK and VSERVERS_SIZE != "-1":
137         utils.sysexec( "lvcreate -L%s -nvservers planetlab" % VSERVERS_SIZE, log )
138         remaining_extents= get_remaining_extents_on_vg( vars, log )
139         utils.sysexec( "lvcreate -l%s -nrawdisk planetlab" % remaining_extents, log )
140     else:
141         # create vservers logical volume with all remaining space
142         # first, we need to get the number of remaining extents we can use
143         remaining_extents= get_remaining_extents_on_vg( vars, log )
144         
145         utils.sysexec( "lvcreate -l%s -nvservers planetlab" % remaining_extents, log )
146
147     # activate volume group (should already be active)
148     #utils.sysexec( TEMP_PATH + "vgchange -ay planetlab", log )
149
150     # make swap
151     utils.sysexec( "mkswap %s" % PARTITIONS["swap"], log )
152
153     # check if badhd option has been set
154     option = ''
155     txt = ''
156     if NODE_MODEL_OPTIONS & ModelOptions.BADHD:
157         option = '-c'
158         txt = " with bad block search enabled, which may take a while"
159     
160     # filesystems partitions names and their corresponding
161     # reserved-blocks-percentages
162     filesystems = {"root":5,"vservers":0}
163
164     # make the file systems
165     for fs in filesystems.keys():
166         # get the reserved blocks percentage
167         rbp = filesystems[fs]
168         devname = PARTITIONS[fs]
169         log.write("formatting %s partition (%s)%s.\n" % (fs,devname,txt))
170         utils.sysexec( "mkfs.ext2 -q %s -m %d -j %s" % (option,rbp,devname), log )
171
172     # save the list of block devices in the log
173     log.write( "Block devices used (in lvm): %s\n" % repr(used_devices))
174
175     # list of block devices used may be updated
176     vars["INSTALL_BLOCK_DEVICES"]= used_devices
177
178     return 1
179
180
181
182 def single_partition_device( device, vars, log ):
183     """
184     initialize a disk by removing the old partition tables,
185     and creating a new single partition that fills the disk.
186
187     return 1 if sucessful, 0 otherwise
188     """
189
190     BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
191     if BOOT_CD_VERSION[0] == 2:
192         compatibility.setup_partdisks_2x_cd( vars, log )
193
194     import parted
195     
196     lvm_flag= parted.partition_flag_get_by_name('lvm')
197     
198     try:
199         # wipe the old partition table
200         utils.sysexec( "dd if=/dev/zero of=%s bs=512 count=1" % device, log )
201
202         # get the device
203         dev= parted.PedDevice.get(device)
204
205         # 2.x cds have different libparted that 3.x cds, and they have
206         # different interfaces
207         if BOOT_CD_VERSION[0] >= 3:
208
209             # create a new partition table
210             disk= dev.disk_new_fresh(parted.disk_type_get("msdos"))
211
212             # create one big partition on each block device
213             constraint= dev.constraint_any()
214
215             new_part= disk.partition_new(
216                 parted.PARTITION_PRIMARY,
217                 parted.file_system_type_get("ext2"),
218                 0, 1 )
219
220             # make it an lvm partition
221             new_part.set_flag(lvm_flag,1)
222
223             # actually add the partition to the disk
224             disk.add_partition(new_part, constraint)
225
226             disk.maximize_partition(new_part,constraint)
227
228             disk.commit()
229             del disk
230         else:
231             # create a new partition table
232             dev.disk_create(parted.disk_type_get("msdos"))
233
234             # get the disk
235             disk= parted.PedDisk.open(dev)
236
237                 # create one big partition on each block device
238             part= disk.next_partition()
239             while part:
240                 if part.type == parted.PARTITION_FREESPACE:
241                     new_part= disk.partition_new(
242                         parted.PARTITION_PRIMARY,
243                         parted.file_system_type_get("ext2"),
244                         part.geom.start,
245                         part.geom.end )
246
247                     constraint = disk.constraint_any()
248
249                     # make it an lvm partition
250                     new_part.set_flag(lvm_flag,1)
251
252                     # actually add the partition to the disk
253                     disk.add_partition(new_part, constraint)
254
255                     break
256
257                 part= disk.next_partition(part)
258
259             disk.write()
260             disk.close()
261             del disk
262             
263     except BootManagerException, e:
264         log.write( "BootManagerException while running: %s\n" % str(e) )
265         return 0
266
267     except parted.error, e:
268         log.write( "parted exception while running: %s\n" % str(e) )
269         return 0
270                    
271     return 1
272
273
274
275 def create_lvm_physical_volume( part_path, vars, log ):
276     """
277     make the specificed partition a lvm physical volume.
278
279     return 1 if successful, 0 otherwise.
280     """
281
282     try:
283         # again, wipe any old data, this time on the partition
284         utils.sysexec( "dd if=/dev/zero of=%s bs=512 count=1" % part_path, log )
285         ### patch Thierry Parmentelat, required on some hardware
286         import time
287         time.sleep(1)
288         utils.sysexec( "pvcreate -ffy %s" % part_path, log )
289     except BootManagerException, e:
290         log.write( "create_lvm_physical_volume failed.\n" )
291         return 0
292
293     return 1
294
295
296
297 def get_partition_path_from_device( device, vars, log ):
298     """
299     given a device, return the path of the first partition on the device
300     """
301
302     BOOT_CD_VERSION= vars["BOOT_CD_VERSION"]
303         
304     # those who wrote the cciss driver just had to make it difficult
305     if BOOT_CD_VERSION[0] >= 3:
306         cciss_test= "/dev/cciss"
307         if device[:len(cciss_test)] == cciss_test:
308             part_path= device + "p1"
309         else:
310             part_path= device + "1"
311     else:
312         # if device ends in /disc, we need to make it end in
313         # /part1 to indicate the first partition (for devfs based 2.x cds)
314         dev_parts= string.split(device,"/")
315         if dev_parts[-1] == "disc":
316             dev_parts[-1]= "part1"
317             part_path= string.join(dev_parts,"/")
318         else:
319             part_path= device + "1"
320
321     return part_path
322
323
324
325 def get_remaining_extents_on_vg( vars, log ):
326     """
327     return the free amount of extents on the planetlab volume group
328     """
329     
330     c_stdout, c_stdin = popen2.popen2("vgdisplay -c planetlab")
331     result= string.strip(c_stdout.readline())
332     c_stdout.close()
333     c_stdin.close()
334     remaining_extents= string.split(result,":")[15]
335     
336     return remaining_extents