From 5166e52317e35e179993b00c748e20735199d8d9 Mon Sep 17 00:00:00 2001
From: David Lehman <dlehman@redhat.com>
Date: Thu, 17 Apr 2014 16:29:44 -0500
Subject: [PATCH] Start a new implementation of partition allocation.

Start with a list of every possible arrangement of partition requests
across the available free space regions on the available disks. Filter
the list of layouts down to those that satisfy the basic requirements
of the partition requests (and the bootloader/platform if doing an OS
installation). Choose the best layout and use the DiskChunk class to
do any growing.

XXX It would probably be worthwhile to pre-process situations like
      a) one request per disk across a large number of disks, and
      b) more identical disks than there are requests
    to reduce the mathematical workload of generating the layouts.
---
 blivet/autopart.py | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 blivet/autopart.py

diff --git a/blivet/autopart.py b/blivet/autopart.py
new file mode 100644
index 0000000..62132a3
--- /dev/null
+++ b/blivet/autopart.py
@@ -0,0 +1,129 @@
+import parted
+from itertools import combinations_with_replacements, ifilter
+
+from blivet.partitioning import PartitionRequest, DiskChunk, sectorsToSize, sizeToSectors, getFreeRegions
+from blivet.size import Size
+
+"""
+    What is needed?
+        - data structures
+            - request
+            - chunk
+            - disk?
+
+    General approach
+        - generate all possible permutations of the requests over the chunks
+            - 
+        - evaluate each permutation
+            - does it fit?
+            - does it meet all of the requests' requirements?
+            - how much growth does it allow?
+                - how do we evaluate this?
+                    - total growth?
+                    - some sort of equality?
+
+    Considerations
+        - size sets
+        - disk preference (soft)
+        - boot disk
+            - are there any platforms/bootloaders for which the boot request(s)
+              absolutely must be on the boot disk?
+                - how prevalent is that assumption throughout blivet/anaconda?
+
+"""
+class PartitionAllocator(object):
+    def __init__(self, disks, partitions, boot_disk=None):
+        self.disks = disks
+        self.partitions = partitions
+        self.boot_disk = boot_disk
+
+        self.disk_dict = dict((d.path, d) for d in self.disks)
+
+    def set_up_chunks(self):
+        free = getFreeRegions(self.disks)
+        chunks = [DiskChunk(f) for f in free]
+        requests = [PartitionRequest(p) for p in self.partitions]
+
+    def check_layout_fit(self, layout):
+        """ Return True if the given layout fits (before any growth). """
+        chunk_free = [c.length for c in self.chunks]
+        request_idx = 0
+        for chunk_idx in layout:
+            chunk = self.chunks[chunk_idx]
+            size = self.requests[request_idx].req_base_size
+            chunk_free[chunk_idx] -= chunk.sizeToLength(size)
+            if chunk_free[chunk_idx] < 0:
+                return False
+
+            request_idx += 1
+
+        return True
+
+    def check_layout_requirements(self, layout):
+        """ Return True if the layout meets all requests' requirements. """
+        request_idx = 0
+        for chunk_idx in layout:
+            request = self.requests[request_idx]
+            chunk = self.chunks[chunk_idx]
+            disk = self.disk_dict[chunk.path]
+
+            req_disks = request.partition.req_disks
+            if req_disks and disk not in req_disks:
+                return False
+
+            start_sector = request.partition.req_start_sector
+            if start_sector is not None and \
+               not chunk.geometry.containsSector(start_sector):
+                return False
+
+            extended = disk.format.extendedPartition
+            if request.partition.req_primary and extended and \
+               extended.geometry.contains(chunk.geometry):
+                return False
+
+            # XXX Is this really necessary on all(any) platforms?
+            if self.boot_disk and request.partition.req_base_weight > 1000 and \
+               disk != boot_disk:
+                return False
+
+            request_idx += 1
+
+        return True
+
+    def score_layout_by_weight(self, layout):
+        """ Score the layout based on request weights.
+
+            Scores are based on each request's weight and its allocation order.
+        """
+        score = 0
+        return score
+        weights = []
+        request_idx = 0
+        for chunk_idx in layout:
+            request = self.requests[request_idx]
+            weight = request.partition.req_base_weight
+            if not weight:
+                request_idx += 1
+                continue
+
+            weights.append(weight) 
+            score += weight * (request_idx + 1)
+
+    def score_layout_by_growth(self, layout):
+        """ Score the layout based on request growth. """
+        return 0
+
+    def get_layouts(self):
+        """ Generate a list of all possible layouts.
+
+            We end up with a list of lists. Each list's indices correspond to
+            those in the list of requests and each value references an index in
+            the chunk list. This tells us which chunk each request would go in
+            for that layout.
+        """
+        all_layouts = combinations_with_replacements(range(len(self.chunks)),
+                                                     len(self.requests))
+        fitting_layouts = ifilter(self.check_layout_fit, all_layouts)
+        working_layouts = ifilter(self.check_layout_requirements,
+                                  fitting_layouts)
+        return working_layouts
-- 
1.9.0

