/[pdpsoft]/nl.nikhef.ndpf.groupviews/trunk/ndpf-gv-mkplots
ViewVC logotype

Contents of /nl.nikhef.ndpf.groupviews/trunk/ndpf-gv-mkplots

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3272 - (show annotations) (download)
Wed Oct 9 09:53:58 2019 UTC (2 years, 8 months ago) by templon
File size: 14248 byte(s)
improvements to color scheme handling.


1 #! /usr/bin/env python
2 # $Id$
3 # Source: $URL$
4 # J. A. Templon, NIKHEF/PDP 2011
5
6 import optparse
7
8 p = optparse.OptionParser(description="Program to make rrdtool plots " + \
9 "of job running jobs by unix group")
10
11 # p.add_option("-r",action="store",dest="minsize",default='0',help="minimum size of dirs considered; can use suffixes k,M,G for multiples of 1000**{1,2,3} bytes")
12 # p.add_option("--qdel",action="store_true",dest="deljobs",help="delete jobs for which TMPDIR is larger than MINSIZE",default=False)
13
14 p.add_option("--rank-only",action="store_true",dest="rankonly",
15 help="don't plot, just print ranking of groups",default=False)
16
17 debug = 0
18
19 opts, args = p.parse_args()
20
21 import os
22
23 DATADIR=os.environ['HOME'] + '/ndpfdata/'
24 PLOTDIR='/www/grid/stats/groupviews/ndpf/'
25
26 # 8 class qualitative paired color scheme
27
28 colors = [ "#A6CEE3", "#1F77B4", "#B2DF8A", "#33A02C",
29 "#FB9A99", "#E31A1C", "#FDBF6F", "#FF7F00" ]
30
31 colors.reverse()
32
33 # for reference : ranges of the RRAs
34 # base step size 60 sec
35 # step 1 : 60 sec x 1600 points : 1600 min : 26,67 hr : 1.11 days
36 # step 2 : 120 sec x 1200 points : 2400 min : 40 hr : 1,67 days
37 # step 10 : 600 sec x 1800 points : 18 000 min : 300 hr : 12,5 days
38 # step 30 : 1800 sec x 2500 points : 75 000 min : 1250 hr : 52.08 days
39 # step 120 : 7200 sec x 1000 points : 120 000 min : 2000 hr : 83.33 days
40 # step 480 : 28800 sec x 1000 points : 480 000 min : 8000 hr : 333.33 days
41 # step 1440 : 86400 sec x 3650 points : 3650 days : 10 years
42
43 # resolutions of RRAs
44
45 # 1 : 60 sec : 1 min
46 # 2 : 120 sec : 2 min
47 # 3 : 600 sec : 10 min
48 # 4 : 1 800 sec : 30 min
49 # 5 : 7 200 sec : 120 min : 2 hr
50 # 6 : 28 800 sec : 480 min : 8 hr
51 # 7 : 86 400 sec : 1440 min : 24 hr : 1 day
52
53 NUMQS = 34 # number of quarters for "alltime" plot
54
55 plotrangedef = {
56 'hr' : { 'timeargs' : [ '-s', 'n-200min', '-e', 'n' ],
57 'timetag' : 'hr',
58 'avrange' : 3*3600,
59 'avres' : 60,
60 'sizeargs' : { 'small' : [ '--width', '200', '--height', '125',
61 '--x-grid',
62 'MINUTE:20:HOUR:1:HOUR:1:0:%H:00'
63 ],
64 'large' : [ '--width', '800', '--height', '500' ]
65 },
66 'numgroups' : 4,
67 },
68 'day' : { 'timeargs' : [ '-s', 'n-2000min', '-e', 'n' ],
69 'timetag' : 'day',
70 'avrange' : 24*3600,
71 'avres' : 60,
72 'sizeargs' : { 'small' : [ '--width', '200', '--height', '125',
73 '--x-grid',
74 'HOUR:6:DAY:1:HOUR:12:0:%a %H:00'
75 ],
76 'large' : [ '--width', '1000', '--height', '625' ]
77 },
78 'numgroups' : 5,
79 },
80 'week' : { 'timeargs' : [ '-s', 'n-288hr', '-e', 'n' ],
81 'timetag' : 'week',
82 'avrange' : 7*24*3600,
83 'avres' : 600,
84 'sizeargs' : { 'small' : [ '--width', '576', '--height', '125' ],
85 'large' : [ '--width', '1728', '--height', '375',
86 '-n', 'DEFAULT:16:']
87 },
88 'numgroups' : 6,
89 },
90
91 # note the construction "repr(576*n)" here --- this is because the plot is
92 # (a multiple of) 576 pixels, and 120 min is one of the RRAs, so choosing
93 # a lower limit of 576*120 gives us a plot with one pixel per RRA bin.
94
95 'month' : { 'timeargs' : [ '-s', 'n-'+repr(576*120)+'min', '-e', 'n'],
96 'timetag' : 'month',
97 'avrange' : 31*24*3600,
98 'avres' : 1800,
99 'sizeargs' : { 'small' : [ '--width', '576', '--height', '105'],
100 'large' : [ '--width', '2304', '--height', '420',
101 '-n', 'DEFAULT:18:',
102 '--x-grid',
103 'HOUR:12:DAY:1:DAY:3:86400:%d-%b'
104 ]
105 },
106 'numgroups' : 7,
107 },
108
109 'year' : { 'timeargs' : [ '-s', 'n-'+repr(576*1440)+'min', '-e', 'n'],
110 'timetag' : 'year',
111 'avrange' : 365*24*3600,
112 'avres' : 86400,
113 'sizeargs' : { 'small' : [ '--width', '576', '--height', '105'],
114 'large' : [ '--width', '2304', '--height', '420']
115 },
116 'numgroups' : 8,
117 },
118
119 # adjusting for nice plot ... set NUMQS above; range is NUMQS/4 years
120
121 'alltime' : { 'timeargs' : [ '-s', 'n-'+repr(NUMQS*365*1440/4)+'min', '-e', 'n'],
122 'timetag' : 'alltime',
123 'avrange' : NUMQS*365*24*3600/4,
124 'avres' : 86400,
125 'sizeargs' : { 'small' : [ '--width', '576', '--height', '105',
126 '--x-grid',
127 'MONTH:3:YEAR:1:YEAR:1:31536000:%Y'
128 ],
129 'large' : [ '--width', '2304', '--height', '420',
130 '--x-grid',
131 'MONTH:1:YEAR:1:MONTH:3:2592000:%b-%Y'
132 ]
133 },
134 'numgroups' : 8,
135 },
136
137
138 }
139
140 commonargs = ['--imgformat', 'PNG',
141 '--legend-position=east', '--legend-direction=bottomup']
142
143 import rrdtool
144 import time
145 import glob
146
147 ### function definitions
148
149 def doplot(glist, dbtype, psize, timetag, sizeargs, timeargs, pcents, ranktype):
150
151 grouplist = glist[:]
152 defs = list()
153 plots = list()
154
155 data_defs = list()
156 plot_defs = list()
157
158 gcolors = dict()
159
160 skipgroup = 0
161 if ranktype == 'bottom':
162 if 'unused' in grouplist:
163 grouplist.remove('unused')
164 grouplist.insert(0,'unused')
165 if 'offline' in grouplist:
166 grouplist.remove('offline')
167 grouplist.insert(0,'offline')
168 else:
169 if 'unused' in grouplist:
170 skipgroup += 1
171 if 'offline' in grouplist:
172 skipgroup += 1
173
174 ngr = len(grouplist)
175 ncol = len(colors)
176 for idx in range(ngr):
177 thisgrp = grouplist[idx]
178 print idx, thisgrp
179 if thisgrp == 'unused':
180 gcolors[thisgrp] = '#d8d8d8'
181 elif thisgrp == "offline":
182 gcolors[thisgrp] = "#790ead"
183 else:
184 print "ranktype, ngr, idx, skipgroup", ranktype, ngr, idx, skipgroup
185 gcolors[thisgrp] = colors[(ncol - ngr) + idx + skipgroup]
186
187 if dbtype == 'queued':
188 if 'unused' in grouplist: grouplist.remove('unused')
189 if 'offline' in grouplist: grouplist.remove('offline')
190
191 if ranktype == 'top':
192 for group in (grouplist + ['total']):
193 data_defs.append('DEF:'+group+'='+DATADIR+group+'.'+dbtype+'.rrd:'+dbtype+':AVERAGE')
194 otherstring = 'CDEF:other=total,'
195 for group in grouplist:
196 otherstring += group + ','
197 otherstring += (len(grouplist)-1) * '+,' + '-'
198
199 # print otherstring
200 data_defs.append(otherstring)
201 elif ranktype == 'bottom':
202 for group in grouplist:
203 data_defs.append('DEF:'+group+'='+DATADIR+group+'.'+dbtype+'.rrd:'+dbtype+':AVERAGE')
204 else:
205 print 'Unknown ranktype detected:', ranktype
206 sys.exit(2)
207
208 sumshown = 0
209 for idx in range(len(grouplist)):
210 group = grouplist[idx]
211 acolor = gcolors[group]
212 pdefstr = 'AREA' ':' + group + acolor + ':' + "%8s" % (group)
213 if pcents:
214 pdefstr = pdefstr + ' (' + "%4.1f" % (pcents[group]) + ')'
215 sumshown += float(pcents[group])
216 pdefstr = pdefstr + '\\n'
217 pdefstr = pdefstr + ':STACK'
218 plot_defs.append(pdefstr)
219
220 if ranktype == 'top':
221 pdefstr = 'AREA' ':' + 'other' + '#794044' + ':' + ' other'
222 if pcents:
223 pdefstr = pdefstr + ' (' + "%4.1f" % (100 - sumshown) + ')'
224 pdefstr = pdefstr + '\\n'
225 plot_defs.insert(0,pdefstr)
226 plot_defs.append("LINE:total#000000") # :total")
227
228 pargs = [ PLOTDIR + dbtype + '-' + timetag + '-' + ranktype + '-' + \
229 psize + '.png'] + commonargs + ['-l', '0'] + sizeargs[psize] + \
230 timeargs + data_defs + plot_defs
231 rrdtool.graph( *pargs )
232
233 def doplot_wait(glist, dbtype, psize, timetag, sizeargs, timeargs, ranktype):
234
235 grouplist = glist[:]
236 defs = list()
237 plots = list()
238
239 data_defs = list()
240 plot_defs = list()
241
242 skipgroup = 0
243 if dbtype == 'waittime':
244 if 'unused' in grouplist:
245 grouplist.remove('unused')
246 skipgroup += 1
247 if 'offline' in grouplist:
248 grouplist.remove('offline')
249 skipgroup += 1
250
251 for group in (grouplist + ['rollover','lastroll']):
252 data_defs.append('DEF:'+group+'='+DATADIR+group+'.'+dbtype+'.rrd:'+dbtype+':AVERAGE')
253
254 ncol = len(colors)
255 ngr = len(grouplist)
256 for idx in range(len(grouplist)):
257 group = grouplist[idx]
258 if group == 'unused':
259 acolor = '#d8d8d8'
260 skipgroup += 1
261 elif group == "offline":
262 acolor = "#790ead"
263 skipgroup += 1
264 else:
265 print ncol, ngr, idx, skipgroup
266 acolor = colors[(ncol - ngr) + idx ]
267 pdefstr = 'LINE3' ':' + group + acolor + ':' + group
268 pdefstr = pdefstr + '\\n'
269 plot_defs.append(pdefstr)
270
271 plot_defs.append('LINE2:rollover#660198')
272 plot_defs.append('LINE2:lastroll#000000')
273
274 pargs = [ PLOTDIR + dbtype + '-' + timetag + '-' + ranktype + '-' + \
275 psize + '.png'] + ['--slope-mode', '-o'] + commonargs + \
276 sizeargs[psize] + timeargs + data_defs + plot_defs
277 rrdtool.graph( *pargs )
278
279 def makeplots(prangedef):
280
281 resolu = prangedef['avres']
282 NUMGROUPS = prangedef['numgroups']
283
284 # first need to find "top eight" list
285 # base it on running jobs
286
287 now=int(time.mktime(time.localtime()))
288 end = (now / resolu) * resolu
289 start = end - (prangedef['avrange']) + resolu
290
291 ### block finding 'top N' group list ###
292
293 tgroup = dict() # structure tgroup[groupname] = total of hourly average
294
295 running_files = glob.glob(DATADIR+'*.running.rrd')
296 for db in running_files:
297 group = db[len(DATADIR):db.find('.running.rrd')]
298 tup = rrdtool.fetch(db,'AVERAGE','-r', repr(resolu),
299 '-s', repr(start), '-e', repr(end))
300 vallist = [0] # start with zero, in case no vals returned, get zero as answer
301 for tup2 in tup[2]:
302 val = tup2[0]
303 if val:
304 vallist.append(val)
305
306 # put numbers in meaningful units now. result returned is an
307 # integration over the time range, of averages over "resolu"
308 # ... native resolution is in minutes, so multiplying by
309 # (resolu / 60) puts the answer in core-minutes; dividing by
310 # the number of minutes in the range gives the average number
311 # of cores occupied, over the range
312
313 if group == "total":
314 totval = sum(vallist) * (resolu / 60) / ( (end-start) / 60. )
315 else:
316 tgroup[group] = sum(vallist) * (resolu / 60) / ( (end-start) / 60. )
317
318 pgroup = dict()
319 for g in tgroup.keys():
320 pgroup[g] = 100*tgroup[g]/totval
321
322 ### start block 'top N' and 'next N' group list ###
323
324 groups_sorted = sorted(tgroup, key=tgroup.get, reverse=False) # increasing useage towards end of list
325
326 topgroups = groups_sorted[ -NUMGROUPS: ]
327 bottgroups = groups_sorted[-2*NUMGROUPS:-NUMGROUPS]
328
329 for glist in [topgroups, bottgroups]:
330 if 'unused' in glist:
331 glist.remove('unused')
332 glist.append('unused') # makes it always appear at top of plot (except see offline)
333 if 'offline' in glist:
334 glist.remove('offline')
335 glist.append('offline') # makes it always appear at top of plot
336
337 if opts.rankonly and prangedef['timetag'] != 'hr' :
338 print "Ranked average running jobs over period", prangedef['timetag']
339 print "%10s %12d" % ('total', totval)
340 rank = 0
341 for g in reversed(groups_sorted):
342 if tgroup[g] > 0:
343 rank += 1
344 print "%2d. %10s %9.4f %9.4f%%" % (rank, g, tgroup[g], pgroup[g])
345 return
346
347 ### end block 'top N' group list ###
348
349 ### block generating plots ###
350
351
352 for dbtype in ['queued', 'running', 'waittime']:
353
354 ### this is a bit of a hack : we only want pgroups for when it's
355 ### a 'running' database and we don't want it for timetag hour.
356 ### fix this up here
357
358 if dbtype == 'running' and prangedef['timetag'] != 'hr':
359 percents = pgroup
360 else:
361 percents = None
362 for grps in [ ('top', topgroups), ('bottom', bottgroups) ]:
363 for psize in ['small', 'large']:
364 if dbtype == 'waittime':
365 doplot_wait(grps[1], dbtype, psize, prangedef['timetag'],
366 prangedef['sizeargs'],
367 prangedef['timeargs'], grps[0]
368 )
369 else:
370 doplot(grps[1], dbtype, psize, prangedef['timetag'],
371 prangedef['sizeargs'],
372 prangedef['timeargs'],
373 percents, grps[0]
374 )
375
376 if opts.rankonly:
377 from signal import signal, SIGPIPE, SIG_DFL
378 signal(SIGPIPE,SIG_DFL)
379
380 for k in ['hr', 'day', 'week', 'month', 'year', 'alltime']: # plotrangedef.keys():
381 makeplots(plotrangedef[k])
382
383 import sys
384
385 sys.stdout.flush()
386 sys.stdout.close()
387
388 sys.stderr.flush()
389 sys.stderr.close()
390
391 sys.exit(0)

Properties

Name Value
svn:executable *
svn:keywords Id URL

grid.support@nikhef.nl
ViewVC Help
Powered by ViewVC 1.1.28