The Partman Partition Recipe Calculator was original posted to the debian-boot mailinglist
It needs python-gdchart2
1 #!/usr/bin/env python
2 # Copyright 2005 W. Borgert, distribute under GNU General Public License v2
3 # Create HTML visualisation of hard-disk partitioning recipe
4
5 import os
6 import sys
7 import gdchart
8 import pyparsing as pp
9
10
11 class Partition:
12 def __init__(self, min, priority, max, max_relative, use):
13 self.min_orig = min
14 self.max_orig = max
15 self.min = min
16 self.priority = priority
17 self.max = max
18 self.max_relative = max_relative
19 self.use = use
20 self.factor = priority - min
21
22 def reset(self):
23 self.min = self.min_orig
24 self.max = self.max_orig
25
26
27 class Recipe:
28 """Pseudo parser for recipe files."""
29 def __init__(self):
30 self.bnf = self.createBNF()
31 self.partitions = []
32 self.init_values()
33
34 def init_values(self):
35 self.min = 0
36 self.priority = 0
37 self.max = 0
38 self.max_relative = False
39 self.use = ""
40
41 def createBNF(self):
42 Identifier = pp.Word(pp.alphas, pp.alphanums + "_-/")
43 FileContents = pp.Word(pp.alphanums + "-/")
44 Specifier = (pp.Optional(pp.Literal("$")) + Identifier \
45 + pp.Literal("{") + pp.ZeroOrMore(FileContents) \
46 + pp.Literal("}"))
47 Specifier.setParseAction(self.set_use)
48 Priority = pp.Word(pp.nums)
49 Priority.setParseAction(self.set_priority)
50 MaximalSize = pp.Word(pp.nums) + pp.Optional(pp.Literal("%"))
51 MaximalSize.setParseAction(self.set_max)
52 MinimalSize = pp.Word(pp.nums)
53 MinimalSize.setParseAction(self.set_min)
54 Limits = MinimalSize + Priority + MaximalSize + Identifier
55 Partition = (Limits + pp.OneOrMore(Specifier) + pp.Literal("."))
56 Partition.setParseAction(self.finish_partition)
57 Header = Identifier + (pp.Literal("::") ^ pp.Literal(":"))
58 bnf = (Header + pp.OneOrMore(Partition) + pp.StringEnd())
59 bnf.ignore(pp.Literal("#") + pp.restOfLine)
60 return bnf
61
62 def set_min(self, s, loc, toks):
63 self.min = int(toks[0])
64
65 def set_priority(self, s, loc, toks):
66 self.priority = int(toks[0])
67
68 def set_max(self, s, loc, toks):
69 self.max = int(toks[0])
70 if len(toks) > 1:
71 self.max_relative = True
72
73 def set_use(self, s, loc, toks):
74 if len(toks) > 3 and toks[0] == "mountpoint":
75 self.use = toks[2]
76 elif len(toks) > 3 and toks[0] == "method" and toks[2] == "swap":
77 self.use = "swap"
78
79 def finish_partition(self, s, loc, toks):
80 self.partitions.append(Partition(
81 self.min, self.priority, self.max, self.max_relative, self.use))
82 self.init_values()
83
84
85 def calculate(partitions, free_space, memory):
86 for p in recipe.partitions:
87 p.reset()
88 ready = False
89 while not ready:
90 minsum = factsum = 0
91 for p in partitions:
92 if p.max_relative:
93 p.max = p.max * memory / 100.
94 minsum += p.min
95 factsum += p.factor
96 ready = True
97 for p in partitions:
98 x = p.min + (free_space - minsum) * p.factor / factsum
99 if x > p.max:
100 x = p.max
101 if x != p.min:
102 ready = False
103 p.min = x
104
105
106 if __name__ == '__main__':
107 if len(sys.argv) < 3:
108 print >>sys.stderr, \
109 """Usage: %s <recipe file> {<free space>:<memory>}+
110 e.g. %s server.rec 160000:1024 200000:2048""" % (sys.argv[0], sys.argv[0])
111 sys.exit(-1)
112 recipe = Recipe()
113 recipe.bnf.parseFile(sys.argv[1])
114 print """<?xml version='1.0' encoding='utf-8'?>
115 <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
116 <html><head><title>Partitioning</title></head><body><h1>Partitioning</h1>"""
117 pie = gdchart.Pie3D()
118 pie.bg_color = "white"
119 pie.color = map(
120 lambda c: gdchart.RGB(c[0], c[1], c[2]),
121 [(0xff, 0xff, 0x00), (0xff, 0xa5, 0x00), (0xff, 0x00, 0x00),
122 (0xff, 0x00, 0xff), (0x80, 0x00, 0x80), (0x80, 0x00, 0x00),
123 (0x00, 0xff, 0xff), (0x00, 0xff, 0x00), (0x00, 0x80, 0x80),
124 (0x00, 0x80, 0x00), (0x00, 0x00, 0xff), (0x00, 0x00, 0x80)])
125 for spec in sys.argv[2:]:
126 free_space, memory = map(lambda x: int(x), spec.split(":"))
127 filename = "hd-%d-%d.png" % (free_space, memory)
128 if os.path.exists(filename):
129 print >>sys.stderr, \
130 "file %s already exists - not overwriting" % filename
131 continue
132 calculate(recipe.partitions, free_space, memory)
133 print """<table border='0'><tr valign='center'><td>
134 <table border='0'>
135 <tr><th align='left'>Hard Disk</th><td align='right'>%d</td></tr>
136 <tr><th align='left'>Memory</th><td align='right'>%d</td></tr>""" \
137 % (free_space, memory)
138 for p in recipe.partitions:
139 print "<tr><th align='left'>%s</th><td align='right'>%d</td></tr>" \
140 % (p.use, p.min)
141 print "</table></td><td rowspan='2'><img src='%s'></td></img></tr></table>" \
142 % filename
143 apply(pie.setData, [p.min for p in recipe.partitions])
144 pie.setLabels([("%d %s" % (p.min/1024., p.use))[:11]
145 for p in recipe.partitions])
146 pie.draw(filename)
147 print "</body></html>"
A sample server.rec could look like this
boot-root :: 100 300 200 ext3 $primary{ } $bootable{ } method{ format } format{ } use_filesystem{ } filesystem{ ext3 } mountpoint{ /boot } . 4000 10000 10000 ext3 method{ format } format{ } $lvmok{ } use_filesystem{ } filesystem{ ext3 } mountpoint{ / } . 500 9000 2000 ext3 method{ format } format{ } $lvmok{ } use_filesystem{ } filesystem{ ext3 } mountpoint{ /tmp } . 6000 9000 20000 ext3 method{ format } format{ } $lvmok{ } use_filesystem{ } filesystem{ ext3 } mountpoint{ /var } . 512 512 200% linux-swap $lvmok{ } method{ swap } format{ } . 40000 60000 100000000 ext3 method{ format } format{ } $lvmok{ } use_filesystem{ } filesystem{ ext3 } mountpoint{ /home } .