[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Wanted: partitioner recipe calculator



Quoting Anton Zinoviev <anton@lml.bas.bg>:
> However I have just described in
> debian-installer/installer/doc/devel/partman-auto-recipe.txt
> how partman-auto computes the partition sizes.

The new description is very good and easy to understand.  Thanks!
When writing a new recipe, it's nice to try the recipe without
really having to eat the soup.  I hacked a little Python script,
that generates an HTML page visualising the effect of a recipe
on different combinations of hard disk size and memory.  Maybe
it's of use to some users of d-i.  See attachment, have fun.

Cheers, WB
#!/usr/bin/env python
# Copyright 2005 W. Borgert, distribute under GNU General Public License v2
# Create HTML visualisation of hard-disk partitioning recipe

import os
import sys
import gdchart
import pyparsing as pp


class Partition:
    def __init__(self, min, priority, max, max_relative, use):
        self.min_orig = min
        self.max_orig = max
        self.min = min
        self.priority = priority
        self.max = max
        self.max_relative = max_relative
        self.use = use
        self.factor = priority - min

    def reset(self):
        self.min = self.min_orig
        self.max = self.max_orig


class Recipe:
    """Pseudo parser for recipe files."""
    def __init__(self):
        self.bnf = self.createBNF()
        self.partitions = []
        self.init_values()

    def init_values(self):
        self.min = 0
        self.priority = 0
        self.max = 0
        self.max_relative = False
        self.use = ""

    def createBNF(self):
        Identifier = pp.Word(pp.alphas, pp.alphanums + "_-/")
        FileContents = pp.Word(pp.alphanums + "-/")
        Specifier = (pp.Optional(pp.Literal("$")) + Identifier \
                     + pp.Literal("{") + pp.ZeroOrMore(FileContents) \
                     + pp.Literal("}"))
        Specifier.setParseAction(self.set_use)
        Priority = pp.Word(pp.nums)
        Priority.setParseAction(self.set_priority)
        MaximalSize = pp.Word(pp.nums) + pp.Optional(pp.Literal("%"))
        MaximalSize.setParseAction(self.set_max)
        MinimalSize = pp.Word(pp.nums)
        MinimalSize.setParseAction(self.set_min)
        Limits = MinimalSize + Priority + MaximalSize + Identifier
        Partition = (Limits + pp.OneOrMore(Specifier) + pp.Literal("."))
        Partition.setParseAction(self.finish_partition)
        Header = Identifier + (pp.Literal("::") ^ pp.Literal(":"))
        bnf = (Header + pp.OneOrMore(Partition) + pp.StringEnd())
        bnf.ignore(pp.Literal("#") + pp.restOfLine)
        return bnf

    def set_min(self, s, loc, toks):
        self.min = int(toks[0])

    def set_priority(self, s, loc, toks):
        self.priority = int(toks[0])

    def set_max(self, s, loc, toks):
        self.max = int(toks[0])
        if len(toks) > 1:
            self.max_relative = True

    def set_use(self, s, loc, toks):
        if len(toks) > 3 and toks[0] == "mountpoint":
            self.use = toks[2]
        elif len(toks) > 3 and toks[0] == "method" and toks[2] == "swap":
            self.use = "swap"

    def finish_partition(self, s, loc, toks):
        self.partitions.append(Partition(
            self.min, self.priority, self.max, self.max_relative, self.use))
        self.init_values()


def calculate(partitions, free_space, memory):
    for p in recipe.partitions:
        p.reset()
    ready = False
    while not ready:
        minsum = factsum = 0
        for p in partitions:
            if p.max_relative:
                p.max = p.max * memory / 100.
            minsum += p.min
            factsum += p.factor
        ready = True
        for p in partitions:
            x = p.min + (free_space - minsum) * p.factor / factsum
            if x > p.max:
                x = p.max
            if x != p.min:
                ready = False
                p.min = x


if __name__ == '__main__':
    if len(sys.argv) < 3:
        print >>sys.stderr, \
              """Usage: %s <recipe file> {<free space>:<memory>}+
e.g. %s server.rec 160000:1024 200000:2048""" % (sys.argv[0], sys.argv[0])
        sys.exit(-1)
    recipe = Recipe()
    recipe.bnf.parseFile(sys.argv[1])
    print """<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\";>
<html><head><title>Partitioning</title></head><body><h1>Partitioning</h1>"""
    pie = gdchart.Pie()
    pie.bg_color = "white"
    pie.color = map(
        lambda c: gdchart.RGB(c[0], c[1], c[2]),
        [(0xff, 0xff, 0x00), (0xff, 0xa5, 0x00), (0xff, 0x00, 0x00),
         (0xff, 0x00, 0xff), (0x80, 0x00, 0x80), (0x80, 0x00, 0x00),
         (0x00, 0xff, 0xff), (0x00, 0xff, 0x00), (0x00, 0x80, 0x80),
         (0x00, 0x80, 0x00), (0x00, 0x00, 0xff), (0x00, 0x00, 0x80)])
    for spec in sys.argv[2:]:
        free_space, memory = map(lambda x: int(x), spec.split(":"))
        filename = "hd-%d-%d.png" % (free_space, memory)
        if os.path.exists(filename):
            print >>sys.stderr, \
                  "file %s already exists - not overwriting" % filename
            continue
        calculate(recipe.partitions, free_space, memory)
        print """<table border='0'><tr valign='center'><td>
<table border='0'>
<tr><th align='left'>Hard Disk</th><td align='right'>%d</td></tr>
<tr><th align='left'>Memory</th><td align='right'>%d</td></tr>""" \
              % (free_space, memory)
        for p in recipe.partitions:
            print "<tr><th align='left'>%s</th><td align='right'>%d</td></tr>" \
                  % (p.use, p.min)
        print "</table></td><td rowspan='2'><img src='%s'></td></tr></table>" \
              % filename
        apply(pie.setData, [p.min for p in recipe.partitions])
        pie.setLabels([("%d %s" % (p.min/1024., p.use))[:11]
                       for p in recipe.partitions])
        pie.draw(filename)
    print "</body></html>"

Reply to: