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