| 1 | #! /usr/bin/env python |
|---|
| 2 | # $Id$ |
|---|
| 3 | |
|---|
| 4 | '''graphviz representation of dCache PoolManager.conf''' |
|---|
| 5 | |
|---|
| 6 | __author__ = 'Greig A Cowan' |
|---|
| 7 | __date__ = '25 February 2008' |
|---|
| 8 | __version__ = '0.1' |
|---|
| 9 | |
|---|
| 10 | import re |
|---|
| 11 | from optparse import OptionParser |
|---|
| 12 | from pydot import Dot, Edge, Node, Cluster |
|---|
| 13 | |
|---|
| 14 | def main(): |
|---|
| 15 | '''Parse command line options, call methods''' |
|---|
| 16 | parser = OptionParser( |
|---|
| 17 | usage = 'usage: %prog [options] <PoolManager file> <graph filename>') |
|---|
| 18 | parser.add_option('-d', '--debug', dest='debug', action='store_true', |
|---|
| 19 | help='Use debug flag only for testing.') |
|---|
| 20 | parser.add_option('-c', dest='graphConf', default='graphPool.conf', |
|---|
| 21 | help='Configuration file for graphing.') |
|---|
| 22 | parser.add_option('-f', '--format', dest='format', default='dot', |
|---|
| 23 | choices=(Dot.formats + ['raw']), |
|---|
| 24 | help='output format supported by GraphViz (default: dot)') |
|---|
| 25 | parser.add_option('-p', '--prog', dest='prog', default='dot', |
|---|
| 26 | choices=('dot', 'twopi', 'neato', 'circo', 'fdp'), |
|---|
| 27 | help='GraphViz program to create output with (default: dot)') |
|---|
| 28 | |
|---|
| 29 | # Parse commane-line input |
|---|
| 30 | (options, args) = parser.parse_args() |
|---|
| 31 | try: |
|---|
| 32 | input_filename, output_filename = args |
|---|
| 33 | except ValueError: |
|---|
| 34 | parser.print_help() |
|---|
| 35 | parser.exit() |
|---|
| 36 | |
|---|
| 37 | if len(args) > 3: |
|---|
| 38 | parser.error('incorrect number of arguments') |
|---|
| 39 | if options.debug: |
|---|
| 40 | print 'reading %s...' % input_filename |
|---|
| 41 | |
|---|
| 42 | (poolList, unitList, linkMap, pgroupMap, |
|---|
| 43 | ugroupMap, linkGroupMap) = parsePoolManager( input_filename) |
|---|
| 44 | |
|---|
| 45 | if options.debug: |
|---|
| 46 | print poolList |
|---|
| 47 | print unitList |
|---|
| 48 | print linkMap |
|---|
| 49 | print pgroupMap |
|---|
| 50 | print ugroupMap |
|---|
| 51 | print linkGroupMap |
|---|
| 52 | |
|---|
| 53 | dot = generate_dot( poolList, pgroupMap, unitList, |
|---|
| 54 | ugroupMap, linkMap, linkGroupMap) |
|---|
| 55 | write_file(dot, output_filename, options.prog, options.format) |
|---|
| 56 | |
|---|
| 57 | def parsePoolManager( poolManager): |
|---|
| 58 | pool = re.compile('psu create pool (?P<pool>[\w\d\_\-]+)') |
|---|
| 59 | pgroup = re.compile('psu create pgroup (?P<pgroup>[\w\d\_\-]+)') |
|---|
| 60 | pgroupAdd = re.compile('psu addto pgroup (?P<pgroup>[\w\d\_\-]+) (?P<pool>[\w\d\_\-]+)') |
|---|
| 61 | |
|---|
| 62 | unit = re.compile('psu create unit -[\w]+\s+(?P<unit>[\w\d\_\-\@\:\/\.]+)+') |
|---|
| 63 | ugroup = re.compile('psu create ugroup (?P<ugroup>[\w\d\_\-]+)') |
|---|
| 64 | ugroupAdd = re.compile('psu addto ugroup (?P<ugroup>[\w\d\_\-]+) (?P<unit>[\w\d\_\-\@\:\/\.]+)') |
|---|
| 65 | |
|---|
| 66 | link = re.compile('psu create link (?P<link>[\w\d\_\-]+)\s+(?P<units>[\w\d\_\-\s]*)') |
|---|
| 67 | linkAdd = re.compile('psu add link (?P<link>[\w\d\_\-]+) (?P<pgroup>[\w\d\_\-]+)') |
|---|
| 68 | linkGroup = re.compile('psu create linkGroup (?P<linkGroup>[\w\d\_\-]+)') |
|---|
| 69 | linkGroupAdd = re.compile('psu addto linkGroup (?P<linkGroup>[\w\d\_\-]+) (?P<link>[\w\d\_\-]+)') |
|---|
| 70 | |
|---|
| 71 | poolList = [] |
|---|
| 72 | pgroupMap = {} |
|---|
| 73 | |
|---|
| 74 | unitList = [] |
|---|
| 75 | ugroupMap = {} |
|---|
| 76 | |
|---|
| 77 | linkMap = {} |
|---|
| 78 | linkGroupList = [] |
|---|
| 79 | linkGroupMap = {} |
|---|
| 80 | |
|---|
| 81 | f = open( poolManager, 'r') |
|---|
| 82 | lines = f.readlines() |
|---|
| 83 | lines.sort() |
|---|
| 84 | |
|---|
| 85 | for line in lines: |
|---|
| 86 | p = pool.match( line) |
|---|
| 87 | if p != None: |
|---|
| 88 | poolList.append( p.group('pool')) |
|---|
| 89 | pg = pgroup.match( line) |
|---|
| 90 | if pg != None: |
|---|
| 91 | if pgroupMap.has_key( pg.group('pgroup')): |
|---|
| 92 | continue |
|---|
| 93 | else: |
|---|
| 94 | pgroupMap[ pg.group('pgroup')] = [] |
|---|
| 95 | |
|---|
| 96 | u = unit.match( line) |
|---|
| 97 | if u != None: |
|---|
| 98 | unitList.append( u.group('unit')) |
|---|
| 99 | ug = ugroup.match( line) |
|---|
| 100 | if ug != None: |
|---|
| 101 | if ugroupMap.has_key( ug.group('ugroup')): |
|---|
| 102 | continue |
|---|
| 103 | else: |
|---|
| 104 | ugroupMap[ ug.group('ugroup')] = [] |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | lAdd = linkAdd.match( line) |
|---|
| 108 | if lAdd != None: |
|---|
| 109 | if linkMap.has_key( lAdd.group('link')): |
|---|
| 110 | linkMap[ lAdd.group('link') ][1].append( lAdd.group('pgroup')) |
|---|
| 111 | else: |
|---|
| 112 | linkMap[ lAdd.group('link') ] = [[],[ lAdd.group('pgroup')]] |
|---|
| 113 | |
|---|
| 114 | l = link.match( line) |
|---|
| 115 | if l != None: |
|---|
| 116 | if linkMap.has_key( l.group('link')): |
|---|
| 117 | linkMap[ l.group('link') ][0] = l.group('units').split() |
|---|
| 118 | else: |
|---|
| 119 | linkMap[ l.group('link') ] = [[],[]] |
|---|
| 120 | |
|---|
| 121 | lg = linkGroup.match( line) |
|---|
| 122 | if lg != None: |
|---|
| 123 | if linkGroupMap.has_key( lg.group('linkGroup')): |
|---|
| 124 | continue |
|---|
| 125 | else: |
|---|
| 126 | linkGroupMap[ lg.group('linkGroup')] = [] |
|---|
| 127 | |
|---|
| 128 | pgAdd = pgroupAdd.match( line) |
|---|
| 129 | if pgAdd != None: |
|---|
| 130 | if pgroupMap.has_key( pgAdd.group('pgroup')): |
|---|
| 131 | pgroupMap[ pgAdd.group('pgroup') ].append( pgAdd.group('pool')) |
|---|
| 132 | else: |
|---|
| 133 | pgroupMap[ pgAdd.group('pgroup') ] = [ pgAdd.group('pool')] |
|---|
| 134 | |
|---|
| 135 | ugAdd = ugroupAdd.match( line) |
|---|
| 136 | if ugAdd != None: |
|---|
| 137 | if ugroupMap.has_key( ugAdd.group('ugroup')): |
|---|
| 138 | ugroupMap[ ugAdd.group('ugroup') ].append( ugAdd.group('unit')) |
|---|
| 139 | else: |
|---|
| 140 | ugroupMap[ ugAdd.group('ugroup') ] = [ ugAdd.group('unit')] |
|---|
| 141 | |
|---|
| 142 | lgAdd = linkGroupAdd.match( line) |
|---|
| 143 | if lgAdd != None: |
|---|
| 144 | if linkGroupMap.has_key( lgAdd.group('linkGroup')): |
|---|
| 145 | linkGroupMap[ lgAdd.group('linkGroup') ].append( lgAdd.group('link')) |
|---|
| 146 | else: |
|---|
| 147 | linkGroupMap[ lgAdd.group('linkGroup') ] = [ lgAdd.group('link')] |
|---|
| 148 | |
|---|
| 149 | |
|---|
| 150 | f.close() |
|---|
| 151 | return poolList, unitList, linkMap, pgroupMap, ugroupMap, linkGroupMap |
|---|
| 152 | |
|---|
| 153 | def generate_dot( poolList, poolgroupMap, |
|---|
| 154 | unitList, unitgroupMap, |
|---|
| 155 | linkMap, linkGroupMap, |
|---|
| 156 | directed=True): |
|---|
| 157 | """Create dot graph representations.""" |
|---|
| 158 | |
|---|
| 159 | dot = Dot('PoolManager',size='90,80', rankdir='LR', |
|---|
| 160 | ranksep='3', ratio='auto', suppress_disconnected=True) |
|---|
| 161 | |
|---|
| 162 | if directed: |
|---|
| 163 | dot.set_type('digraph') |
|---|
| 164 | else: |
|---|
| 165 | dot.set_type('graph') |
|---|
| 166 | |
|---|
| 167 | cols = ['darkolivegreen3','darkolivegreen1', |
|---|
| 168 | 'darkseagreen2','darkseagreen4', |
|---|
| 169 | 'pink','pink3', |
|---|
| 170 | 'red4','red', |
|---|
| 171 | 'gold3','gold', |
|---|
| 172 | 'orange3','orange', |
|---|
| 173 | 'white'] |
|---|
| 174 | |
|---|
| 175 | # Create nodes. |
|---|
| 176 | # Pools |
|---|
| 177 | poolCluster = Cluster(graph_name='0', label='Pools', color=cols[12]) |
|---|
| 178 | for pool in poolList: |
|---|
| 179 | poolCluster.add_node(Node(pool+'_p', label=pool, style='filled', |
|---|
| 180 | color=cols[8], fillcolor=cols[9])) |
|---|
| 181 | |
|---|
| 182 | # Pool Groups |
|---|
| 183 | poolGroupCluster = Cluster(graph_name='1', label='Pool Groups', |
|---|
| 184 | color=cols[12], suppress_disconnected=False) |
|---|
| 185 | maxNumPools = 1 |
|---|
| 186 | for poolgroup, pools in poolgroupMap.iteritems(): |
|---|
| 187 | poolGroupCluster.add_node(Node(poolgroup+'_pg', label=poolgroup, |
|---|
| 188 | style='filled', |
|---|
| 189 | color=cols[10], fillcolor=cols[11])) |
|---|
| 190 | if len(pools) > maxNumPools : maxNumPools = len(pools) |
|---|
| 191 | |
|---|
| 192 | # Edges for Pool Groups -> Pools |
|---|
| 193 | # Loop again since we had to calculate the weighting above |
|---|
| 194 | for poolgroup, pools in poolgroupMap.iteritems(): |
|---|
| 195 | color = str( float( len(pools))/maxNumPools ) |
|---|
| 196 | for pool in pools: |
|---|
| 197 | dot.add_edge(Edge(poolgroup+'_pg', pool+'_p', |
|---|
| 198 | color='%s %s %s' % (color, color, color))) |
|---|
| 199 | |
|---|
| 200 | # Units |
|---|
| 201 | unitCluster = Cluster(graph_name='2', label='Unit', |
|---|
| 202 | color=cols[12], suppress_disconnected=False) |
|---|
| 203 | maxNumUnits = 1 |
|---|
| 204 | unitCluster = Cluster(graph_name='2', label='Units', color=cols[12]) |
|---|
| 205 | for unit in unitList: |
|---|
| 206 | unitCluster.add_node(Node(unit+'_u', label=unit, style='filled', |
|---|
| 207 | color=cols[0], fillcolor=cols[1])) |
|---|
| 208 | |
|---|
| 209 | # Unit Groups |
|---|
| 210 | unitGroupCluster = Cluster(graph_name='3', label='Unit Groups', |
|---|
| 211 | color=cols[12], suppress_disconnected=False) |
|---|
| 212 | for unitgroup, units in unitgroupMap.iteritems(): |
|---|
| 213 | unitGroupCluster.add_node(Node(unitgroup+'_ug', label=unitgroup, |
|---|
| 214 | style='filled', |
|---|
| 215 | color=cols[3], fillcolor=cols[2])) |
|---|
| 216 | if len(units) > maxNumUnits : maxNumUnits = len(units) |
|---|
| 217 | |
|---|
| 218 | # Edges for Units -> Unit Groups |
|---|
| 219 | # Loop again since we had to calculate the weighting above |
|---|
| 220 | for unitgroup, units in unitgroupMap.iteritems(): |
|---|
| 221 | color = str( float( len(units))/maxNumUnits ) |
|---|
| 222 | for unit in units: |
|---|
| 223 | dot.add_edge(Edge(unit+'_u', unitgroup+'_ug', |
|---|
| 224 | color='%s %s %s' % (color, color, color))) |
|---|
| 225 | |
|---|
| 226 | # Links |
|---|
| 227 | linkCluster = Cluster(graph_name='4', label='Link', |
|---|
| 228 | color=cols[12], suppress_disconnected=False) |
|---|
| 229 | |
|---|
| 230 | # Build dictionary that maps ugroups to links |
|---|
| 231 | unitgroupLinkMap = {} |
|---|
| 232 | for unitgroup in unitgroupMap: |
|---|
| 233 | for link, [unitgroups,poolgroups] in linkMap.iteritems(): |
|---|
| 234 | if unitgroup in unitgroups: |
|---|
| 235 | if unitgroupLinkMap.has_key( unitgroup): |
|---|
| 236 | unitgroupLinkMap[ unitgroup].append( link) |
|---|
| 237 | else: |
|---|
| 238 | unitgroupLinkMap[ unitgroup] = [] |
|---|
| 239 | |
|---|
| 240 | # Getting weighting for colour |
|---|
| 241 | maxNumLinks2PoolGroups = 0 |
|---|
| 242 | for link, [unitgroups, poolgroups] in linkMap.iteritems(): |
|---|
| 243 | linkCluster.add_node(Node(link+'_l', label=link, style='filled', |
|---|
| 244 | color=cols[5], fillcolor=cols[4])) |
|---|
| 245 | if len(poolgroups)> maxNumLinks2PoolGroups: |
|---|
| 246 | maxNumLinks2PoolGroups = len(poolgroups) |
|---|
| 247 | |
|---|
| 248 | |
|---|
| 249 | # Getting weighting for colour |
|---|
| 250 | maxNumUnitGroups2Links = 0 |
|---|
| 251 | for unitgroup, links in unitgroupLinkMap.iteritems(): |
|---|
| 252 | if len(links) > maxNumUnitGroups2Links : |
|---|
| 253 | maxNumUnitGroups2Links = len(links) |
|---|
| 254 | |
|---|
| 255 | # Tie it all together. Unit Groups -> Links -> Pool Groups |
|---|
| 256 | for link, [unitgroups,poolgroups] in linkMap.iteritems(): |
|---|
| 257 | for unitgroup in unitgroups: |
|---|
| 258 | color=str( float( len(unitgroupLinkMap[unitgroup]))/maxNumUnitGroups2Links) |
|---|
| 259 | dot.add_edge(Edge(unitgroup+'_ug', link+'_l', |
|---|
| 260 | color='%s %s %s' % (color, color, color))) |
|---|
| 261 | for poolgroup in poolgroups: |
|---|
| 262 | color = str( float( len( poolgroups))/maxNumLinks2PoolGroups) |
|---|
| 263 | dot.add_edge(Edge(link+'_l', poolgroup+'_pg', |
|---|
| 264 | color='%s %s %s' % (color, color, color))) |
|---|
| 265 | |
|---|
| 266 | # Link Groups |
|---|
| 267 | linkGroupCluster = Cluster(graph_name='5', label='Link Groups', |
|---|
| 268 | color=cols[12]) |
|---|
| 269 | for linkgroup in linkGroupMap.keys(): |
|---|
| 270 | linkGroupCluster.add_node(Node(linkgroup+'_lg', label=linkgroup, |
|---|
| 271 | color=cols[7], fillcolor=cols[6])) |
|---|
| 272 | |
|---|
| 273 | # Edges for Link Groups -> Links |
|---|
| 274 | for linkgroup, links in linkGroupMap.iteritems(): |
|---|
| 275 | for link in links: |
|---|
| 276 | dot.add_edge(Edge(linkgroup+'_lg', link+'_l', color='grey')) |
|---|
| 277 | |
|---|
| 278 | |
|---|
| 279 | dot.add_subgraph( poolCluster) |
|---|
| 280 | dot.add_subgraph( poolGroupCluster) |
|---|
| 281 | |
|---|
| 282 | dot.add_subgraph( unitCluster) |
|---|
| 283 | dot.add_subgraph( unitGroupCluster) |
|---|
| 284 | |
|---|
| 285 | dot.add_subgraph( linkCluster) |
|---|
| 286 | dot.add_subgraph( linkGroupCluster) |
|---|
| 287 | |
|---|
| 288 | return dot |
|---|
| 289 | |
|---|
| 290 | |
|---|
| 291 | def write_file(dot, name, prog, format): |
|---|
| 292 | '''Create a graphics file from the DOT file.''' |
|---|
| 293 | filename = '%s.%s.%s' % (name, prog, format) |
|---|
| 294 | dot.write(filename, prog, format) |
|---|
| 295 | print "Wrote %s output to '%s' using %s." % (format, filename, prog) |
|---|
| 296 | |
|---|
| 297 | |
|---|
| 298 | if __name__=='__main__': |
|---|
| 299 | main() |
|---|