#!/usr/bin/python """ Perform recipe calculation gnuplot plot """ import sys import re from subprocess import call import logging logger = logging.getLogger(__name__) def main(): logging.basicConfig(stream=sys.stderr, level=logging.INFO) filename = sys.argv[1] datafile = filename + '.dat' parts = list(read(filename)) start, step, end = 9<<10, 1<<10, 1<<20 # MiB with open(datafile, 'w') as data: size = start while size <= end: calc(parts, size) print >> data, size, '\t'.join('%d' % part.size for part in parts) size += step cmd = ( 'gnuplot', '-e', 'set logscale', '-e', 'set xrange [%d:%d]' % (start, end), '-e', """plot %s""" % ( ', '.join( '"%s" using 1:%d title "%s"' % (datafile, i, part.label) for i, part in enumerate(parts, start=2) ) ), '-', ) logger.info(cmd) call(cmd) cmd = ('rm', '-f', datafile) logger.debug(cmd) call(cmd) def calc(parts, free_space): for part in parts: part.reset() facsum = sum(part.factor for part in parts) ready = False while not ready: ready = True minsum = sum(part.size for part in parts) for part in parts: logger.debug(part) assert minsum <= free_space for i, part in enumerate(parts): new_size = part.size + (free_space - minsum) * part.factor / facsum if part.maximum_size != -1 and new_size > part.maximum_size: new_size = part.maximum_size assert 0 <= new_size <= (free_space if part.maximum_size == -1 else part.maximum_size) if new_size != part.size: part.size = new_size ready = False class Part(object): RE_MP = re.compile(r'mountpoint *\{ *([/a-z]+) *\}') RE_ME = re.compile(r'method *\{ *([a-z]+) *\}') def __init__(self, mins, fac, maxs): mins = self.memsize(mins) self.minimum_size = mins fac = int(fac) self.priority = max(fac, self.minimum_size) maxs = self.memsize(maxs) if maxs == -1: self.maximum_size = -1 else: self.maximum_size = max(maxs, self.minimum_size) self.label = '' self.reset() def reset(self): self._size = self.minimum_size @staticmethod def memsize(s, memory=1024): # MiB if s.endswith('%'): return int(s[:-1], 10) * memory / 100 return int(s) @property def factor(self): return self.priority - self.minimum_size @property def size(self): return self._size @size.setter def size(self, value): if self.maximum_size != -1: assert self.minimum_size <= value <= self.maximum_size self._size = min(value, self.maximum_size) else: assert self.minimum_size <= value self._size = value def __str__(self): return '%d %d %d %s %d' % (self.minimum_size, self.priority, self.maximum_size, self.label, self.size) def read(filename): parts = [] with open(filename, 'r') as recipe: for line in recipe: if '::' in line: continue match = Part.RE_MP.search(line) if match: part.label = match.group(1) match = Part.RE_ME.search(line) if match: part.label = match.group(1) if line.startswith('\t'): continue line = line.strip() if not line: rec = None continue mins, fac, maxs, fs = line.split() part = Part(mins, fac, maxs) parts.append(part) return parts if __name__ == '__main__': main()