From: "André Erdmann" <dywi@mailerd.de>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/db/
Date: Wed, 14 Aug 2013 14:56:24 +0000 (UTC) [thread overview]
Message-ID: <1376491577.cd4a553f69b3da9519310ba5f528aac8541f5bd9.dywi@gentoo> (raw)
commit: cd4a553f69b3da9519310ba5f528aac8541f5bd9
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Wed Aug 14 14:46:17 2013 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Wed Aug 14 14:46:17 2013 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=cd4a553f
roverlay/db/rrdgraph: graph creation
This module provides an interface for calling "rrdtool graphv".
It features include:
* a "protocol" for parsing rrdtool graphv's output into a dict
* objects representing data/graph statements (RRDGraphArg_*)
work in progress -- module is not "stable" yet.
---
roverlay/db/rrdgraph.py | 520 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 520 insertions(+)
diff --git a/roverlay/db/rrdgraph.py b/roverlay/db/rrdgraph.py
new file mode 100644
index 0000000..7464165
--- /dev/null
+++ b/roverlay/db/rrdgraph.py
@@ -0,0 +1,520 @@
+# R overlay -- stats collection, rrdtool graph creation
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 André Erdmann <dywi@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import os
+import sys
+import time
+
+import roverlay.util.objects
+
+
+# a few colors for drawing graphs (as str)
+# RRGGBB[AA], rgb+alpha
+COLORS = {
+ 'white' : 'ffffff',
+ 'silver' : 'c0c0c0',
+ 'gray' : '808080',
+ 'grey' : '808080',
+ 'black' : '000000',
+ 'red' : 'ff0000',
+ 'maroon' : '800000',
+ 'yellow' : 'ffff00',
+ 'olive' : '808000',
+ 'lime' : '00ff00',
+ 'green' : '008000',
+ 'aqua' : '00ffff',
+ 'teal' : '008080',
+ 'blue' : '0000ff',
+ 'navy' : '000080',
+ 'fuchsia' : 'ff00ff',
+ 'purple' : '800080',
+}
+
+
+class RRDGraphArg ( object ):
+
+ def gen_options ( self, options, no_key=False ):
+ for attr_name in options:
+ #attr = getattr ( self, attr_name, None )
+ attr = getattr ( self, attr_name.lower().replace ( '-', '_' ) )
+ if attr is not None:
+ if no_key:
+ yield ':' + str ( attr )
+ elif attr is True:
+ yield ':' + attr_name
+ else:
+ yield ':{k}={v}'.format ( k=attr_name, v=str ( attr ) )
+ # --- end of gen_options (...) ---
+
+ def get_options_str ( self, *options ):
+ return ''.join ( self.gen_options ( options, no_key=False ) )
+ # --- end of get_options_str (...) ---
+
+ def get_options_str_nokey ( self, *options ):
+ return ''.join ( self.gen_options ( options, no_key=True ) )
+ # --- end of get_options_str_nokey (...) ---
+
+
+ @roverlay.util.objects.abstractmethod
+ def get_str ( self, rrd_db ):
+ pass
+ # --- end of get_str (...) ---
+
+ def get_color_suffix ( self, color ):
+ if color is None:
+ return ''
+ elif color in COLORS:
+ return '#' + COLORS [color]
+ else:
+ return '#' + str ( color ).lstrip ( '#' )
+ # --- end of get_color_suffix (...) ---
+
+# --- end of RRDGraphArg ---
+
+class RRDGraphArg_DEF ( RRDGraphArg ):
+
+ def __init__ ( self,
+ name, ds_name, cf, rrdfile=None, step=None, start=None, stop=None
+ ):
+ super ( RRDGraphArg_DEF, self ).__init__()
+ self.name = name
+ self.rrdfile = rrdfile
+ self.ds_name = ds_name
+ self.cf = cf
+ self.step = step
+ self.start = start
+ self.stop = stop
+ # --- end of __init__ (...) ---
+
+ def get_str ( self, rrd_db ):
+ rrdfile = self.rrdfile if self.rrdfile is not None else rrd_db.filepath
+ return (
+ "DEF:{vname}={rrdfile}:{ds_name}:{cf}".format (
+ vname=self.name, rrdfile=rrdfile, ds_name=self.ds_name, cf=self.cf
+ )
+ + self.get_options_str ( 'step', 'start', 'stop' )
+ )
+ # --- end of get_str (...) ---
+
+# --- end of RRDGraphArg_DEF ---
+
+class RRDGraphArg_CDEF ( RRDGraphArg ):
+
+ DEFTYPE = 'CDEF'
+
+ def __init__ ( self, name, rpn, expression=None ):
+ super ( RRDGraphArg_CDEF, self ).__init__()
+ self.name = name
+ self.rpn = rpn
+ self.expression = expression
+ # --- end of __init__ (...) ---
+
+ def get_str ( self, rrd_db ):
+ if self.expression is None:
+ return "{deftype}:{vname}={rpn}".format (
+ deftype=self.__class__.DEFTYPE, vname=self.name, rpn=self.rpn
+ )
+ else:
+ return "{deftype}:{vname}={rpn} {expr}".format (
+ deftype=self.__class__.DEFTYPE,
+ vname=self.name, rpn=self.rpn, expr=self.expression
+ )
+ # --- end of get_str (...) ---
+
+# --- end of RRDGraphArg_CDEF ---
+
+class RRDGraphArg_VDEF ( RRDGraphArg_CDEF ):
+ DEFTYPE = 'VDEF'
+# --- end of RRDGraphArg_VDEF ---
+
+class RRDGraphArg_AREA ( RRDGraphArg ):
+
+ def __init__ ( self,
+ value, color=None, legend=None, stack=None, skipscale=None
+ ):
+ super ( RRDGraphArg_AREA, self ).__init__()
+ self.value = value
+ self.color = color
+ self.legend = legend
+ self.stack = stack
+ self.skipscale = skipscale
+ # --- end of __init__ (...) ---
+
+ def get_str ( self, rrd_db ):
+ return (
+ "AREA:{value}{vcolor}".format (
+ value=self.value, vcolor=self.get_color_suffix ( self.color )
+ )
+ + self.get_options_str_nokey ( 'legend' )
+ + self.get_options_str ( 'STACK', 'skipscale' )
+ )
+ # --- end of get_str (...) ---
+
+# --- end of RRDGraphArg_AREA ---
+
+class RRDGraphArg_LINE ( RRDGraphArg ):
+
+ def __init__ ( self,
+ value, color=None, width=None, legend=None, stack=None, skipscale=None,
+ dashes=None, dash_offset=None,
+ ):
+ super ( RRDGraphArg_LINE, self ).__init__()
+ self.width = width
+ self.value = value
+ self.color = color
+ self.legend = legend
+ self.stack = stack
+ self.skipscale = skipscale
+ self.dashes = dashes
+ self.dash_offset = dash_offset
+ # --- end of __init__ (...) ---
+
+ def get_str ( self, rrd_db ):
+ if self.width is not None:
+ if isinstance ( self.width, str ):
+ width = self.width
+ else:
+ width = str ( float ( self.width ) )
+ else:
+ width = ""
+
+ width
+ return (
+ 'LINE' + width + ':{value}{vcolor}'.format (
+ value=self.value, vcolor=self.get_color_suffix ( self.color ),
+ ) + self.get_options_str_nokey ( 'legend' )
+ + self.get_options_str (
+ 'STACK', 'skipscale', 'dashes', 'dash-offset'
+ )
+ )
+ # --- end of get_str (...) ---
+
+# --- end of RRDGraphArg_AREA ---
+
+class RRDGraphArg_PRINT ( RRDGraphArg ):
+
+ ARG_TYPE = 'PRINT'
+
+ def __init__ ( self, name, format_str=None ):
+ super ( RRDGraphArg_PRINT, self ).__init__()
+ self.name = name
+ if format_str is None:
+ self.format_str = '%.0lf %S'
+ else:
+ self.format_str = format_str
+ # --- end of __init__ (...) ---
+
+ def get_str ( self, rrd_db ):
+ return self.ARG_TYPE + ':' + self.name + ':' + self.format_str
+ # --- end of get_str (...) ---
+
+# --- end of RRDGraphArg_PRINT ---
+
+class RRDGraphArg_GPRINT ( RRDGraphArg_PRINT ):
+ ARG_TYPE = 'GPRINT'
+# --- end of RRDGraphArg_GPRINT ---
+
+
+class RRDGraph ( object ):
+
+ GRAPH_COMMAND = 'graphv'
+
+ SECONDS_MINUTE = 60
+ SECONDS_HOUR = SECONDS_MINUTE*60
+ SECONDS_DAY = SECONDS_HOUR*24
+ SECONDS_WEEK = SECONDS_DAY*7
+ SECONDS_MONTH = SECONDS_DAY*31
+ SECONDS_YEAR = SECONDS_DAY*365
+
+ OPTIONS_ATTR = (
+ 'start', 'end', 'imgformat', 'title', 'vertical_label'
+ )
+
+ def __init__ ( self, rrd_db, **kwargs ):
+ super ( RRDGraph, self ).__init__()
+ self.image_data = None
+ self.rrd_db = rrd_db
+ self.extra_options = list()
+ self.args = list()
+
+ self.start = None
+ self.end = None
+ self.imgformat = 'PNG'
+ self.title = None
+ self.vertical_label = None
+ self.colors = []
+
+ self.__dict__.update ( kwargs )
+ # --- end of __init__ (...) ---
+
+ def get_image ( self ):
+ if self.image_data:
+ return self.image_data ['image_data']
+ else:
+ return None
+ # --- end of get_image (...) ---
+
+ def get_timespan ( self, delta, stop=None, delta_factor=1.0 ):
+ t_stop = time.time() - 1 if stop is None else stop
+ return ( int ( t_stop - ( delta_factor * delta ) ), int ( t_stop ) )
+ # --- end of get_timespan (...) ---
+
+ def set_timespan ( self, delta, stop=None, delta_factor=SECONDS_DAY, recursive=False ):
+ self.start, self.end = self.get_timespan (
+ delta, stop=stop, delta_factor=delta_factor
+ )
+ if recursive:
+ for arg in self.args:
+ if isinstance ( arg, RRDGraphArg ):
+ if hasattr ( arg, 'start' ):
+ arg.start = self.start
+ if hasattr ( arg, 'stop' ):
+ arg.stop = self.end
+ # --- end of set_timespan (...) ---
+
+ def add_arg ( self, arg ):
+ self.args.append ( arg )
+ return arg
+ # --- end of add_arg (...) ---
+
+ def add_args ( self, *args ):
+ self.args.extend ( args )
+ # --- end of add_args (...) ---
+
+ def add_def ( self, name, ds_name, cf, step=None ):
+ return self.add_arg (
+ RRDGraphArg_DEF (
+ name=name, ds_name=ds_name, cf=cf, rrdfile=None, step=step,
+ )
+ )
+ # --- end of add_def (...) ---
+
+ def add_cdef ( self, name, rpn, expression=None ):
+ return self.add_arg (
+ RRDGraphArg_CDEF ( name=name, rpn=rpn, expression=expression )
+ )
+ # --- end of add_cdef (...) ---
+
+ def add_vdef ( self, name, rpn, expression=None ):
+ return self.add_arg (
+ RRDGraphArg_VDEF ( name=name, rpn=rpn, expression=expression )
+ )
+ # --- end of add_vdef (...) ---
+
+ def add_line (
+ self, value, color, width=None, legend=None, extra_kwargs={}
+ ):
+ return self.add_arg (
+ RRDGraphArg_LINE (
+ value=value, color=color, width=width, legend=legend,
+ **extra_kwargs
+ )
+ )
+ # --- end of add_line (...) ---
+
+ def add_area (
+ self, value, color, legend=None, stack=None, skipscale=None
+ ):
+ return self.add_arg (
+ RRDGraphArg_AREA (
+ value=value, color=color, legend=legend,
+ stack=stack, skipscale=skipscale
+ )
+ )
+ # --- end of add_area (...) ---
+
+ def add_print ( self, name, format_str=None, inline=False ):
+ if inline:
+ return self.add_arg ( RRDGraphArg_GPRINT ( name, format_str ) )
+ else:
+ return self.add_arg ( RRDGraphArg_PRINT ( name, format_str ) )
+ # --- end of add_print (...) ---
+
+ def add_def_line (
+ self, name, ds_name, cf, color, step=None,
+ width=None, legend=None, line_kwargs={}
+ ):
+ self.add_def ( name, ds_name, cf, step )
+ self.add_line ( name, color, width, legend, line_kwargs )
+ # --- end of add_def_line (...) ---
+
+ def add_def_area (
+ self, name, ds_name, cf, color, step=None,
+ legend=None, stack=None, skipscale=None
+ ):
+ self.add_def ( name, ds_name, cf, step ),
+ self.add_area ( name, color, legend, stack, skipscale )
+ # --- end of add_def_area (...) ---
+
+ def add_option ( self, option, *values ):
+ if values:
+ for value in values:
+ self.extra_options.append ( option )
+ self.extra_options.append ( value )
+ else:
+ self.extra_options.append ( option )
+ # --- end of add_option (...) ---
+
+ def construct_args ( self ):
+ def gen_args():
+ yield self.GRAPH_COMMAND
+ yield '-'
+
+ for attr_name in self.OPTIONS_ATTR:
+ #attr = getattr ( self, attr_name, None )
+ attr = getattr ( self, attr_name )
+ if attr:
+ yield '--' + attr_name.replace ( '_', '-' )
+ yield str ( attr )
+ # -- end for
+
+ for color_spec in self.colors:
+ yield '--color'
+ yield str ( color_spec )
+
+ for arg in self.extra_options:
+ yield str ( arg )
+
+ for arg in self.args:
+ if isinstance ( arg, RRDGraphArg ):
+ yield arg.get_str ( self.rrd_db )
+ else:
+ yield str ( arg )
+ # --- end of gen_args (...) ---
+
+ return tuple ( gen_args() )
+ # --- end of construct_args (...) ---
+
+ def make ( self ):
+ def parse_output ( data, include_raw_data=False ):
+ # output format:
+ # <key> = <value>\n
+ # special case:
+ # image = BLOB_SIZE:<size>\n<image data>
+ #
+
+ WHITESPACE = ' '
+ EQUAL = '='
+ NEWLINE = '\n'
+
+ # mode / data receive "protocol":
+ # 0 -> read key,
+ # 1 -> have first whitespace,
+ # 2 -> have equal sign,
+ # 3 -> have second whitespace <=> want value,
+ # newline during 3 -> commit key/value, reset to mode 0
+ #
+ mode = 0
+ key = ""
+ value = None
+ get_chr = chr if sys.hexversion >= 0x3000000 else ( lambda b: b )
+
+
+ for index, b in enumerate ( data ):
+ c = get_chr ( b )
+ if mode == 0:
+ if c == WHITESPACE:
+ # done with key
+ mode += 1
+ else:
+ # append to key
+ key += c
+ elif mode == 1:
+ if c == EQUAL:
+ mode += 1
+ else:
+ raise Exception (
+ "expected equal sign after whitespace."
+ )
+ elif mode == 2:
+ if c == WHITESPACE:
+ mode += 1
+ value = ""
+ else:
+ raise Exception (
+ "expected whitesapce after equal sign."
+ )
+ elif c == NEWLINE:
+ mode = 0
+
+ if key == 'image':
+ img_size = int ( value.partition ( 'BLOB_SIZE:' ) [-1] )
+ img_data = data[index+1:index+img_size+1]
+ if len ( img_data ) == img_size:
+ yield ( "image_data", img_data )
+ else:
+ raise Exception (
+ "cannot read image (got {} out of {} bytes).".format (
+ len ( img_data ), img_size
+ )
+ )
+
+ yield ( "image_info", value )
+ yield ( "image_size", img_size )
+
+ # *** BREAK ***
+ break
+ else:
+ try:
+ if key in { 'value_min', 'value_max' }:
+ c_val = float ( value )
+ else:
+ c_val = int ( value )
+ ival = int ( value )
+ except ValueError:
+ yield ( key, value )
+ else:
+ yield ( key, c_val )
+
+ key = ""
+ value = None
+ else:
+ value += c
+ # -- end for
+
+ if include_raw_data:
+ yield ( "raw_data", data )
+
+ # --- end of parse_output (...) ---
+
+ retcode, output = self.rrd_db._call_rrdtool (
+ self.construct_args(), return_success=True, get_output=True,
+ binary_stdout=True,
+ )
+
+ if retcode == os.EX_OK:
+ self.image_data = dict ( parse_output ( output[0] ) )
+ return True
+ else:
+ # discard output
+ return False
+ # --- end of make (...) ---
+
+# --- end of RRDGraph ---
+
+class RRDGraphFactory ( object ):
+
+ def __init__ ( self, rrd_db, graph_kwargs=None ):
+ super ( RRDGraphFactory, self ).__init__()
+ self.rrd_db = rrd_db
+ self.graph_kwargs = dict() if graph_kwargs is None else graph_kwargs
+ self.default_args = list()
+ self.default_options = list()
+ # --- end of __init__ (...) ---
+
+ def get_new ( self ):
+ graph = RRDGraph ( self.rrd_db, **self.graph_kwargs )
+
+ if self.default_args:
+ graph.args.extend ( self.default_args )
+
+ if self.default_options:
+ graph.extra_options.extend ( self.default_options )
+
+ return graph
+ # --- end of get_new (...) ---
+
+# --- end of RRDGraphFactory ---
next reply other threads:[~2013-08-14 14:56 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-08-14 14:56 André Erdmann [this message]
-- strict thread matches above, loose matches on Subject: below --
2016-07-07 4:19 [gentoo-commits] proj/R_overlay:master commit in: roverlay/db/ Benda XU
2014-07-16 15:14 André Erdmann
2013-09-05 14:43 André Erdmann
2013-09-05 14:43 André Erdmann
2013-09-05 14:43 André Erdmann
2013-09-05 10:24 André Erdmann
2013-09-05 9:25 André Erdmann
2013-09-05 9:25 André Erdmann
2013-09-05 9:25 André Erdmann
2013-09-03 15:37 André Erdmann
2013-09-03 13:15 André Erdmann
2013-09-03 12:21 André Erdmann
2013-09-03 12:21 André Erdmann
2013-09-02 8:44 André Erdmann
2013-08-30 15:25 André Erdmann
2013-08-30 15:23 André Erdmann
2013-08-30 14:49 André Erdmann
2013-08-30 14:49 André Erdmann
2013-08-22 9:01 André Erdmann
2013-08-16 14:26 André Erdmann
2013-08-16 12:42 André Erdmann
2013-08-15 9:18 André Erdmann
2013-08-14 14:56 André Erdmann
2013-08-13 8:56 André Erdmann
2013-07-30 18:40 André Erdmann
2013-07-10 15:10 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-07-10 16:16 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-06-22 15:14 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-06-22 15:24 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1376491577.cd4a553f69b3da9519310ba5f528aac8541f5bd9.dywi@gentoo \
--to=dywi@mailerd.de \
--cc=gentoo-commits@lists.gentoo.org \
--cc=gentoo-dev@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox