/[gli]/branches/overhaul/src/Partitioning.py
Gentoo

Contents of /branches/overhaul/src/Partitioning.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1596 - (show annotations) (download) (as text)
Sun Jan 7 02:11:42 2007 UTC (7 years, 7 months ago) by agaffney
File MIME type: text/x-python
File size: 25327 byte(s)
more cleanups
1 # Copyright 1999-2006 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
6 import commands, string, os, parted
7 from glob import glob
8 from GLIException import *
9 import GLIUtility
10 import time
11
12 MEGABYTE = 1024 * 1024
13
14 supported_filesystems = {
15 'all': ( 'ext2', 'ext3', 'linux-swap', 'xfs', 'jfs', 'reiserfs' ),
16 'x86': ( 'ntfs', 'fat16', 'fat32' ),
17 'amd64': ( 'ntfs', 'fat16', 'fat32' ),
18 'ppc': ( 'reiserfs', 'apple_bootstrap', 'hfs' )
19 }
20
21 labelinfo = {
22 'msdos': { 'ignoredparts': [], 'extended': True },
23 'mac': { 'ignoredparts': [1], 'extended': False },
24 'sun': { 'ignoredparts': [3], 'extended': False },
25 'loop': { 'ignoredparts': [], 'extended': False }
26 }
27
28 archinfo = {
29 'sparc': 'sun',
30 'hppa': 'msdos',
31 'x86': 'msdos',
32 'amd64': 'msdos',
33 'ppc': 'mac'
34 }
35
36 ##
37 # This class provides a partitioning abstraction for the frontends
38 class Device:
39 "Class representing a partitionable device."
40
41 _device = None
42 _partitions = None
43 _geometry = None
44 _parted_dev = None
45 _parted_disk = None
46 _arch = None
47 _disklabel = None
48
49 ##
50 # Initialization function for Device class
51 # @param device Device node (e.g. /dev/hda) of device being represented
52 # @param arch Architecture that we're partition for (defaults to 'x86' for now)
53 def __init__(self, device, arch="x86"):
54 self._device = device
55 self._arch = arch
56 self._partitions = []
57 self._geometry = {}
58 self._parted_dev = parted.PedDevice.get(self._device)
59 try:
60 self._parted_disk = parted.PedDisk.new(self._parted_dev)
61 except:
62 self._parted_disk = self._parted_dev.disk_new_fresh(parted.disk_type_get(archinfo[self._arch]))
63 self._disklabel = self._parted_disk.type.name
64 self._labelinfo = labelinfo[self._disklabel]
65 self.set_disk_geometry_from_disk()
66 self.set_partitions_from_disk()
67
68 def __getitem__(self, name):
69 return self.get_partition(name)
70
71 def __iter__(self):
72 for part in self.get_partitions():
73 yield part
74
75 ##
76 # Returns list of supported filesystems based on arch
77 def get_supported_filesystems(self):
78 return supported_filesystems['all'] + supported_filesystems[self._arch]
79
80 ##
81 # Sets disk geometry info from disk. This function is used internally by __init__()
82 def set_disk_geometry_from_disk(self):
83 self._geometry = {
84 'sector_size': self._parted_dev.sector_size,
85 'total_bytes': self._parted_dev.length * self._parted_dev.sector_size,
86 'heads': self._parted_dev.heads,
87 'cylinders': self._parted_dev.cylinders,
88 'sectors': self._parted_dev.sectors,
89 'cylinder_bytes': self._parted_dev.heads * self._parted_dev.sectors * self._parted_dev.sector_size,
90 'total_sectors': self._parted_dev.length,
91 'sectors_in_cylinder': self._parted_dev.heads * self._parted_dev.sectors,
92 'total_mb': long((self._parted_dev.length * self._parted_dev.sector_size) / MEGABYTE)
93 }
94
95 ##
96 # Sets partition info from disk.
97 def set_partitions_from_disk(self):
98 self._partitions = []
99 parted_part = self._parted_disk.next_partition()
100 while parted_part:
101 # Make sure it's a physical partition or unallocated space
102 if parted_part.num >= 1 or parted_part.type_name == "free":
103 self._partitions.append(Partition(self, parted_part))
104 parted_part = self._parted_disk.next_partition(parted_part)
105
106 ##
107 # Returns the partition object with the specified minor
108 # @param idx Index of partition object
109 def get_partition(self, idx):
110 # for part in self._partitions:
111 # if part.get_minor() == minor:
112 # return part
113 # return None
114 return self._partitions[idx]
115
116 def get_partition_idx_from_part(self, part):
117 for idx, tmppart in enumerate(self._partitions):
118 if tmppart['start'] == part['start'] and tmppart['end'] == part['end']:
119 return idx
120 else:
121 return -1
122
123 def get_partition_idx_from_minor(self, minor):
124 for idx, tmppart in enumerate(self._partitions):
125 if tmppart['minor'] == minor:
126 return idx
127 else:
128 return -1
129
130 def get_partition_idx_from_start_end(self, start, end):
131 for idx, tmppart in enumerate(self._partitions):
132 if tmppart['start'] == start and tmppart['end'] == end:
133 return idx
134 else:
135 return -1
136
137 def get_partition_position(self, minor):
138 for i, part in enumerate(self._partitions):
139 if part['minor'] == minor:
140 return i
141 return -1
142
143 def get_partiton_at_start(self, start):
144 for i, part in enumerate(self._partitions):
145 if part['start'] == start:
146 return i
147 return -1
148
149 ##
150 # Returns name of device (e.g. /dev/hda) being represented
151 def get_device(self):
152 return self._device
153
154 ##
155 # Uses magic to apply the recommended partition layout
156 def do_recommended(self):
157 freeidx = -1
158 free_part = None
159 recommended_parts = [ { 'type': "ext2", 'size': 100, 'mountpoint': "/boot" },
160 { 'type': "linux-swap", 'size': 512, 'mountpoint': "" },
161 { 'type': "ext3", 'size': "*", 'mountpoint': "/" } ]
162 to_create = []
163 physical_memory = int(GLIUtility.spawn(r"free -m | egrep '^Mem:' | sed -e 's/^Mem: \+//' -e 's/ \+.\+$//'", return_output=True)[1].strip())
164
165 self.clear_partitions()
166
167 # parts = self.get_ordered_partition_list()
168 # Search for concurrent unallocated space >=4GB
169 for idx, part in self._partitions:
170 if part['type'] == "free" and part['mb'] >= 4096:
171 freeidx = idx
172 free_part = part
173 break
174 else:
175 raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "You do not have atleast 4GB of concurrent unallocated space. Please remove some partitions and try again.")
176 remaining_free = free_part['mb']
177 # XXX: I left off here
178 for newpart in recommended_parts:
179 # extended/logical partitions suck like a hoover
180 if self._labelinfo['extended'] and free_minor == (3 + FREE_MINOR_FRAC_PRI) and not newpart == recommended_parts[-1]:
181 if self.get_extended_partition():
182 raise GLIException("RecommendedPartitionLayoutError", "notice", "do_recommended", "This code is not yet robust enough to handle automatic partitioning with your current layout.")
183 to_create.append({ 'type': "extended", 'size': remaining_free, 'mountpoint': "", 'free_minor': free_minor })
184 free_minor = 4 + FREE_MINOR_FRAC_LOG
185 newpart['free_minor'] = free_minor
186 # Small hack to calculate optimal swap partition size
187 if newpart['type'] == "linux-swap" and physical_memory:
188 newpart['size'] = physical_memory * 2
189 if newpart['size'] > 1024:
190 newpart['size'] = 1024
191 to_create.append(newpart)
192 free_minor = free_minor + 1
193 if not newpart['size'] == "*":
194 remaining_free = remaining_free - newpart['size']
195 for newpart in to_create:
196 if newpart['size'] == "*":
197 # This doesn't seem quite right...it should probably be set to remaining_free
198 # newpart['size'] = self.get_partition(newpart['free_minor']).get_mb()
199 newpart['size'] = remaining_free
200 self.add_partition(newpart['free_minor'], newpart['size'], 0, 0, newpart['type'], mountpoint=newpart['mountpoint'])
201
202 def _megabytes_to_sectors(self, mb, sector_bytes=512):
203 return long(mb * MEGABYTE / sector_bytes)
204
205 def _sectors_to_megabytes(self, sectors, sector_bytes=512):
206 return float((float(sectors) * sector_bytes)/ float(MEGABYTE))
207
208 def _wait_for_device_node(self, devnode):
209 if GLIUtility.is_file("/sbin/udevsettle"):
210 GLIUtility.spawn("/sbin/udevsettle")
211 if not GLIUtility.is_file(devnode):
212 GLIUtility.spawn("/sbin/udevsettle")
213 else:
214 for i in range(0, 10):
215 if GLIUtility.is_file(devnode):
216 break
217 time.sleep(1)
218 time.sleep(1)
219 for i in range(0, 10):
220 if GLIUtility.is_file(devnode):
221 break
222 time.sleep(1)
223
224 ##
225 # Adds a new partition to the partition info
226 # @param freeidx minor of unallocated space partition is being created in
227 # @param mb size of partition in MB
228 # @param type Partition type (ext2, ext3, fat32, linux-swap, free, extended, etc.)
229 def add_partition(self, freeidx, mb, fs, pregap=0):
230 types = { 'primary': parted.PARTITION_PRIMARY, 'extended': parted.PARTITION_EXTENDED, 'logical': parted.PARTITION_LOGICAL }
231 fs_types = {}
232 fstype = None
233 try:
234 free_part = self._partitions[freeidx]
235 except:
236 # raise an exception here
237 pass
238 if mb > free_part['mb']:
239 # raise an exception here
240 pass
241 # Enumerate supported filesystem types
242 fs_type = parted.file_system_type_get_next()
243 while fs_type:
244 fs_types[fs_type.name] = fs_type
245 fs_type = parted.file_system_type_get_next(fs_type)
246 # apple_bootstrap is a "magic" hfs
247 if fs == "apple_bootstrap":
248 fs = "hfs"
249 # grab relevant parted filesystemtype object
250 if fs:
251 fstype = fs_types[fs]
252 # determine correct partition type
253 parttype = "primary"
254 if fs == "extended":
255 fstype = None
256 parttype = "extended"
257 elif free_part.is_logical():
258 parttype = "logical"
259 # figure out start/end sectors
260 start = free_part['start'] + self._megabytes_to_sectors(pregap)
261 end = start + self._megabytes_to_sectors(mb)
262 parted_newpart = self._parted_disk.partition_new(types[parttype], fstype, start, end)
263 constraint = self._parted_disk.dev.constraint_any()
264 self._parted_disk.add_partition(parted_newpart, constraint)
265 self._parted_disk.commit()
266 self.set_partitions_from_disk()
267 if parttype != "extended":
268 newpart = self._partitions[self.get_partition_position(parted_newpart.num)]
269 devnode = newpart['devnode']
270 format_cmds = { 'linux-swap': "mkswap", 'fat16': "mkfs.vfat -F 16", 'fat32': "mkfs.vfat -F 32",
271 'ntfs': "mkntfs", 'xfs': "mkfs.xfs -f", 'jfs': "mkfs.jfs -f",
272 'reiserfs': "mkfs.reiserfs -f", 'ext2': "mkfs.ext2", 'ext3': "mkfs.ext3",
273 'hfs': "hformat", 'apple_bootstrap': "hformat"
274 }
275 if fs in format_cmds:
276 cmdname = format_cmds[fs]
277 else:
278 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Unknown partition type " + fstype)
279 # sleep a bit first
280 time.sleep(1)
281 self._wait_for_device_node(devnode)
282 cmd = "%s %s" % (cmdname, devnode)
283 ret = GLIUtility.spawn(cmd)
284 if not GLIUtility.exitsuccess(ret):
285 raise GLIException("PartitionFormatError", 'fatal', '_partition_format_step', "Could not create %s filesystem on %s" % (fstype, devnode))
286 self.set_partitions_from_disk()
287 newidx = self.get_partition_idx_from_start_end(parted_newpart.geom.start, parted_newpart.geom.end)
288 return newidx
289
290 ##
291 # Removes partition from partition info
292 # @param minor Minor of partition to remove
293 def delete_partition(self, partidx):
294 try:
295 tmp_part = self._partitions[partidx]
296 except:
297 # raise exception here
298 pass
299 if tmp_part['minor'] < 1:
300 # raise an exception here
301 pass
302 try:
303 self._parted_disk.delete_partition(self._parted_disk.get_partition(tmp_part['minor']))
304 except:
305 # raise an exception here
306 pass
307 self._parted_disk.commit()
308 self.set_partitions_from_disk()
309
310 # def resize_partition(self, partidx, mb):
311 # part = self._partitions[partidx]
312 # type = part['type']
313 # start = part['start']
314 # end = start + long(mb * MEGABYTE / 512) - 1
315 #
316 # total_sectors = end - start + 1
317 # total_bytes = long(total_sectors) * 512
318 #
319 # # Delete partition and recreate at same start point with new size if growing
320 # if mb > part['mb']:
321 # curminor = self._find_current_minor_for_part(device, start)
322 # self._parted_disk.delete_partition(self._parted_disk.get_partition(part['minor']))
323 # if part['logical']:
324 # tmptype = "logical"
325 # else:
326 # tmptype = "primary"
327 # self._add_partition(parted_disk, start, end, tmptype, tmppart_new['type'], strict_start=True)
328 # parted_disk.commit()
329 #
330 # curminor = self._find_current_minor_for_part(device, start)
331 # devnode = device + str(curminor)
332 #
333 # self._wait_for_device_node(devnode)
334 #
335 # if type in ("ext2", "ext3"):
336 # resizecmd = "resize2fs %s %sK" % (devnode, str(int((total_bytes - (2 * MEGABYTE)) / 1024)))
337 # self._logger.log("_partition_resize_step(): running: " + resizecmd)
338 # ret = GLIUtility.spawn(resizecmd, logfile=self._compile_logfile, append_log=True)
339 # if not GLIUtility.exitsuccess(ret):
340 # raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize ext2/3 filesystem on " + devnode)
341 # elif type == "ntfs":
342 # ret = GLIUtility.spawn("yes | ntfsresize -v --size " + str(total_bytes) + " " + devnode, logfile=self._compile_logfile, append_log=True)
343 # if not GLIUtility.exitsuccess(ret):
344 # raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize NTFS filesystem on " + devnode)
345 # elif type in ("linux-swap", "fat32", "fat16"):
346 # parted_fs = parted_disk.get_partition(curminor).geom.file_system_open()
347 # resize_constraint = parted_fs.get_resize_constraint()
348 # if total_sectors < resize_constraint.min_size or start != resize_constraint.start_range.start:
349 # raise GLIException("PartitionError", 'fatal', 'partition', "New size specified for " + devnode + " is not within allowed boundaries (blame parted)")
350 # new_geom = resize_constraint.start_range.duplicate()
351 # new_geom.set_start(start)
352 # new_geom.set_end(end)
353 # try:
354 # parted_fs.resize(new_geom)
355 # except:
356 # raise GLIException("PartitionResizeError", 'fatal', 'partition', "could not resize " + devnode)
357 # self._logger.log(" Deleting old minor " + str(oldpart) + " to be recreated in next pass")
358 # self._delete_partition(parted_disk, oldpart)
359 # parted_disk.delete_all()
360 # parted_disk.commit()
361
362
363 ##
364 # This function clears the partition table
365 def clear_partitions(self, disklabel=None):
366 if not disklabel:
367 disklabel = archinfo[self._arch]
368 self._parted_disk = self._parted_dev.disk_new_fresh(parted.disk_type_get(disklabel))
369 self._disklabel = disklabel
370 self._parted_disk.commit()
371 self.set_partitions_from_disk()
372
373 ##
374 # Returns an ordered list (disk order) of partitions
375 def get_partitions(self):
376 return self._partitions
377
378 ##
379 # Returns the minor of the extended partition, if any
380 def get_extended_partition(self):
381 for part in self._partitions:
382 if part.is_extended():
383 return part['minor']
384 return 0
385
386 ##
387 # Returns the drive model
388 def get_model(self):
389 return self._parted_dev.model
390
391 ##
392 # Returns the disklabel type
393 def get_disklabel(self):
394 return self._disklabel
395
396 ##
397 # Returns all the geometry information
398 def get_geometry(self):
399 return self._geometry
400
401 ##
402 # This class represents a partition within a GLIStorageDevice object
403 class Partition:
404 "Class representing a single partition within a Device object"
405
406 ##
407 # Initialization function for the Partition class
408 # @param device Parent GLIStorageDevice object
409 # @param parted_part parted.Partition object
410 def __init__(self, device, parted_part):
411 self._device = device
412 self._start = parted_part.geom.start
413 self._end = parted_part.geom.end
414 self._type = parted_part.type_name
415 self._minor = parted_part.num
416 self._mb = float((self._end - self._start + 1) * device._geometry['sector_size'] / MEGABYTE)
417 self._part_name = ""
418 self._resizeable = False
419
420 # determine the /dev node that refers to this partition
421 tmpdevice = device.get_device()
422 label_type = device._parted_disk.type.name
423 if label_type == "loop":
424 self._devnode = tmpdevice
425 elif tmpdevice[-1] in "0123456789":
426 self._devnode = tmpdevice + "p" + str(self._minor)
427 else:
428 self._devnode = tmpdevice + str(self._minor)
429
430 if not self._type == "free":
431 if parted_part.fs_type:
432 self._type = parted_part.fs_type.name
433 if self._type == "hfs" and parted_part.is_flag_available(1) and parted_part.get_flag(1):
434 self._type = "apple_bootstrap"
435 else:
436 # Add additional partition identification code here
437 self._type = "unknown"
438 if parted_part.type == 2: self._type = "extended"
439 if device._parted_disk.type.check_feature(parted.DISK_TYPE_PARTITION_NAME):
440 self._part_name = parted_part.get_name()
441 # The 10 is completely arbitrary. If flags seem to be missed, this number should be increased
442 for flag in range(0, 10):
443 if parted_part.is_flag_available(flag) and parted_part.get_flag(flag):
444 self._flags.append(flag)
445
446 if type == "ext2" or type == "ext3":
447 block_size = long(string.strip(commands.getoutput("dumpe2fs -h " + self._devnode + r" 2>&1 | grep -e '^Block size:' | sed -e 's/^Block size:\s\+//'")))
448 free_blocks = long(string.strip(commands.getoutput("dumpe2fs -h " + self._devnode + r" 2>&1 | grep -e '^Free blocks:' | sed -e 's/^Free blocks:\s\+//'")))
449 free_bytes = long(block_size * free_blocks)
450 # can't hurt to pad (the +50) it a bit since this is really just a guess
451 self._min_size = self._mb - long(free_bytes / MEGABYTE) + 50
452 self._resizeable = True
453 elif type == "ntfs":
454 min_bytes = long(commands.getoutput("ntfsresize -f --info " + self._devnode + " | grep -e '^You might resize' | sed -e 's/You might resize at //' -e 's/ bytes or .\+//'"))
455 self._min_size = long(min_bytes / MEGABYTE) + 50
456 self._resizeable = True
457 else:
458 try:
459 parted_fs = parted_part.geom.file_system_open()
460 resize_constraint = parted_fs.get_resize_constraint()
461 min_bytes = resize_constraint.min_size * self._device._geometry['sector_size']
462 self._min_size = long(min_bytes / MEGABYTE) + 1
463 self._resizeable = True
464 except:
465 self._resizeable = False
466
467 def __getitem__(self, name):
468 tmpdict = {
469 'start': self.get_start,
470 'end': self.get_end,
471 'type': self.get_type,
472 'minor': self.get_minor,
473 'mb': self.get_mb,
474 'flags': self.get_flags,
475 'name': self.get_name,
476 'devnode': self.get_devnode,
477 'resizeable': self.get_resizeable,
478 'min_size': self.get_min_size,
479 'max_size': self.get_max_size,
480 'extended': self.is_extended,
481 'logical': self.is_logical
482 }
483 if name in tmpdict:
484 return tmpdict[name]()
485 else:
486 raise ValueError(name + " is not a valid attribute!")
487
488 def __setitem__(self, name, value):
489 tmpdict = {
490 'flags': self.set_flags,
491 'name': self.set_name
492 }
493 if name in tmpdict:
494 tmpdict[name](value)
495 else:
496 raise ValueError(name + " is not a valid attribute!")
497
498 ##
499 # Returns the dev node that this partition will have
500 def get_devnode(self):
501 return self._devnode
502
503 ##
504 # Returns whether or not the partition is extended
505 def is_extended(self):
506 if self._type == "extended":
507 return True
508 else:
509 return False
510
511 ##
512 # Returns whether or not the partition is logical
513 def is_logical(self):
514 if self._type == "free":
515 ext_part = self._device.get_extended_partition()
516 if not ext_part: return False
517 if self._start >= ext_part['start'] and self._start <= ext_part['end'] and self._end >= ext_part['start'] and self._end <= ext_part['end']:
518 return True
519 else:
520 return False
521 elif self._device._labelinfo['extended'] and self._minor > 4:
522 return True
523 else:
524 return False
525
526 ##
527 # Returns a list of logical partitions if this is an extended partition
528 def get_logicals(self):
529 if not self.is_extended():
530 return None
531 logicals = []
532 for part in self._device._partitions:
533 if part.is_logical():
534 logicals.append(part)
535 return logicals
536
537 ##
538 # Returns the start sector for the partition
539 def get_start(self):
540 return long(self._start)
541
542 ##
543 # Returns end sector for the partition
544 def get_end(self):
545 return long(self._end)
546
547 ##
548 # Returns size of partition in MB
549 def get_mb(self):
550 return self._mb
551
552 ##
553 # Returns type of partition
554 def get_type(self):
555 return self._type
556
557 ##
558 # Returns parent Device object
559 def get_device(self):
560 return self._device
561
562 ##
563 # Returns minor of partition
564 def get_minor(self):
565 return self._minor
566
567 ##
568 # Returns whether the partition is resizeable
569 def get_resizeable(self):
570 return self._resizeable
571
572 ##
573 # Sets partition flags
574 def set_flags(self, flags):
575 self._flags = flags
576
577 ##
578 # Returns partition flags
579 def get_flags(self):
580 return self._flags
581
582 ##
583 # Sets partition name
584 def set_name(self, name):
585 self._name = name
586
587 ##
588 # Returns partition name
589 def get_name(self):
590 return self._name
591
592 ##
593 # Returns minimum MB for resize
594 def get_min_size(self):
595 if self._resizeable:
596 return self._min_size
597 else:
598 return -1
599
600 ##
601 # Returns maximum MB for resize
602 def get_max_size(self):
603 if self._resizeable:
604 partidx = self._device.get_partition_position(self._minor)
605 free_part = self._device._partitions[partidx + 1]
606 if free_part['type'] == "free":
607 return self._mb
608 if free_part.is_logical():
609 if self.is_logical():
610 return self._mb + free_part['mb']
611 else:
612 return self._mb
613 else:
614 return self._mb + free_part['mb']
615 else:
616 return -1
617
618 ##
619 # Resizes the partition
620 # @param mb New size in MB
621 def resize(self, mb):
622 minor_pos = self._device.get_partition_position(self._minor)
623 try:
624 free_minor = self._device._partitions[minor_pos+1].get_minor()
625 except:
626 free_minor = 0
627 if mb < self._mb:
628 # Shrinking
629 if not free_minor or not self._device.get_partition(free_minor).get_type() == "free":
630 if self._device._disklabel == "mac":
631 free_minor = self._minor + 1
632 elif self.is_logical():
633 free_minor = self._minor + FREE_MINOR_FRAC_LOG
634 else:
635 free_minor = self._minor + FREE_MINOR_FRAC_PRI
636 if self._device.get_partition(free_minor):
637 for i, part in enumerate(self._device._partitions):
638 if i <= minor_pos or free_minor > part.get_minor(): continue
639 part.set_minor(part.get_minor() + 1)
640 self._device._partitions.insert(minor_pos+1, Partition(self._device, free_minor, self._mb - mb, 0, 0, "free", format=False, existing=False))
641 else:
642 self._device.get_partition(free_minor).set_mb(self._device.get_partition(free_minor).get_mb() + (self._mb - mb))
643 self._mb = mb
644 else:
645 if mb == self._mb + self._device.get_partition(free_minor).get_mb():
646 # Using all available unallocated space
647 self._device._partitions.pop(self._device.get_partition_position(free_minor))
648 self._mb = mb
649 else:
650 # Growing
651 self._device.get_partition(free_minor).set_mb(self._device.get_partition(free_minor).get_mb() - (mb - self._mb))
652 self._mb = mb
653 self._resized = True
654 self._device.tidy_partitions()
655
656 ##
657 # Returns a list of detected partitionable devices
658 def detect_devices():
659 devices = []
660
661 # Make sure sysfs exists
662 # TODO: rewrite for 2.4 support
663 if not os.path.exists("/sys/bus"):
664 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "no sysfs found (you MUST use a kernel >2.6)")
665 # Make sure /proc/partitions exists
666 if not os.path.exists("/proc/partitions"):
667 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "/proc/partitions does not exist! Please make sure procfs is in your kernel and mounted!")
668
669 # Load /proc/partitions into the variable 'partitions'
670 partitions = []
671 for line in open("/proc/partitions"):
672 tmpparts = line.split()
673 if len(tmpparts) < 4 or not tmpparts[0].isdigit() or not tmpparts[1].isdigit():
674 continue
675
676 # Get the major, minor and device name
677 major = int(tmpparts[0])
678 minor = int(tmpparts[1])
679 device = "/dev/" + tmpparts[3]
680
681 # If there is no /dev/'device_name', then scan
682 # all the devices in /dev to try and find a
683 # devices with the same major and minor
684 if not os.path.exists(device):
685 device = None
686 for path, dirs, files in os.walk("/dev"):
687 for d_file in files:
688 full_file = os.path.join(path, d_file)
689 if not os.path.exists(full_file):
690 continue
691 statres = os.stat(full_file)
692 fmaj = os.major(statres.st_rdev)
693 fmin = os.minor(statres.st_rdev)
694 if fmaj == major and fmin == minor:
695 device = full_file
696 break
697 if not device:
698 continue
699
700 partitions.append(( major, minor, device ))
701
702 # Scan sysfs for the devices of type 'x'
703 # 'x' being a member of the list below:
704 # Compaq cards.../sys/block/{cciss,ida}!cXdX/dev
705 for dev_glob in ("/sys/bus/ide/devices/*/block*/dev", "/sys/bus/scsi/devices/*/block*/dev", "/sys/block/cciss*/dev", "/sys/block/ida*/dev"):
706 sysfs_devices = glob(dev_glob)
707 if not sysfs_devices: continue
708 for sysfs_device in sysfs_devices:
709 # Get the major and minor info
710 try:
711 major, minor = open(sysfs_device).read().split(":")
712 major = int(major)
713 minor = int(minor)
714 except:
715 raise GLIException("GLIStorageDeviceError", 'fatal', 'detect_devices', "invalid major/minor in " + sysfs_device)
716
717 # Find a device listed in /proc/partitions
718 # that has the same minor and major as our
719 # current block device.
720 for record in partitions:
721 if major == record[0] and minor == record[1]:
722 devices.append(record[2])
723
724 # For testing the partitioning code
725 if GLIUtility.is_file("/tmp/disk.img"):
726 devices.append("/tmp/disk.img")
727
728 # We have assembled the list of devices, so return it
729 return devices
730
731 def list_partitions():
732 # Load /proc/partitions into the variable 'partitions'
733 # return [("/dev/" + x[3]) for x in [y.strip().split() for y in open("/proc/partitions", "r").readlines()] if len(x) >= 4 and x[3][-1].isdigit()]
734 partitions = {}
735 for line in open("/proc/partitions"):
736 tmpparts = line.split()
737 if len(tmpparts) < 4 or not tmpparts[0].isdigit() or not tmpparts[1].isdigit():
738 continue
739 device = "/dev/" + tmpparts[3]
740 if device == "/dev/loop0": #we don't want this.
741 continue
742 if not device[-1].isdigit(): #A Drive
743 continue
744 else:
745 partitions[device] = tmpparts[2]
746 return partitions

Properties

Name Value
svn:eol-style native

  ViewVC Help
Powered by ViewVC 1.1.20