]>
git.ipfire.org Git - ipfire-3.x.git/blob - pkgs/core/pomona/src/storage/partitioning.py
6 from constants
import *
10 _
= lambda x
: gettext
.ldgettext("pomona", x
)
12 def doAutoPartition(installer
):
13 if installer
.dispatch
.dir == DISPATCH_BACK
:
14 installer
.ds
.storage
.reset()
20 if installer
.ds
.storage
.doAutoPart
:
21 clearPartitions(installer
)
22 (disks
, devs
) = _createFreeSpacePartitions(installer
)
25 installer
.intf
.messageWindow(_("Error Partitioning"),
26 _("Could not find enough free space "
27 "for automatic partitioning, please "
28 "use another partitioning method."))
31 _schedulePartitions(installer
, disks
)
33 # run the autopart function to allocate and grow partitions
35 doPartitioning(installer
)
37 if installer
.ds
.storage
.doAutoPart
:
38 _scheduleLVs(installer
, devs
)
42 except PartitioningWarning
as msg
:
43 installer
.intf
.messageWindow(_("Warnings During Automatic Partitioning"),
44 _("Following warnings occurred during automatic "
45 "partitioning:\n\n%s") % (msg
,),)
47 except PartitioningError
as msg
:
48 # restore drives to original state
49 installer
.ds
.storage
.reset()
50 #installer.dispatch.skipStep("partition", skip = 0)
51 installer
.intf
.messageWindow(_("Error Partitioning"),
52 _("Could not allocate requested partitions: \n\n"
53 "%s.%s") % (msg
, extra
))
56 # now do a full check of the requests
57 (errors
, warnings
) = installer
.ds
.storage
.sanityCheck()
59 for warning
in warnings
:
60 installer
.log
.warning(warning
)
62 errortxt
= "\n".join(errors
)
63 installer
.intf
.messageWindow(_("Automatic Partitioning Errors"),
64 _("The following errors occurred with your "
65 "partitioning:\n\n%s\n\n"
66 "This can happen if there is not enough "
67 "space on your hard drive(s) for the "
69 "Press 'OK' to choose a different partitioning option.")
72 installer
.ds
.storage
.reset()
73 #XXX return DISPATCH_BACK
76 def doPartitioning(installer
):
77 """ Allocate and grow partitions.
79 When this function returns without error, all PartitionDevice
80 instances must have their parents set to the disk they are
81 allocated on, and their partedPartition attribute set to the
82 appropriate parted.Partition instance from their containing
83 disk. All req_xxxx attributes must be unchanged.
87 storage - Main anaconda Storage instance
91 exclusiveDisks -- list of names of disks to use
94 storage
= installer
.ds
.storage
97 exclusiveDisks
= storage
.clearDisks
99 disks
= [d
for d
in disks
if d
.name
in exclusiveDisks
]
104 partitions
= storage
.partitions
105 for part
in partitions
:
106 part
.req_bootable
= False
108 # start over with flexible-size requests
109 part
.req_size
= part
.req_base_size
111 # FIXME: isn't there a better place for this to happen?
113 # bootDev = anaconda.platform.bootDevice()
118 # bootDev.req_bootable = True
120 # FIXME: make sure non-existent partitions have empty parents list
121 allocatePartitions(installer
, disks
, partitions
)
122 growPartitions(installer
, disks
, partitions
)
123 # The number and thus the name of partitions may have changed now,
124 # allocatePartitions() takes care of this for new partitions, but not
125 # for pre-existing ones, so we update the name of all partitions here
126 for part
in partitions
:
129 # XXX hack -- if we created any extended partitions we need to add
130 # them to the tree now
132 extended
= disk
.partedDisk
.getExtendedPartition()
136 extendedName
= devicePathToName(extended
.getDeviceNodeName())
137 device
= storage
.devicetree
.getDeviceByName(extendedName
)
139 if not device
.exists
:
140 # created by us, update partedPartition
141 device
.partedPartition
= extended
144 # This is a little odd because normally instantiating a partition
145 # that does not exist means leaving self.parents empty and instead
146 # populating self.req_disks. In this case, we need to skip past
147 # that since this partition is already defined.
148 device
= PartitionDevice(extendedName
, parents
=disk
)
149 device
.parents
= [disk
]
150 device
.partedPartition
= extended
151 storage
.createDevice(device
)
153 def clearPartitions(installer
):
154 """ Clear partitions and dependent devices from disks.
158 storage -- a storage.Storage instance
166 - Needs some error handling, especially for the parted bits.
169 storage
= installer
.ds
.storage
171 # we are only interested in partitions that physically exist
172 partitions
= [p
for p
in storage
.partitions
if p
.exists
]
173 disks
= [] # a list of disks from which we've removed partitions
174 clearparts
= [] # list of partitions we'll remove
175 for part
in partitions
:
176 # if we got a list of disks to clear, make sure this one's on it
177 if storage
.clearDisks
and part
.disk
.name
not in storage
.clearDisks
:
180 # don't clear partitions holding install media
181 #if part.name in storage.protectedPartitions:
184 # we don't want to fool with extended partitions, freespace
185 if part
.partType
not in (parted
.PARTITION_NORMAL
, parted
.PARTITION_LOGICAL
):
188 # XXX is there any argument for not removing incomplete devices?
189 # -- maybe some RAID devices
190 devices
= storage
.deviceDeps(part
)
192 installer
.log
.debug("Devices to remove: %s" % ([d
.name
for d
in devices
],))
193 leaves
= [d
for d
in devices
if d
.isleaf
]
194 installer
.log
.debug("Leaves to remove: %s" % ([d
.name
for d
in leaves
],))
196 storage
.destroyDevice(leaf
)
199 #installer.log.debug("Partitions left: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions])
200 disk_name
= os
.path
.basename(part
.partedPartition
.disk
.device
.path
)
201 if disk_name
not in disks
:
202 disks
.append(disk_name
)
204 clearparts
.append(part
)
206 for part
in clearparts
:
207 storage
.destroyDevice(part
)
209 # now remove any empty extended partitions
210 removeEmptyExtendedPartitions(installer
)
212 def removeEmptyExtendedPartitions(installer
):
213 storage
= installer
.ds
.storage
214 for disk
in storage
.disks
:
215 #installer.log.debug("Checking whether disk %s has an empty extended" % disk.name)
216 extended
= disk
.partedDisk
.getExtendedPartition()
217 logical_parts
= disk
.partedDisk
.getLogicalPartitions()
218 #installer.log.debug("Extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts]))
219 if extended
and not logical_parts
:
220 installer
.log
.debug("Removing empty extended partition from %s" % disk
.name
)
221 extended_name
= devicePathToName(extended
.getDeviceNodeName())
222 extended
= storage
.devicetree
.getDeviceByName(extended_name
)
223 storage
.destroyDevice(extended
)
224 #disk.partedDisk.removePartition(extended.partedPartition)
226 def _createFreeSpacePartitions(installer
):
227 # get a list of disks that have at least one free space region of at
230 for disk
in installer
.ds
.storage
.disks
:
231 if disk
.name
not in installer
.ds
.storage
.clearDisks
:
234 partedDisk
= disk
.partedDisk
235 part
= disk
.partedDisk
.getFirstPartition()
237 if not part
.type & parted
.PARTITION_FREESPACE
:
238 part
= part
.nextPartition()
241 if part
.getSize(unit
="MB") > 100:
245 part
= part
.nextPartition()
247 # create a separate pv partition for each disk with free space
250 if installer
.ds
.storage
.encryptedAutoPart
:
254 part
= installer
.ds
.storage
.newPartition(fmt_type
=fmt_type
,
257 installer
.ds
.storage
.createDevice(part
)
262 def _schedulePartitions(installer
, disks
):
264 # Convert storage.autoPartitionRequests into Device instances and
265 # schedule them for creation
267 # First pass is for partitions only. We'll do LVs later.
269 for request
in installer
.ds
.storage
.autoPartitionRequests
:
273 if request
.fstype
is None:
274 request
.fstype
= installer
.ds
.storage
.defaultFSType
276 dev
= installer
.ds
.storage
.newPartition(fmt_type
=request
.fstype
,
279 maxsize
=request
.maxSize
,
280 mountpoint
=request
.mountpoint
,
282 weight
=request
.weight
)
284 # schedule the device for creation
285 installer
.ds
.storage
.createDevice(dev
)
287 # make sure preexisting broken lvm/raid configs get out of the way
290 def allocatePartitions(installer
, disks
, partitions
):
291 """ Allocate partitions based on requested features.
293 Non-existing partitions are sorted according to their requested
294 attributes, and then allocated.
296 The basic approach to sorting is that the more specifically-
297 defined a request is, the earlier it will be allocated. See
298 the function partitionCompare for details on the sorting
301 The PartitionDevice instances will have their name and parents
302 attributes set once they have been allocated.
304 #installer.log.debug("disks=%s ; partitions=%s" % (disks, partitions))
305 new_partitions
= [p
for p
in partitions
if not p
.exists
]
306 new_partitions
.sort(cmp=partitionCompare
)
308 # XXX is this needed anymore?
311 if disk
.path
not in partedDisks
.keys():
312 partedDisks
[disk
.path
] = disk
.partedDisk
#.duplicate()
314 # remove all newly added partitions from the disk
315 installer
.log
.debug("Removing all non-preexisting from disk(s)")
316 for _part
in new_partitions
:
317 if _part
.partedPartition
:
319 continue # these get removed last
320 #_part.disk.partedDisk.removePartition(_part.partedPartition)
321 partedDisk
= partedDisks
[_part
.disk
.partedDisk
.device
.path
]
322 installer
.log
.debug("Removing part %s (%s) from disk %s (%s)" %
323 (_part
.partedPartition
.path
,
324 [p
.path
for p
in _part
.partedPartition
.disk
.partitions
],
325 partedDisk
.device
.path
,
326 [p
.path
for p
in partedDisk
.partitions
]))
328 partedDisk
.removePartition(_part
.partedPartition
)
329 _part
.partedPartition
= None
332 # remove empty extended so it doesn't interfere
333 extended
= partedDisk
.getExtendedPartition()
334 if extended
and not partedDisk
.getLogicalPartitions():
335 installer
.log
.debug("Removing empty extended partition")
336 #partedDisk.minimizeExtendedPartition()
337 partedDisk
.removePartition(extended
)
339 for _part
in new_partitions
:
340 if _part
.partedPartition
and _part
.isExtended
:
341 # ignore new extendeds as they are implicit requests
344 # obtain the set of candidate disks
347 # we have a already selected a disk for this request
348 req_disks
= [_part
.disk
]
349 elif _part
.req_disks
:
350 # use the requested disk set
351 req_disks
= _part
.req_disks
353 # no disks specified means any disk will do
356 #installer.log.debug("allocating partition: %s ; disks: %s ; boot: %s ; "
357 # "primary: %s ; size: %dMB ; grow: %s ; max_size: %s" %
358 # (_part.name, req_disks, _part.req_bootable, _part.req_primary,
359 # _part.req_size, _part.req_grow, _part.req_max_size))
364 for _disk
in req_disks
:
365 disk
= partedDisks
[_disk
.path
]
366 #for p in disk.partitions:
367 # installer.log.debug("disk %s: part %s" % (disk.device.path, p.path))
368 sectorSize
= disk
.device
.physicalSectorSize
371 #installer.log.debug("Checking freespace on %s" % _disk.name)
373 new_part_type
= getNextPartitionType(disk
)
374 if new_part_type
is None:
375 # can't allocate any more partitions on this disk
376 installer
.log
.debug("No free partition slots on %s" % _disk
.name
)
379 if _part
.req_primary
and new_part_type
!= parted
.PARTITION_NORMAL
:
380 # we need a primary slot and none are free on this disk
381 installer
.log
.debug("No primary slots available on %s" % _disk
.name
)
384 best
= getBestFreeSpaceRegion(installer
, disk
,
388 boot
=_part
.req_bootable
)
390 if best
== free
and not _part
.req_primary
and \
391 new_part_type
== parted
.PARTITION_NORMAL
:
392 # see if we can do better with a logical partition
393 installer
.log
.debug("Not enough free space for primary -- trying logical")
394 new_part_type
= getNextPartitionType(disk
, no_primary
=True)
396 best
= getBestFreeSpaceRegion(disk
,
400 boot
=_part
.req_bootable
)
402 if best
and free
!= best
:
403 # now we know we are choosing a new free space,
404 # so update the disk and part type
405 #installer.log.debug("Updating use_disk to %s (%s), type: %s"
406 # % (_disk, _disk.name, new_part_type))
407 part_type
= new_part_type
409 installer
.log
.debug("New free: %s (%d-%d / %dMB)" % (best
.device
.path
,
415 if free
and _part
.req_bootable
:
416 # if this is a bootable partition we want to
417 # use the first freespace region large enough
418 # to satisfy the request
419 installer
.log
.debug("Found free space for bootable request")
423 raise PartitioningError("Not enough free space on disks")
426 disk
= _disk
.partedDisk
428 # create the extended partition if needed
429 # TODO: move to a function (disk, free)
430 if part_type
== parted
.PARTITION_EXTENDED
:
431 installer
.log
.debug("Creating extended partition")
432 geometry
= parted
.Geometry(device
=disk
.device
,
436 extended
= parted
.Partition(disk
=disk
,
437 type=parted
.PARTITION_EXTENDED
,
439 constraint
= parted
.Constraint(device
=disk
.device
)
440 # FIXME: we should add this to the tree as well
441 disk
.addPartition(extended
, constraint
)
443 # end proposed function
445 # now the extended partition exists, so set type to logical
446 part_type
= parted
.PARTITION_LOGICAL
448 # recalculate freespace
449 installer
.log
.debug("Recalculating free space")
450 free
= getBestFreeSpaceRegion(disk
,
453 boot
=_part
.req_bootable
)
455 raise PartitioningError("Not enough free space after "
456 "creating extended partition")
458 # create minimum geometry for this request
460 sectors_per_track
= disk
.device
.biosGeometry
[2]
461 length
= (_part
.req_size
* (1024 * 1024)) / sectorSize
462 new_geom
= parted
.Geometry(device
=disk
.device
,
463 start
=max(sectors_per_track
, free
.start
),
466 # create maximum and minimum geometries for constraint
467 start
= max(0 , free
.start
- 1)
468 max_geom
= parted
.Geometry(device
=disk
.device
,
470 length
=min(length
+ 1, disk
.device
.length
- start
))
471 min_geom
= parted
.Geometry(device
=disk
.device
,
472 start
=free
.start
+ 1,
476 # create the partition and add it to the disk
477 partition
= parted
.Partition(disk
=disk
,
480 constraint
= parted
.Constraint(maxGeom
=max_geom
, minGeom
=min_geom
)
481 disk
.addPartition(partition
=partition
,
482 constraint
=constraint
)
483 installer
.log
.debug("Created partition %s of %dMB and added it to %s" %
484 (partition
.getDeviceNodeName(), partition
.getSize(), disk
.device
.path
))
486 # this one sets the name
487 _part
.partedPartition
= partition
490 # parted modifies the partition in the process of adding it to
491 # the disk, so we need to grab the latest version...
492 _part
.partedPartition
= disk
.getPartitionByPath(_part
.path
)
494 def growPartitions(installer
, disks
, partitions
):
495 """ Grow all growable partition requests.
497 All requests should know what disk they will be on by the time
498 this function is called. This is reflected in the
499 PartitionDevice's disk attribute. Note that the req_disks
500 attribute remains unchanged.
502 The total available free space is summed up for each disk and
503 partition requests are allocated a maximum percentage of the
504 available free space on their disk based on their own base size.
506 Each attempted size means calling allocatePartitions again with
507 one request's size having changed.
509 After taking into account several factors that may limit the
510 maximum size of a requested partition, we arrive at a firm
511 maximum number of sectors by which a request can potentially grow.
513 An initial attempt is made to allocate the full maximum size. If
514 this fails, we begin a rough binary search with a maximum of three
515 iterations to settle on a new size.
519 disks -- a list of all usable disks (DiskDevice instances)
520 partitions -- a list of all partitions (PartitionDevice
523 #installer.log.debug("growPartitions: disks=%s, partitions=%s" %
524 # ([d.name for d in disks], [p.name for p in partitions]))
525 all_growable
= [p
for p
in partitions
if p
.req_grow
]
529 # sort requests by base size in decreasing order
530 all_growable
.sort(key
=lambda p
: p
.req_size
, reverse
=True)
532 installer
.log
.debug("Growable requests are %s" % [p
.name
for p
in all_growable
])
535 installer
.log
.debug("Growing requests on %s" % disk
.name
)
536 for p
in disk
.partedDisk
.partitions
:
537 installer
.log
.debug(" %s: %s (%dMB)" % (disk
.name
, p
.getDeviceNodeName(),
539 sectorSize
= disk
.partedDisk
.device
.physicalSectorSize
540 # get a list of free space regions on the disk
541 free
= disk
.partedDisk
.getFreeSpaceRegions()
543 installer
.log
.debug("No free space on %s" % disk
.name
)
546 # sort the free regions in decreasing order of size
547 free
.sort(key
=lambda r
: r
.length
, reverse
=True)
548 disk_free
= reduce(lambda x
,y
: x
+ y
, [f
.length
for f
in free
])
549 installer
.log
.debug("Total free: %d sectors ; largest: %d sectors (%dMB)"
550 % (disk_free
, free
[0].length
, free
[0].getSize()))
552 # make a list of partitions currently allocated on this disk
553 # -- they're already sorted
556 for part
in all_growable
:
557 #log.debug("checking if part %s (%s) is on this disk" % (part.name,
559 if part
.disk
== disk
:
560 growable
.append(part
)
561 disk_total
+= part
.partedPartition
.geometry
.length
562 installer
.log
.debug("Add %s (%dMB/%d sectors) to growable total"
563 % (part
.name
, part
.partedPartition
.getSize(),
564 part
.partedPartition
.geometry
.length
))
565 installer
.log
.debug("Growable total is now %d sectors" % disk_total
)
567 # now we loop through the partitions...
568 # this first loop is to identify obvious chunks of free space that
569 # will be left over due to max size
573 for part
in growable
:
574 # calculate max number of sectors this request can grow
575 req_sectors
= part
.partedPartition
.geometry
.length
576 share
= float(req_sectors
) / float(disk_total
)
577 max_grow
= (share
* disk_free
)
578 max_sectors
= req_sectors
+ max_grow
579 limited
[part
.name
] = False
581 if part
.req_max_size
:
582 req_max_sect
= (part
.req_max_size
* (1024 * 1024)) / sectorSize
583 if req_max_sect
< max_sectors
:
584 mb
= ((max_sectors
- req_max_sect
) * sectorSize
) / (1024*1024)
586 installer
.log
.debug("Adding %dMB to leftovers from %s"
588 leftover
+= (max_sectors
- req_max_sect
)
589 limited
[part
.name
] = True
591 if not limited
[part
.name
]:
592 unlimited_total
+= req_sectors
594 # now we loop through the partitions...
595 for part
in growable
:
596 # calculate max number of sectors this request can grow
597 req_sectors
= part
.partedPartition
.geometry
.length
598 share
= float(req_sectors
) / float(disk_total
)
599 max_grow
= (share
* disk_free
)
600 if not limited
[part
.name
]:
601 leftover_share
= float(req_sectors
) / float(unlimited_total
)
602 max_grow
+= leftover_share
* leftover
603 max_sectors
= req_sectors
+ max_grow
604 max_mb
= (max_sectors
* sectorSize
) / (1024 * 1024)
606 installer
.log
.debug("%s: base_size=%dMB, max_size=%sMB" %
607 (part
.name
, part
.req_base_size
, part
.req_max_size
))
608 installer
.log
.debug("%s: current_size=%dMB (%d sectors)" %
609 (part
.name
, part
.partedPartition
.getSize(),
610 part
.partedPartition
.geometry
.length
))
611 installer
.log
.debug("%s: %dMB (%d sectors, or %d%% of %d)" %
612 (part
.name
, max_mb
, max_sectors
, share
* 100, disk_free
))
614 installer
.log
.debug("Checking constraints on max size...")
615 # don't grow beyond the request's maximum size
616 if part
.req_max_size
:
617 installer
.log
.debug("max_size: %dMB" % part
.req_max_size
)
618 # FIXME: round down to nearest cylinder boundary
619 req_max_sect
= (part
.req_max_size
* (1024 * 1024)) / sectorSize
620 if req_max_sect
< max_sectors
:
621 max_grow
-= (max_sectors
- req_max_sect
)
622 max_sectors
= req_sectors
+ max_grow
624 # don't grow beyond the resident filesystem's max size
625 if part
.format
.maxSize
> 0:
626 installer
.log
.debug("Format maxsize: %dMB" % part
.format
.maxSize
)
627 # FIXME: round down to nearest cylinder boundary
628 fs_max_sect
= (part
.format
.maxSize
* (1024 * 1024)) / sectorSize
629 if fs_max_sect
< max_sectors
:
630 max_grow
-= (max_sectors
- fs_max_sect
)
631 max_sectors
= req_sectors
+ max_grow
633 # we can only grow as much as the largest free region on the disk
634 if free
[0].length
< max_grow
:
635 installer
.log
.debug("Largest free region: %d sectors (%dMB)" %
636 (free
[0].length
, free
[0].getSize()))
637 # FIXME: round down to nearest cylinder boundary
638 max_grow
= free
[0].length
639 max_sectors
= req_sectors
+ max_grow
641 # Now, we try to grow this partition as close to max_grow
644 # We could call allocatePartitions after modifying this
645 # request and saving the original value of part.req_size,
646 # or we could try to use disk.maximizePartition().
647 max_size
= (max_sectors
* sectorSize
) / (1024 * 1024)
648 orig_size
= part
.req_size
649 # try the max size to begin with
650 installer
.log
.debug("Attempting to allocate maximum size: %dMB" % max_size
)
651 part
.req_size
= max_size
653 allocatePartitions(installer
, disks
, partitions
)
654 except PartitioningError
, e
:
655 installer
.log
.debug("Max size attempt failed: %s (%dMB)" % (part
.name
,
657 part
.req_size
= orig_size
661 installer
.log
.debug("Starting binary search: size=%d max_size=%d" % (part
.req_size
, max_size
))
665 last_good_size
= part
.req_size
668 last_size
= part
.req_size
670 req_sectors
= op_func(req_sectors
, increment
)
671 part
.req_size
= (req_sectors
* sectorSize
) / (1024 * 1024)
672 installer
.log
.debug("Attempting size=%dMB" % part
.req_size
)
675 allocatePartitions(disks
, partitions
)
676 except PartitioningError
, e
:
677 installer
.log
.debug("Attempt at %dMB failed" % part
.req_size
)
682 last_good_size
= part
.req_size
686 part
.req_size
= last_good_size
687 installer
.log
.debug("Backing up to size=%dMB" % part
.req_size
)
689 allocatePartitions(disks
, partitions
)
690 except PartitioningError
, e
:
691 raise PartitioningError("Failed to grow partitions")
693 # reset all requests to their original requested size
694 for part
in partitions
:
697 part
.req_size
= part
.req_base_size
699 def growLVM(installer
):
700 """ Grow LVs according to the sizes of the PVs. """
701 storage
= installer
.ds
.storage
702 for vg
in storage
.vgs
:
703 total_free
= vg
.freeSpace
705 installer
.log
.debug("vg %s has no free space" % vg
.name
)
708 installer
.log
.debug("vg %s: %dMB free ; lvs: %s" % (vg
.name
, vg
.freeSpace
,
709 [l
.lvname
for l
in vg
.lvs
]))
711 # figure out how much to grow each LV
713 lv_total
= vg
.size
- total_free
714 installer
.log
.debug("used: %dMB ; vg.size: %dMB" % (lv_total
, vg
.size
))
716 # This first loop is to calculate percentage-based growth
717 # amounts. These are based on total free space.
719 lvs
.sort(cmp=lvCompare
)
721 if not lv
.req_grow
or not lv
.req_percent
:
724 portion
= (lv
.req_percent
* 0.01)
725 grow
= portion
* vg
.vgFree
726 new_size
= lv
.req_size
+ grow
727 if lv
.req_max_size
and new_size
> lv
.req_max_size
:
728 grow
-= (new_size
- lv
.req_max_size
)
730 if lv
.format
.maxSize
and lv
.format
.maxSize
< new_size
:
731 grow
-= (new_size
- lv
.format
.maxSize
)
733 # clamp growth amount to a multiple of vg extent size
734 grow_amounts
[lv
.name
] = vg
.align(grow
)
738 # This second loop is to calculate non-percentage-based growth
739 # amounts. These are based on free space remaining after
740 # calculating percentage-based growth amounts.
742 # keep a tab on space not allocated due to format or requested
743 # maximums -- we'll dole it out to subsequent requests
746 installer
.log
.debug("Checking lv %s: req_grow: %s ; req_percent: %s"
747 % (lv
.name
, lv
.req_grow
, lv
.req_percent
))
748 if not lv
.req_grow
or lv
.req_percent
:
751 portion
= float(lv
.req_size
) / float(lv_total
)
752 grow
= portion
* total_free
753 installer
.log
.debug("grow is %dMB" % grow
)
755 todo
= lvs
[lvs
.index(lv
):]
756 unallocated
= reduce(lambda x
,y
: x
+y
,
757 [l
.req_size
for l
in todo
758 if l
.req_grow
and not l
.req_percent
])
759 extra_portion
= float(lv
.req_size
) / float(unallocated
)
760 extra
= extra_portion
* leftover
761 installer
.log
.debug("%s getting %dMB (%d%%) of %dMB leftover space"
762 % (lv
.name
, extra
, extra_portion
* 100, leftover
))
765 installer
.log
.debug("grow is now %dMB" % grow
)
766 max_size
= lv
.req_size
+ grow
767 if lv
.req_max_size
and max_size
> lv
.req_max_size
:
768 max_size
= lv
.req_max_size
770 if lv
.format
.maxSize
and max_size
> lv
.format
.maxSize
:
771 max_size
= lv
.format
.maxSize
773 installer
.log
.debug("max size is %dMB" % max_size
)
775 leftover
+= (lv
.req_size
+ grow
) - max_size
776 grow
= max_size
- lv
.req_size
777 installer
.log
.debug("lv %s gets %dMB" % (lv
.name
, vg
.align(grow
)))
778 grow_amounts
[lv
.name
] = vg
.align(grow
)
781 installer
.log
.debug("No growable lvs in vg %s" % vg
.name
)
784 # now grow the lvs by the amounts we've calculated above
786 if lv
.name
not in grow_amounts
.keys():
788 lv
.size
+= grow_amounts
[lv
.name
]
790 # now there shouldn't be any free space left, but if there is we
791 # should allocate it to one of the LVs
792 vg_free
= vg
.freeSpace
793 installer
.log
.debug("vg %s has %dMB free" % (vg
.name
, vg_free
))
799 if lv
.req_max_size
and lv
.size
== lv
.req_max_size
:
802 if lv
.format
.maxSize
and lv
.size
== lv
.format
.maxSize
:
805 # first come, first served
806 projected
= lv
.size
+ vg
.freeSpace
807 if lv
.req_max_size
and projected
> lv
.req_max_size
:
808 projected
= lv
.req_max_size
810 if lv
.format
.maxSize
and projected
> lv
.format
.maxSize
:
811 projected
= lv
.format
.maxSize
813 installer
.log
.debug("Giving leftover %dMB to %s" % (projected
- lv
.size
,
817 def partitionCompare(part1
, part2
):
818 """ More specifically defined partitions come first.
826 if part1
.req_base_weight
:
827 ret
-= part1
.req_base_weight
829 if part2
.req_base_weight
:
830 ret
+= part2
.req_base_weight
832 # bootable partitions to the front
833 ret
-= cmp(part1
.req_bootable
, part2
.req_bootable
) * 1000
835 # more specific disk specs to the front of the list
836 ret
+= cmp(len(part1
.parents
), len(part2
.parents
)) * 500
838 # primary-only to the front of the list
839 ret
-= cmp(part1
.req_primary
, part2
.req_primary
) * 200
841 # larger requests go to the front of the list
842 ret
-= cmp(part1
.size
, part2
.size
) * 100
844 # fixed size requests to the front
845 ret
+= cmp(part1
.req_grow
, part2
.req_grow
) * 50
847 # potentially larger growable requests go to the front
848 if part1
.req_grow
and part2
.req_grow
:
849 if not part1
.req_max_size
and part2
.req_max_size
:
851 elif part1
.req_max_size
and not part2
.req_max_size
:
854 ret
-= cmp(part1
.req_max_size
, part2
.req_max_size
) * 25
863 def lvCompare(lv1
, lv2
):
864 """ More specifically defined lvs come first.
872 # larger requests go to the front of the list
873 ret
-= cmp(lv1
.size
, lv2
.size
) * 100
875 # fixed size requests to the front
876 ret
+= cmp(lv1
.req_grow
, lv2
.req_grow
) * 50
878 # potentially larger growable requests go to the front
879 if lv1
.req_grow
and lv2
.req_grow
:
880 if not lv1
.req_max_size
and lv2
.req_max_size
:
882 elif lv1
.req_max_size
and not lv2
.req_max_size
:
885 ret
-= cmp(lv1
.req_max_size
, lv2
.req_max_size
) * 25
894 def getNextPartitionType(disk
, no_primary
=None):
895 """ Find the type of partition to create next on a disk.
897 Return a parted partition type value representing the type of the
898 next partition we will create on this disk.
900 If there is only one free primary partition and we can create an
901 extended partition, we do that.
903 If there are free primary slots and an extended partition we will
904 recommend creating a primary partition. This can be overridden
905 with the keyword argument no_primary.
909 disk -- a parted.Disk instance representing the disk
913 no_primary -- given a choice between primary and logical
914 partitions, prefer logical
918 extended
= disk
.getExtendedPartition()
919 supports_extended
= disk
.supportsFeature(parted
.DISK_TYPE_EXTENDED
)
920 logical_count
= len(disk
.getLogicalPartitions())
921 max_logicals
= disk
.getMaxLogicalPartitions()
922 primary_count
= disk
.primaryPartitionCount
924 if primary_count
== disk
.maxPrimaryPartitionCount
and \
925 extended
and logical_count
< max_logicals
:
926 part_type
= parted
.PARTITION_LOGICAL
927 elif primary_count
== (disk
.maxPrimaryPartitionCount
- 1) and \
928 not extended
and supports_extended
:
929 # last chance to create an extended partition
930 part_type
= parted
.PARTITION_EXTENDED
931 elif no_primary
and extended
and logical_count
< max_logicals
:
932 # create a logical even though we could presumably create a
934 part_type
= parted
.PARTITION_LOGICAL
936 # XXX there is a possiblity that the only remaining free space on
937 # the disk lies within the extended partition, but we will
938 # try to create a primary first
939 part_type
= parted
.PARTITION_NORMAL
943 def getBestFreeSpaceRegion(installer
, disk
, part_type
, req_size
,
944 boot
=None, best_free
=None):
945 """ Return the "best" free region on the specified disk.
947 For non-boot partitions, we return the largest free region on the
948 disk. For boot partitions, we return the first region that is
949 large enough to hold the partition.
951 Partition type (parted's PARTITION_NORMAL, PARTITION_LOGICAL) is
952 taken into account when locating a suitable free region.
954 For locating the best region from among several disks, the keyword
955 argument best_free allows the specification of a current "best"
956 free region with which to compare the best from this disk. The
957 overall best region is returned.
961 disk -- the disk (a parted.Disk instance)
962 part_type -- the type of partition we want to allocate
963 (one of parted's partition type constants)
964 req_size -- the requested size of the partition (in MB)
968 boot -- indicates whether this will be a bootable partition
970 best_free -- current best free region for this partition
973 #installer.log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB boot=%s best=%s" %
974 # (disk.device.path, part_type, req_size, boot, best_free))
975 extended
= disk
.getExtendedPartition()
976 for _range
in disk
.getFreeSpaceRegions():
978 # find out if there is any overlap between this region and the
980 installer
.log
.debug("Looking for intersection between extended (%d-%d) and free (%d-%d)" %
981 (extended
.geometry
.start
, extended
.geometry
.end
, _range
.start
, _range
.end
))
983 # parted.Geometry.overlapsWith can handle this
985 free_geom
= extended
.geometry
.intersect(_range
)
986 except ArithmeticError, e
:
987 # this freespace region does not lie within the extended
988 # partition's geometry
991 if (free_geom
and part_type
== parted
.PARTITION_NORMAL
) or \
992 (not free_geom
and part_type
== parted
.PARTITION_LOGICAL
):
993 installer
.log
.debug("Free region not suitable for request")
996 if part_type
== parted
.PARTITION_NORMAL
:
997 # we're allocating a primary and the region is not within
998 # the extended, so we use the original region
1003 installer
.log
.debug("Current free range on %s is %d-%d (%dMB)" % (disk
.device
.path
,
1006 free_geom
.getSize()))
1007 free_size
= free_geom
.getSize()
1009 if req_size
<= free_size
:
1010 if not best_free
or free_geom
.length
> best_free
.length
:
1011 best_free
= free_geom
1014 # if this is a bootable partition we want to
1015 # use the first freespace region large enough
1016 # to satisfy the request
1021 def _scheduleLVs(installer
, devs
):
1022 if installer
.ds
.storage
.encryptedAutoPart
:
1025 pv
= LUKSDevice("luks-%s" % dev
.name
,
1026 format
=getFormat("lvmpv", device
=dev
.path
),
1030 installer
.ds
.storage
.createDevice(pv
)
1034 # create a vg containing all of the autopart pvs
1035 vg
= installer
.ds
.storage
.newVG(pvs
=pvs
)
1036 installer
.ds
.storage
.createDevice(vg
)
1039 # Convert storage.autoPartitionRequests into Device instances and
1040 # schedule them for creation.
1042 # Second pass, for LVs only.
1043 for request
in installer
.ds
.storage
.autoPartitionRequests
:
1044 if not request
.asVol
:
1047 if request
.fstype
is None:
1048 request
.fstype
= installer
.ds
.storage
.defaultFSType
1050 # FIXME: move this to a function and handle exceptions
1051 dev
= installer
.ds
.storage
.newLV(vg
=vg
,
1052 fmt_type
=request
.fstype
,
1053 mountpoint
=request
.mountpoint
,
1055 maxsize
=request
.maxSize
,
1058 # schedule the device for creation
1059 installer
.ds
.storage
.createDevice(dev
)