/[gli]/trunk/src/GLIStorageDevice.py
Gentoo

Diff of /trunk/src/GLIStorageDevice.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 596 Revision 1291
1# Copyright 1999-2005 Gentoo Foundation
2# This source code is distributed under the terms of version 2 of the GNU
3# General Public License as published by the Free Software Foundation, a copy
4# of which can be found in the main directory of this project.
5
1import commands, string, os, parted 6import commands, string, os, parted
2from GLIException import * 7from GLIException import *
3import GLIUtility 8import GLIUtility
4 9
5MEGABYTE = 1024 * 1024 10MEGABYTE = 1024 * 1024
6 11
12# these are here so that we can change them easily in future
13# the values were chosen to represent perfect floating point representations
14FREE_MINOR_FRAC_PRI = 1.0/32.0
15FREE_MINOR_FRAC_LOG = 1.0/8.0
16
7archinfo = { 'sparc': { 'fixedparts': [ { 'minor': 3, 'type': "wholedisk" } ], 'disklabel': 'sun', 'extended': False }, 17archinfo = { 'sparc': { 'fixedparts': [ { 'minor': 3, 'type': "wholedisk" } ], 'disklabel': 'sun', 'extended': False },
8 'hppa': { 'fixedparts': [ { 'minor': 1, 'type': "boot" } ], 'disklabel': 'msdos', 'extended': False }, 18 'hppa': { 'fixedparts': [ { 'minor': 1, 'type': "palo" } ], 'disklabel': 'msdos', 'extended': True },
9 'x86': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True }, 19 'x86': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True },
10 'amd64': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True }, 20 'amd64': { 'fixedparts': [], 'disklabel': 'msdos', 'extended': True },
11 'ppc': { 'fixedparts': [ { 'minor': 1, 'type': "metadata" } ], 'disklabel': 'mac', 'extended': False } 21 'ppc': { 'fixedparts': [ { 'minor': 1, 'type': "metadata" } ], 'disklabel': 'mac', 'extended': False }
12 } 22 }
13 23
32 42
33 ## 43 ##
34 # Initialization function for GLIStorageDevice class 44 # Initialization function for GLIStorageDevice class
35 # @param device Device node (e.g. /dev/hda) of device being represented 45 # @param device Device node (e.g. /dev/hda) of device being represented
36 # @param arch="x86" Architecture that we're partition for (defaults to 'x86' for now) 46 # @param arch="x86" Architecture that we're partition for (defaults to 'x86' for now)
37 def __init__(self, device, arch="x86", set_geometry=True): 47 def __init__(self, device, arch="x86", set_geometry=True, local_device=True):
38 self._device = device 48 self._device = device
39 self._partitions = {} 49 self._partitions = {}
40 self._geometry = {'cylinders': 0, 'heads': 0, 'sectors': 0, 'sectorsize': 512} 50 self._geometry = {'cylinders': 0, 'heads': 0, 'sectors': 0, 'sectorsize': 512}
41 self._total_bytes = 0 51 self._total_bytes = 0
42 self._cylinder_bytes = 0 52 self._cylinder_bytes = 0
43 self._arch = arch 53 self._arch = arch
54 self._local_device = local_device
55 if self._local_device:
44 self._parted_dev = parted.PedDevice.get(self._device) 56 self._parted_dev = parted.PedDevice.get(self._device)
45 try: 57 try:
46 self._parted_disk = parted.PedDisk.new(self._parted_dev) 58 self._parted_disk = parted.PedDisk.new(self._parted_dev)
47 except: 59 except:
48 self._parted_disk = self._parted_dev.disk_new_fresh(parted.disk_type_get(archinfo[self._arch]['disklabel'])) 60 self._parted_disk = self._parted_dev.disk_new_fresh(parted.disk_type_get(archinfo[self._arch]['disklabel']))
49 self._disklabel = self._parted_disk.type.name 61 self._disklabel = self._parted_disk.type.name
62 else:
63 self._disklabel = archinfo[self._arch]['disklabel']
50 if set_geometry: 64 if set_geometry:
51 self.set_disk_geometry_from_disk() 65 self.set_disk_geometry_from_disk()
52 66
53 ## 67 ##
54 # Sets disk geometry info from disk. This function is used internally by __init__() 68 # Sets disk geometry info from disk. This function is used internally by __init__()
57 self._geometry['heads'], self._geometry['sectors'], self._geometry['cylinders'] = self._parted_dev.heads, self._parted_dev.sectors, self._parted_dev.cylinders 71 self._geometry['heads'], self._geometry['sectors'], self._geometry['cylinders'] = self._parted_dev.heads, self._parted_dev.sectors, self._parted_dev.cylinders
58 self._sector_bytes = self._parted_dev.sector_size 72 self._sector_bytes = self._parted_dev.sector_size
59 self._cylinder_bytes = self._geometry['heads'] * self._geometry['sectors'] * self._sector_bytes 73 self._cylinder_bytes = self._geometry['heads'] * self._geometry['sectors'] * self._sector_bytes
60 self._total_sectors = self._parted_dev.length 74 self._total_sectors = self._parted_dev.length
61 self._sectors_in_cylinder = self._geometry['heads'] * self._geometry['sectors'] 75 self._sectors_in_cylinder = self._geometry['heads'] * self._geometry['sectors']
62 self._total_mb = int(self._total_bytes / MEGABYTE) 76 self._total_mb = long(self._total_bytes / MEGABYTE)
63 77
64 ## 78 ##
65 # Sets partition info from disk. 79 # Sets partition info from disk.
66 def set_partitions_from_disk(self): 80 def set_partitions_from_disk(self):
67 last_part = 0 81 last_part = 0
68 last_log_part = 4 82 last_log_part = 4
69 parted_part = self._parted_disk.next_partition() 83 parted_part = self._parted_disk.next_partition()
70 while parted_part: 84 while parted_part:
71 part_mb = int((parted_part.geom.end - parted_part.geom.start + 1) * self._sector_bytes / MEGABYTE) 85 part_mb = long((parted_part.geom.end - parted_part.geom.start + 1) * self._sector_bytes / MEGABYTE)
86 # Ignore metadata "partitions"...this will need to be changed for non-x86
72 if parted_part.num >= 1: 87 if parted_part.num >= 1:
73 fs_type = "" 88 fs_type = ""
74 if parted_part.fs_type != None: fs_type = parted_part.fs_type.name 89 if parted_part.fs_type != None: fs_type = parted_part.fs_type.name
75 if parted_part.type == 2: fs_type = "extended" 90 if parted_part.type == 2: fs_type = "extended"
76 if archinfo[self._arch]['extended'] and parted_part.num > 4: 91 if archinfo[self._arch]['extended'] and parted_part.num > 4:
77 last_log_part = parted_part.num 92 last_log_part = parted_part.num
78 else: 93 else:
79 last_part = parted_part.num 94 last_part = parted_part.num
80 self._partitions[int(parted_part.num)] = Partition(self, parted_part.num, part_mb, parted_part.geom.start, parted_part.geom.end, fs_type, format=False, existing=True) 95 self._partitions[int(parted_part.num)] = Partition(self, parted_part.num, part_mb, parted_part.geom.start, parted_part.geom.end, fs_type, format=False, existing=True)
81 elif parted_part.type_name == "free": 96 elif parted_part.type_name == "free":
82 parent_part = self.get_partition_at(parted_part.geom.start, ignore_extended=0) 97 if self.get_extended_partition() and parted_part.geom.start >= self._partitions[self.get_extended_partition()].get_start() and parted_part.geom.end <= self._partitions[self.get_extended_partition()].get_end():
83 if parent_part:
84 self._partitions[last_log_part+0.9] = Partition(self, last_log_part+0.9, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False) 98 self._partitions[last_log_part+FREE_MINOR_FRAC_LOG] = Partition(self, last_log_part+FREE_MINOR_FRAC_LOG, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
85 last_log_part += 1 99 last_log_part += 1
86 else: 100 else:
87 self._partitions[last_part+0.1] = Partition(self, last_part+0.1, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False) 101 self._partitions[last_part+FREE_MINOR_FRAC_PRI] = Partition(self, last_part+FREE_MINOR_FRAC_PRI, part_mb, parted_part.geom.start, parted_part.geom.end, "free", format=False, existing=False)
88 last_part += 1 102 last_part += 1
89 parted_part = self._parted_disk.next_partition(parted_part) 103 parted_part = self._parted_disk.next_partition(parted_part)
90 104
91 ## 105 ##
92 # Imports partition info from the install profile partition structure (currently does nothing) 106 # Imports partition info from the install profile partition structure
93 # @param ips Parameter structure returned from install_profile.get_partition_tables() 107 # @param ips Parameter structure returned from install_profile.get_partition_tables()
94 def set_partitions_from_install_profile_structure(self, ips): 108 def set_partitions_from_install_profile_structure(self, ips):
95 for part in ips: 109 for part in ips:
96 tmppart = ips[part] 110 tmppart = ips[part]
97 existing = False 111 existing = False
98 if tmppart['origminor'] and not tmppart['format']: 112 if tmppart['origminor'] and not tmppart['format']:
99 existing = True 113 existing = True
100 self._partitions[tmppart['minor']] = Partition(self, tmppart['minor'], tmppart['mb'], tmppart['start'], tmppart['end'], tmppart['type'], format=tmppart['format'], origminor=tmppart['origminor'], existing=existing, mountpoint=tmppart['mountpoint'], mountopts=['mountopts']) 114 self._partitions[tmppart['minor']] = Partition(self, tmppart['minor'], tmppart['mb'], tmppart['start'], tmppart['end'], tmppart['type'], format=tmppart['format'], origminor=tmppart['origminor'], existing=existing, mountpoint=tmppart['mountpoint'], mountopts=tmppart['mountopts'], mkfsopts=tmppart['mkfsopts'], resized=(existing and tmppart['resized']))
101 115
102 ## 116 ##
103 # Returns name of device (e.g. /dev/hda) being represented 117 # Returns name of device (e.g. /dev/hda) being represented
104 def get_device(self): 118 def get_device(self):
105 return self._device 119 return self._device
120
121 ##
122 # Returns whether the device is local or not
123 def is_local(self):
124 return self._local_device
125
126 ##
127 # Uses magic to apply the recommended partition layout
128 def do_recommended(self):
129 free_minor = 0
130 recommended_parts = [ { 'type': "ext2", 'size': 100, 'mountpoint': "/boot" },
131 { 'type': "linux-swap", 'size': 512, 'mountpoint': "" },
132 { 'type': "ext3", 'size': "*", 'mountpoint': "/" } ]
133 to_create = []
134 physical_memory = int(GLIUtility.spawn(r"free -m | egrep '^Mem:' | sed -e 's/^Mem: \+//' -e 's/ \+.\+$//'", return_output=True)[1].strip())
135 parts = self.get_ordered_partition_list()
136 # Search for concurrent unallocated space >=4GB
137 for part in parts:
138 if self._partitions[part].get_type() == "free" and self._partitions[part].get_mb() >= 4096:
139 free_minor = part
140 break
141 if not free_minor:
142 raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "You do not have atleast 4GB of concurrent unallocated space. Please remove some partitions and try again.")
143 remaining_free = self._partitions[free_minor].get_mb()
144 for newpart in recommended_parts:
145 # extended/logical partitions suck like a hoover
146 if archinfo[self._arch]['extended'] and free_minor == (3 + FREE_MINOR_FRAC_PRI) and not newpart == recommended_parts[-1]:
147 if self.get_extended_partition():
148 raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "This code is not yet robust enough to handle automatic partitioning with your current layout.")
149 to_create.append({ 'type': "extended", 'size': remaining_free, 'mountpoint': "", 'free_minor': free_minor })
150 free_minor = 4 + FREE_MINOR_FRAC_LOG
151 newpart['free_minor'] = free_minor
152 # Small hack to calculate optimal swap partition size
153 if newpart['type'] == "linux-swap" and physical_memory and physical_memory < 1024:
154 newpart['size'] = physical_memory * 2
155 to_create.append(newpart)
156 free_minor = free_minor + 1
157 if not newpart['size'] == "*":
158 remaining_free = remaining_free - newpart['size']
159 for newpart in to_create:
160 if newpart['size'] == "*":
161 # This doesn't seem quite right...it should probably be set to remaining_free
162 newpart['size'] = self._partitions[newpart['free_minor']].get_mb()
163 self.add_partition(newpart['free_minor'], newpart['size'], 0, 0, newpart['type'], mountpoint=newpart['mountpoint'])
106 164
107 ## 165 ##
108 # Combines free space and closes gaps in minor numbers. This is used internally 166 # Combines free space and closes gaps in minor numbers. This is used internally
109 def tidy_partitions(self): 167 def tidy_partitions(self):
110 last_minor = 0 168 last_minor = 0
112 last_free = 0 170 last_free = 0
113 last_log_free = 0 171 last_log_free = 0
114 parts = self._partitions.keys() 172 parts = self._partitions.keys()
115 parts.sort() 173 parts.sort()
116 for part in parts: 174 for part in parts:
117 if archinfo[self._arch]['extended'] and part > 4: break 175 if archinfo[self._arch]['extended'] and part > (4 + FREE_MINOR_FRAC_PRI): break
118 tmppart = self._partitions[part] 176 tmppart = self._partitions[part]
119 if tmppart.get_type() == "extended": 177 if tmppart.get_type() == "extended":
120 for part_log in parts: 178 for part_log in parts:
121 if part_log < 4.9: continue 179 if part_log < (4 + FREE_MINOR_FRAC_LOG): continue
122 tmppart_log = self._partitions[part_log] 180 tmppart_log = self._partitions[part_log]
123 if tmppart_log.get_type() == "free": 181 if tmppart_log.get_type() == "free":
124 if last_log_minor < last_log_free: 182 if last_log_minor < last_log_free:
125 self._partitions[last_log_free].set_mb(self._partitions[last_log_free].get_mb()+tmppart_log.get_mb()) 183 self._partitions[last_log_free].set_mb(self._partitions[last_log_free].get_mb()+tmppart_log.get_mb())
126 del self._partitions[part_log] 184 del self._partitions[part_log]
127 else: 185 else:
128 if not last_log_free: 186 if not last_log_free:
129 last_log_free = last_log_minor + 0.9 187 last_log_free = last_log_minor + FREE_MINOR_FRAC_LOG
130 else: 188 else:
131 lost_log_free = part_log 189 last_log_free = part_log
132 tmppart_log.set_minor(last_log_free) 190 tmppart_log.set_minor(last_log_free)
133 self._partitions[last_log_free] = tmppart_log 191 self._partitions[last_log_free] = tmppart_log
134 if part_log != last_log_free: del self._partitions[part_log] 192 if part_log != last_log_free: del self._partitions[part_log]
135 continue
136 else: 193 else:
137 if part_log > (last_log_minor + 1): 194 if part_log > (last_log_minor + 1):
138 tmppart_log.set_minor(last_log_minor + 1) 195 tmppart_log.set_minor(last_log_minor + 1)
139 last_log_minor = last_log_minor + 1 196 last_log_minor = last_log_minor + 1
140 self._partitions[last_log_minor] = tmppart_log 197 self._partitions[last_log_minor] = tmppart_log
141 del self._partitions[part_log] 198 del self._partitions[part_log]
142 continue
143 else: 199 else:
144 last_log_minor = part_log 200 last_log_minor = part_log
145 if tmppart.get_type() == "free": 201 elif tmppart.get_type() == "free":
146 if last_minor < last_free: 202 if last_minor < last_free:
147 self._partitions[last_free].set_mb(self._partitions[last_free].get_mb()+tmppart.get_mb()) 203 self._partitions[last_free].set_mb(self._partitions[last_free].get_mb()+tmppart.get_mb())
148 del self._partitions[part] 204 del self._partitions[part]
149 else: 205 else:
150 if not last_free: 206 if not last_free:
151 last_free = last_minor + 0.1 207 last_free = last_minor + FREE_MINOR_FRAC_PRI
152 else: 208 else:
153 last_free = part 209 last_free = part
154 tmppart.set_minor(last_free) 210 tmppart.set_minor(last_free)
155 self._partitions[last_free] = tmppart 211 self._partitions[last_free] = tmppart
156 if part != last_free: del self._partitions[part] 212 if part != last_free: del self._partitions[part]
157 continue
158 else: 213 else:
159 if part > (last_minor + 1): 214 if part > (last_minor + 1):
160 tmppart.set_minor(last_minor + 1) 215 tmppart.set_minor(last_minor + 1)
161 last_minor = last_minor + 1 216 last_minor = last_minor + 1
162 self._partitions[last_minor] = tmppart 217 self._partitions[last_minor] = tmppart
163 del self._partitions[part] 218 del self._partitions[part]
164 continue
165 else: 219 else:
166 last_minor = part 220 last_minor = part
167 221
168 ## 222 ##
169 # Adds a new partition to the partition info 223 # Adds a new partition to the partition info
172 # @param start Start sector (only used for existing partitions) 226 # @param start Start sector (only used for existing partitions)
173 # @param end End sector (only used for existing partitions) 227 # @param end End sector (only used for existing partitions)
174 # @param type Partition type (ext2, ext3, fat32, linux-swap, free, extended, etc.) 228 # @param type Partition type (ext2, ext3, fat32, linux-swap, free, extended, etc.)
175 # @param mountpoint='' Partition mountpoint 229 # @param mountpoint='' Partition mountpoint
176 # @param mountopts='' Partition mount options 230 # @param mountopts='' Partition mount options
231 # @param mkfsopts='' Additional mkfs options
177 def add_partition(self, free_minor, mb, start, end, type, mountpoint='', mountopts=''): 232 def add_partition(self, free_minor, mb, start, end, type, mountpoint='', mountopts='',mkfsopts=''):
178 free_minor = free_minor 233 # Automatically pick the first unused minor if not a local device
234 if not self._local_device or free_minor == -1:
235 tmpparts = self._partitions.keys()
236 tmpparts.sort()
237 tmpminor = 0
238 if len(tmpparts):
239 tmpminor = tmpparts[-1]
240 if archinfo[self._arch]['extended'] and tmpminor >= 5:
241 free_minor = tmpminor + FREE_MINOR_FRAC_LOG
242 else:
243 free_minor = tmpminor + FREE_MINOR_FRAC_PRI
244 self._partitions[free_minor] = Partition(self, free_minor, mb, 0, 0, "free")
179 new_minor = int(free_minor) + 1 245 new_minor = int(free_minor) + 1
180# print "add_partition(): free_minor=" + str(free_minor) + ", new_minor=" + str(new_minor) 246 if self._local_device:
247 # Check to see if the new minor we picked already exists. If it does, scoot all partitions from
248 # that one on down a minor
181 if self._partitions.has_key(new_minor): 249 if self._partitions.has_key(new_minor):
182 parts = self._partitions.keys() 250 parts = self._partitions.keys()
183 parts.sort() 251 parts.sort()
184 parts.reverse() 252 parts.reverse()
185 hole_at = 0 253 hole_at = 0
186 for i in range(1, parts[0]+1): 254 for i in range(1, parts[0]+1):
187 if i <= new_minor: continue 255 if i <= new_minor: continue
188 if not self._partitions.has_key(i): 256 if not self._partitions.has_key(i):
189 hole_at = i 257 hole_at = i
190 break 258 break
191 stopscooting = 0 259 stopscooting = 0
192 for i in parts: 260 for i in parts:
193 if stopscooting: break 261 if stopscooting: break
194 if i >= hole_at and hole_at: continue 262 if i >= hole_at and hole_at: continue
195 if i >= new_minor: 263 if i >= new_minor:
196 self._partitions[i].set_minor(i+1) 264 self._partitions[i].set_minor(i+1)
197 self._partitions[i+1] = self._partitions[i] 265 self._partitions[i+1] = self._partitions[i]
198 if i == new_minor: stopscooting = 1 266 if i == new_minor: stopscooting = 1
267 # If the size specified for the new partition is less than the size of the unallocated space that it
268 # is getting placed into, a new partition to represent the remaining unallocated space needs to be
269 # created.
199 if mb != self._partitions[free_minor].get_mb(): 270 if mb != self._partitions[free_minor].get_mb():
200 old_free_mb = self._partitions[free_minor].get_mb() 271 old_free_mb = self._partitions[free_minor].get_mb()
201 del self._partitions[free_minor] 272 del self._partitions[free_minor]
202 if archinfo[self._arch]['extended'] and new_minor >= 5: 273 if archinfo[self._arch]['extended'] and new_minor >= 5:
203 free_minor = new_minor + 0.9 274 free_minor = new_minor + FREE_MINOR_FRAC_LOG
275 else:
276 free_minor = new_minor + FREE_MINOR_FRAC_PRI
277 self._partitions[free_minor] = Partition(self, free_minor, old_free_mb-mb, 0, 0, "free")
204 else: 278 else:
205 free_minor = new_minor + 0.1
206 self._partitions[free_minor] = Partition(self, free_minor, old_free_mb-mb, 0, 0, "free")
207# print "add_partition(): new part doesn't use all freespace. new free part is: minor=" + str(free_minor)
208 else:
209 del self._partitions[free_minor] 279 del self._partitions[free_minor]
210 self._partitions[new_minor] = Partition(self, new_minor, mb, start, end, type, mountpoint=mountpoint, mountopts=mountopts) 280 self._partitions[new_minor] = Partition(self, new_minor, mb, start, end, type, mountpoint=mountpoint, mountopts=mountopts,mkfsopts=mkfsopts)
281 # When we create an extended partition, we have to create the partition to represent the unallocated
282 # space inside of the extended partition
211 if type == "extended": 283 if type == "extended":
212 self._partitions[4.9] = Partition(self, 4.9, mb, 0, 0, "free") 284 self._partitions[4 + FREE_MINOR_FRAC_LOG] = Partition(self, (4 + FREE_MINOR_FRAC_LOG), mb, 0, 0, "free")
213 self.tidy_partitions() 285 self.tidy_partitions()
286 return new_minor
214 287
215 ## 288 ##
216 # Removes partition from partition info 289 # Removes partition from partition info
217 # @param minor Minor of partition to remove 290 # @param minor Minor of partition to remove
218 def remove_partition(self, minor): 291 def remove_partition(self, minor):
219 tmppart = self._partitions[int(minor)] 292 tmppart = self._partitions[int(minor)]
220 free_minor = 0 293 free_minor = 0
221 if tmppart.is_logical(): 294 if tmppart.is_logical():
222 free_minor = minor-0.1 295 free_minor = int(minor-1)+FREE_MINOR_FRAC_LOG
223 else: 296 else:
224 free_minor = minor-0.9 297 free_minor = int(minor-1)+FREE_MINOR_FRAC_PRI
298 if free_minor in self._partitions:
299 self._partitions[free_minor].set_mb(self._partitions[free_minor].get_mb() + tmppart.get_mb())
300 else:
225 self._partitions[free_minor] = Partition(self, free_minor, tmppart.get_mb(), 0, 0, "free", format=False, existing=False) 301 self._partitions[free_minor] = Partition(self, free_minor, tmppart.get_mb(), 0, 0, "free", format=False, existing=False)
226 del self._partitions[int(minor)] 302 del self._partitions[int(minor)]
227 self.tidy_partitions() 303 self.tidy_partitions()
228 304
229 ## 305 ##
230 # Returns free space (no longer used) 306 # This function clears the partition table
231 # @param start Start sector for search 307 def clear_partitions(self):
232 def get_free_space(self, start): 308 self._partitions = { (0 + FREE_MINOR_FRAC_PRI): Partition(self, (0 + FREE_MINOR_FRAC_PRI), self.get_total_mb(), 0, 0, "free", format=False, existing=False) }
233 GAP_SIZE = 100 309
310 ##
311 # Returns an ordered list (disk order) of partition minors
312 def get_ordered_partition_list(self):
234 parts = self._partitions.keys() 313 parts = self._partitions.keys()
235 parts.sort() 314 parts.sort()
236 lastend_pri = 0 315 partlist = []
237 lastend_log = 0
238 free_start = -1
239 free_end = -1
240 if start > self._total_sectors: return (-1, -1)
241 for part in parts:
242 if part > 4: break
243 tmppart = self._partitions[part]
244 if (tmppart.get_start() > (lastend_pri + GAP_SIZE)) and (lastend_pri >= start):
245 free_start = lastend_pri
246 free_end = tmppart.get_start() - 1
247 break
248 if tmppart.is_extended() and start < tmppart.get_end():
249 lastend_log = tmppart.get_start()
250 for part_log in parts:
251 if part_log < 5: continue
252 tmppart_log = self._partitions[part_log]
253 if (tmppart_log.get_start() > (lastend_log + GAP_SIZE)) and (lastend_log >= start):
254 free_start = lastend_log
255 free_end = tmppart_log.get_start() - 1
256 break
257 lastend_log = tmppart_log.get_end() + 1
258 if free_start == -1 and lastend_log < tmppart.get_end():
259 free_start = lastend_log
260 free_end = tmppart.get_end()
261 break
262 lastend_pri = tmppart.get_end() + 1
263 if free_start == -1 and lastend_pri < self._total_sectors:
264 free_start = lastend_pri
265 free_end = self._total_sectors
266 return (free_start, free_end)
267
268 ##
269 # Gets partition containing a certain sector (no longer used)
270 # @param sector Sector to look at
271 # @param ignore_extended=1 Ignore extended partitions
272 def get_partition_at(self, sector, ignore_extended=1):
273 parts = self._partitions.keys()
274 parts.sort()
275 for part in parts:
276 tmppart = self._partitions[part]
277 if ignore_extended and tmppart.is_extended(): continue
278 if (sector >= tmppart.get_start()) and (sector <= tmppart.get_end()):
279 return part
280 return 0
281
282 ##
283 # Returns free minor (no longer used)
284 # @param start Parameter description
285 # @param end Parameter description
286 def get_free_minor_at(self, start, end):
287 parts = self._partitions.keys()
288 parts.sort()
289 minor = 1
290 lastpart = 0
291 for part in parts:
292 if part > 4: break
293 tmppart = self._partitions[part]
294 if end < tmppart.get_start():
295 minor = part
296 if (minor - 1) > lastpart: minor = lastpart + 1
297 break
298 if tmppart.is_extended() and start < tmppart.get_end():
299 minor = 5
300 lastpart = 4
301 for part_log in parts:
302 if part_log < 5: continue
303 tmppart_log = self._partitions[part_log]
304 if end < tmppart_log.get_start():
305 minor = part_log
306 if (minor - 1) > lastpart: minor = lastpart + 1
307 break
308 minor = part_log + 1
309 lastpart = part_log
310 break
311 minor = part + 1
312 lastpart = part
313 return minor
314
315 ##
316 # Returns an ordered list (disk order) of partition minors
317 def get_ordered_partition_list(self):
318 parts = self._partitions.keys()
319 parts.sort()
320 partlist = []
321 tmppart = None 316 tmppart = None
322 for part in parts: 317 for part in parts:
323 if archinfo[self._arch]['extended'] and part > 4.1: break 318 if archinfo[self._arch]['extended'] and part > (4 + FREE_MINOR_FRAC_PRI): break
324 tmppart = self._partitions[part] 319 tmppart = self._partitions[part]
325 partlist.append(part) 320 partlist.append(part)
326 if tmppart.is_extended(): 321 if tmppart.is_extended():
327 for part_log in parts: 322 for part_log in parts:
328 if part_log < 4.9: continue 323 if part_log < (4 + FREE_MINOR_FRAC_LOG): continue
329 partlist.append(part_log) 324 partlist.append(part_log)
330 return partlist 325 return partlist
331 326
332 ## 327 ##
333 # Returns partition info in a format suitable for passing to install_profile.set_partition_tables() 328 # Returns partition info in a format suitable for passing to install_profile.set_partition_tables()
334 def get_install_profile_structure(self): 329 def get_install_profile_structure(self):
335 devdic = {} 330 devdic = {}
336 for part in self._partitions: 331 for part in self._partitions:
337 tmppart = self._partitions[part] 332 tmppart = self._partitions[part]
338 devdic[part] = { 'mb': tmppart.get_mb(), 'minor': float(part), 'origminor': tmppart.get_orig_minor(), 'start': tmppart.get_start(), 'end': tmppart.get_end(), 'type': tmppart.get_type(), 'mountpoint': tmppart.get_mountpoint(), 'mountopts': tmppart.get_mountopts(), 'format': tmppart.get_format() } 333 devdic[part] = { 'mb': tmppart.get_mb(), 'minor': float(part), 'origminor': tmppart.get_orig_minor(), 'type': tmppart.get_type(), 'mountpoint': tmppart.get_mountpoint(), 'mountopts': tmppart.get_mountopts(), 'format': tmppart.get_format(), 'mkfsopts': tmppart.get_mkfsopts(), 'start': 0, 'end': 0, 'resized': tmppart.get_resized() }
339 return devdic 334 return devdic
340 335
341 ## 336 ##
342 # Returns the minor of the extended partition, if any 337 # Returns the minor of the extended partition, if any
343 def get_extended_partition(self): 338 def get_extended_partition(self):
346 if tmppart.is_extended(): 341 if tmppart.is_extended():
347 return part 342 return part
348 return 0 343 return 0
349 344
350 ## 345 ##
346 # Returns the drive model
347 def get_model(self):
348 if self._local_device:
349 return self._parted_dev.model
350 else:
351 return "Generic disk"
352
353 ##
351 # Sets the disklabel type 354 # Sets the disklabel type
352 def set_disklabel(self, disklabel): 355 def set_disklabel(self, disklabel):
353 self._disklabel = disklabel 356 self._disklabel = disklabel
354 357
355 ## 358 ##
358 return self._disklabel 361 return self._disklabel
359 362
360 ## 363 ##
361 # Returns the number of sectors on the device 364 # Returns the number of sectors on the device
362 def get_num_sectors(self): 365 def get_num_sectors(self):
363 return int(self._total_sectors) 366 return long(self._total_sectors)
364 367
365 ## 368 ##
366 # Returns the size of a cylinder in bytes 369 # Returns the size of a cylinder in bytes
367 def get_cylinder_size(self): 370 def get_cylinder_size(self):
368 return int(self._cylinder_bytes) 371 return long(self._cylinder_bytes)
369 372
370 ## 373 ##
371 # Returns the size of a sector in bytes 374 # Returns the size of a sector in bytes
372 def get_sector_size(self): 375 def get_sector_size(self):
373 return int(self._sector_bytes) 376 return long(self._sector_bytes)
374 377
375 ## 378 ##
376 # Returns the number of cylinders 379 # Returns the number of cylinders
377 def get_num_cylinders(self): 380 def get_num_cylinders(self):
378 return int(self._geometry['cylinders']) 381 return long(self._geometry['cylinders'])
379 382
380 ## 383 ##
381 # Returns the total number of bytes on the device 384 # Returns the total number of bytes on the device
382 def get_drive_bytes(self): 385 def get_drive_bytes(self):
383 return int(self._total_bytes) 386 return long(self._total_bytes)
384 387
385 ## 388 ##
386 # Returns the total number of MB on the device 389 # Returns the total number of MB on the device
387 def get_total_mb(self): 390 def get_total_mb(self):
391 if self._local_device:
388 return self._total_mb 392 return self._total_mb
393 else:
394 total_mb = 0
395 for tmppart in self._partitions:
396 total_mb += self._partitions[tmppart].get_mb()
397 return total_mb
389 398
390 ## 399 ##
391 # Returns partition info dictionary 400 # Returns partition info dictionary
392 def get_partitions(self): 401 def get_partitions(self):
393 return self._partitions 402 return self._partitions
394 403
395 ## 404 ##
396 # Prints disk geometry to STDOUT (no longer used) 405 # Prints disk geometry to STDOUT (no longer used)
397 def print_geometry(self): 406 def print_geometry(self):
398 print self._total_bytes, self._geometry 407 print self._total_bytes, self._geometry
399
400 ##
401 # Utility function for running a command and returning it's output as a list
402 # @param cmd Command to run
403 def _run(self, cmd):
404 "Runs a command and returns the output"
405
406 # Run command
407 output_string = commands.getoutput(cmd)
408
409 # What we will return
410 output_list = []
411
412 # As long as there is a new line in the output_string
413 while output_string.find("\n") != -1:
414
415 # Find the \n in the string
416 index = output_string.find("\n") + 1
417
418 # Add the line to the output and remove it from
419 # the output_string
420 output_list.append(output_string[:index])
421 output_string = output_string[index:]
422
423 # return output
424 return output_list
425 408
426## 409##
427# This class represents a partition within a GLIStorageDevice object 410# This class represents a partition within a GLIStorageDevice object
428class Partition: 411class Partition:
429 "Class representing a single partition within a Device object" 412 "Class representing a single partition within a Device object"
437 _mountpoint = None 420 _mountpoint = None
438 _mountopts = None 421 _mountopts = None
439 _format = None 422 _format = None
440 _resizeable = None 423 _resizeable = None
441 _min_mb_for_resize = 0 424 _min_mb_for_resize = 0
442 _mb = 0 425 _mb = ""
426 _mkfsopts = None
427 _resized = False
443 428
444 ## 429 ##
445 # Initialization function for the Partition class 430 # Initialization function for the Partition class
446 # @param device Parent GLIStorageDevice object 431 # @param device Parent GLIStorageDevice object
447 # @param minor Minor of partition 432 # @param minor Minor of partition
449 # @param start Parameter Start sector of partition 434 # @param start Parameter Start sector of partition
450 # @param end Parameter Start sector of partition 435 # @param end Parameter Start sector of partition
451 # @param type Parameter Type of partition (ext2, ext3, fat32, linux-swap, free, extended, etc.) 436 # @param type Parameter Type of partition (ext2, ext3, fat32, linux-swap, free, extended, etc.)
452 # @param mountpoint='' Mountpoint of partition 437 # @param mountpoint='' Mountpoint of partition
453 # @param mountopts='' Mount options of partition 438 # @param mountopts='' Mount options of partition
439 # @param mkfsopts='' Additional mkfs options
454 # @param format=True Format partition 440 # @param format=True Format partition
455 # @param existing=False This partition exists on disk 441 # @param existing=False This partition exists on disk
456 def __init__(self, device, minor, mb, start, end, type, mountpoint='', mountopts='', format=True, existing=False, origminor=0): 442 def __init__(self, device, minor, mb, start, end, type, mountpoint='', mountopts='', format=True, existing=False, origminor=0, mkfsopts='', resized=False):
457 self._device = device 443 self._device = device
458 self._minor = float(minor) 444 self._minor = float(minor)
459 self._start = int(start) 445 self._start = long(start)
460 self._end = int(end) 446 self._end = long(end)
461 self._type = type or "unknown" 447 self._type = type or "unknown"
462 self._mountpoint = mountpoint 448 self._mountpoint = mountpoint
463 self._mountopts = mountopts 449 self._mountopts = mountopts
464 self._format = format 450 self._format = format
465 self._mb = mb 451 self._mb = mb
466 self._orig_minor = origminor 452 self._orig_minor = origminor
453 self._mkfsopts = mkfsopts
454 self._resizeable = False
455 self._resized = resized
467 if type != "free": 456 if type != "free":
468 if existing and not origminor: 457 if existing and not origminor:
469 self._orig_minor = self._minor 458 self._orig_minor = self._minor
470 self._minor = int(self._minor) 459 self._minor = int(self._minor)
471 self._orig_minor = int(self._orig_minor) 460 self._orig_minor = int(self._orig_minor)
472 if existing: 461 if existing:
462 try:
473 parted_part = device._parted_disk.get_partition(self._orig_minor) 463 parted_part = device._parted_disk.get_partition(self._orig_minor)
474 label_type = device._parted_disk.type.name 464 label_type = device._parted_disk.type.name
475 if label_type == "loop": 465 if label_type == "loop":
476 dev_node = device._device 466 dev_node = device._device
477 else: 467 else:
478 dev_node = device._device + str(self._orig_minor) 468 dev_node = device._device + str(self._orig_minor)
479 print "dev_node = " + dev_node 469# print "dev_node = " + dev_node
480 if type == "ntfs": 470 if type == "ntfs":
481 min_bytes = int(commands.getoutput("ntfsresize -f --info " + dev_node + " | grep -e '^You might resize' | sed -e 's/You might resize at //' -e 's/ bytes or .\+//'")) 471 min_bytes = long(commands.getoutput("ntfsresize -f --info " + dev_node + " | grep -e '^You might resize' | sed -e 's/You might resize at //' -e 's/ bytes or .\+//'"))
482 self._min_mb_for_resize = int(min_bytes / MEGABYTE) + 1 472 self._min_mb_for_resize = long(min_bytes / MEGABYTE) + 1
483 self._resizeable = True 473 self._resizeable = True
484 elif type == "ext2" or type == "ext3": 474 elif type == "ext2" or type == "ext3":
485 block_size = int(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Block size:' | sed -e 's/^Block size:\s\+//'"))) 475 block_size = long(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Block size:' | sed -e 's/^Block size:\s\+//'")))
486 free_blocks = int(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Free blocks:' | sed -e 's/^Free blocks:\s\+//'"))) 476 free_blocks = long(string.strip(commands.getoutput("dumpe2fs -h " + dev_node + r" 2>&1 | grep -e '^Free blocks:' | sed -e 's/^Free blocks:\s\+//'")))
487 free_bytes = int(block_size * free_blocks) 477 free_bytes = long(block_size * free_blocks)
488 # can't hurt to pad (the +50) it a bit since this is really just a guess 478 # can't hurt to pad (the +50) it a bit since this is really just a guess
489 self._min_mb_for_resize = self._mb - int(free_bytes / MEGABYTE) + 50 479 self._min_mb_for_resize = self._mb - long(free_bytes / MEGABYTE) + 50
490 self._resizeable = True 480 self._resizeable = True
491 else: 481 else:
492 parted_part = self._device._parted_disk.get_partition(int(self._orig_minor)) 482 parted_part = self._device._parted_disk.get_partition(int(self._orig_minor))
493 try:
494 parted_fs = parted_part.geom.file_system_open() 483 parted_fs = parted_part.geom.file_system_open()
495 except:
496 self._resizeable = False
497 return
498 resize_constraint = parted_fs.get_resize_constraint() 484 resize_constraint = parted_fs.get_resize_constraint()
499 min_bytes = resize_constraint.min_size * self._device._sector_bytes 485 min_bytes = resize_constraint.min_size * self._device._sector_bytes
500 self._min_mb_for_resize = int(min_bytes / MEGABYTE) + 1 486 self._min_mb_for_resize = long(min_bytes / MEGABYTE) + 1
501 self._resizeable = True 487 self._resizeable = True
488 except:
489 self._resizeable = False
502 490
503 ## 491 ##
504 # Returns whether or not the partition is extended 492 # Returns whether or not the partition is extended
505 def is_extended(self): 493 def is_extended(self):
506 if self._type == "extended": 494 if self._type == "extended":
510 498
511 ## 499 ##
512 # Returns whether or not the partition is logical 500 # Returns whether or not the partition is logical
513 def is_logical(self): 501 def is_logical(self):
514 if self._type == "free": 502 if self._type == "free":
515 if int(self._minor) + 0.9 == self._minor: 503 if int(self._minor) + FREE_MINOR_FRAC_LOG == self._minor:
516 return True 504 return True
517 else: 505 else:
518 return False 506 return False
519 elif archinfo[self._device._arch]['extended'] and self._minor > 4: 507 elif archinfo[self._device._arch]['extended'] and self._minor > 4:
520 return True 508 return True
542 return None 530 return None
543 else: 531 else:
544 return self._device.get_partition_at(self._start, ignore_extended=0) 532 return self._device.get_partition_at(self._start, ignore_extended=0)
545 533
546 ## 534 ##
535 # Sets the options passed to mkfs
536 # @param mkfsopts Options passed to mkfs
537 def set_mkfsopts(self, mkfsopts):
538 self._mkfsopts = mkfsopts
539
540 ##
541 # Returns the options passes to mkfs
542 def get_mkfsopts(self):
543 return self._mkfsopts
544
545 ##
547 # Sets the start sector for the partition 546 # Sets the start sector for the partition
548 # @param start Start sector 547 # @param start Start sector
549 def set_start(self, start): 548 def set_start(self, start):
550 self._start = int(start) 549 self._start = long(start)
551 550
552 ## 551 ##
553 # Returns the start sector for the partition 552 # Returns the start sector for the partition
554 def get_start(self): 553 def get_start(self):
555 return int(self._start) 554 return long(self._start)
556 555
557 ## 556 ##
558 # Sets the end sector of the partition 557 # Sets the end sector of the partition
559 # @param end End sector 558 # @param end End sector
560 def set_end(self, end): 559 def set_end(self, end):
561 self._end = int(end) 560 self._end = long(end)
562 561
563 ## 562 ##
564 # Returns end sector for the partition 563 # Returns end sector for the partition
565 def get_end(self): 564 def get_end(self):
566 return int(self._end) 565 return long(self._end)
567 566
568 ## 567 ##
569 # Returns size of partition in MB 568 # Returns size of partition in MB
570 def get_mb(self): 569 def get_mb(self):
571 return int(self._mb) 570 return self._mb
572 571
573 ## 572 ##
574 # Sets size of partition in MB 573 # Sets size of partition in MB
575 # @param mb Parameter description 574 # @param mb Parameter description
576 def set_mb(self, mb): 575 def set_mb(self, mb):
577 self._mb = int(mb) 576 self._mb = mb
578 577
579 ## 578 ##
580 # Sets type of partition 579 # Sets type of partition
581 # @param type Parameter description 580 # @param type Parameter description
582 def set_type(self, type): 581 def set_type(self, type):
635 # Returns the mount options for the partition 634 # Returns the mount options for the partition
636 def get_mountopts(self): 635 def get_mountopts(self):
637 return self._mountopts 636 return self._mountopts
638 637
639 ## 638 ##
640 # Set whether to format the partition 639 # Returns whether to partition is resized
641 # @param format Format this partition (True/False)
642 def set_format(self, format):
643 self._format = format
644
645 ##
646 # Returns whether to format the partition
647 def get_format(self): 640 def get_resized(self):
648 return self._format 641 return self._resized
649 642
650 ## 643 ##
651 # Returns whether the partition is resizeable 644 # Returns whether the partition is resizeable
652 def is_resizeable(self): 645 def is_resizeable(self):
653 return self._resizeable 646 return self._resizeable
671 # Returns maximum MB for resize 664 # Returns maximum MB for resize
672 def get_max_mb_for_resize(self): 665 def get_max_mb_for_resize(self):
673 if self._resizeable: 666 if self._resizeable:
674 free_minor = 0 667 free_minor = 0
675 if self.is_logical(): 668 if self.is_logical():
676 free_minor = self._minor + 0.9 669 free_minor = self._minor + FREE_MINOR_FRAC_LOG
677 else: 670 else:
678 free_minor = self._minor + 0.1 671 free_minor = self._minor + FREE_MINOR_FRAC_PRI
679 if free_minor in self._device._partitions: 672 if free_minor in self._device._partitions:
680 return self._mb + self._device._partitions[free_minor]._mb 673 return self._mb + self._device._partitions[free_minor]._mb
681 else: 674 else:
682 return self._mb 675 return self._mb
683 else: 676 else:
687 # Resizes the partition 680 # Resizes the partition
688 # @param mb New size in MB 681 # @param mb New size in MB
689 def resize(self, mb): 682 def resize(self, mb):
690 free_minor = self._minor 683 free_minor = self._minor
691 if self.is_logical(): 684 if self.is_logical():
692 free_minor += 0.9 685 free_minor += FREE_MINOR_FRAC_LOG
693 else: 686 else:
694 free_minor += 0.1 687 free_minor += FREE_MINOR_FRAC_PRI
695 if mb < self._mb: 688 if mb < self._mb:
696 # Shrinking 689 # Shrinking
697 if not free_minor in self._device._partitions: 690 if not free_minor in self._device._partitions:
698 self._device._partitions[free_minor] = Partition(self._device, free_minor, 0, 0, 0, "free", format=False, existing=False) 691 self._device._partitions[free_minor] = Partition(self._device, free_minor, 0, 0, 0, "free", format=False, existing=False)
699 self._device._partitions[free_minor]._mb += self._mb - mb 692 self._device._partitions[free_minor]._mb += self._mb - mb
704 self._mb = mb 697 self._mb = mb
705 elif mb > self._mb: 698 elif mb > self._mb:
706 # Growing 699 # Growing
707 self._device._partitions[free_minor]._mb = mb - self._mb 700 self._device._partitions[free_minor]._mb = mb - self._mb
708 self._mb = mb 701 self._mb = mb
702 self._resized = True
709 703
710## 704##
711# Returns a list of detected partitionable devices 705# Returns a list of detected partitionable devices
712def detect_devices(): 706def detect_devices():
713 devices = [] 707 devices = []
714 708
715 # Make sure sysfs exists 709 # Make sure sysfs exists
710 # TODO: rewrite for 2.4 support
716 if not os.path.exists("/sys/bus"): 711 if not os.path.exists("/sys/bus"):
717 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "no sysfs found (you MUST use a kernel >2.6)") 712 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "no sysfs found (you MUST use a kernel >2.6)")
718 # Make sure /proc/partitions exists 713 # Make sure /proc/partitions exists
719 if not os.path.exists("/proc/partitions"): 714 if not os.path.exists("/proc/partitions"):
720 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "/proc/partitions does not exist! Please make sure procfs is in your kernel and mounted!") 715 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "/proc/partitions does not exist! Please make sure procfs is in your kernel and mounted!")
757 752
758 partitions.append(( major, minor, device )) 753 partitions.append(( major, minor, device ))
759 754
760 # Scan sysfs for the devices of type 'x' 755 # Scan sysfs for the devices of type 'x'
761 # 'x' being a member of the list below: 756 # 'x' being a member of the list below:
757 # TODO: rewrite for 2.4 support
762 for dev_type in [ "ide", "scsi" ]: # Other device types? usb? fw? 758 for dev_type in [ "ide", "scsi" ]: # Other device types? usb? fw?
763 if os.path.exists("/sys/bus/" + dev_type): 759 if os.path.exists("/sys/bus/" + dev_type):
764 sysfs_devices = os.listdir("/sys/bus/"+dev_type+"/devices") 760 sysfs_devices = os.listdir("/sys/bus/"+dev_type+"/devices")
765 761
766 # For each device in the devices on that bus 762 # For each device in the devices on that bus

Legend:
Removed from v.596  
changed lines
  Added in v.1291

  ViewVC Help
Powered by ViewVC 1.1.20