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: