rrd-procs.tcl
Procedures in this file
Detailed information
[
hide source ]
| [
make this
the default ]
Content File Source
::xo::library doc {
RRD-procs - nx based monitoring and graphing
@creation-date 2015-11-01
@author Gustaf Neumann
@cvs-id $Id: xowiki-procs.tcl,v 1.485.2.15 2015/10/22 08:28:42 gustafn Exp $
}
if {[catch {
package require Rrd
} errorMsg]} {
ns_log warning "Can't load Tcl package Rrd: $errorMsg"
namespace eval ::Rrd {
proc ::Rrd::create {args} {ns_log warning "Would call Rrd::create $args"}
proc ::Rrd::update {args} {ns_log warning "Would call Rrd::update $args"}
proc ::Rrd::lastupdate {args} {ns_log warning "Would call Rrd::lastupdate $args"}
}
}
namespace eval rrd {
nx::Class create ::rrd::VolatileObject {
:method init {} {
:destroy_on_cleanup
}
}
nx::Class create ::rrd::Graph -superclass ::rrd::VolatileObject {
:property filename
:property name
:property -incremental {elements:0..n ""}
:property {period seconds}
:property start
:property end
:property step
:property title
:property vertical-label
:property {width:integer 400}
:property {height:integer 175}
:property only-graph:switch
:property fill-size-mode:switch
:property upper-limit
:property lower-limit
:property rigid:switch
:property alt-autoscale:switch
:property alt-autoscale-min:switch
:property alt-autoscale-max:switch
:property no-gridfit:switch
:property x-grid
:property x-grid-none:switch
:property week-fmt
:property y-grid
:property y-grid-none:switch
:property left-axis-formatter
:property left-axis-format
:property alt-y-grid:switch
:property logarithmic:switch
:property units-exponent:integer
:property units-length:integer
:property right-axis
:property right-axis-label
:property right-axis-formatter
:property right-axis-format
:property no-legend:switch
:property force-rules-legend:switch
:property legend-position
:property legend-direction
:property lazy:switch
:property daemon
:property imginfo
:property {color:0..n {FONT#666666 ARROW#CFD6F8}} ;
:property grid-dash
:property border:integer
:property dynamic-labels:switch
:property zoom
:property {font:0..n {
"DEFAULT:0:DejaVuSans,DejaVu Sans,DejaVu LGC Sans,Bitstream Vera Sans"
LEGEND:7:monospace
TITLE:12
AXIS:8
UNIT:8
}}
:property font-render-mode
:property font-smoothing-threshold
:property pango-markup:switch
:property graph-render-mode
:property slope-mode:switch
:property {imgformat PNG}
:property interlaced:switch
:property tabwidth:integer
:property {base:integer 1000}
:property {watermark "nx-rrd 0.1"}
:property use-nan-for-all-missing-data:swtich
:method init {} {
next
if {![info exists name]} {
set :name [namespace tail [self]]
}
if {![info exists filename]} {
set :imgname ${:name}.[string tolower ${:imgformat}]
set :filename $::acs::rootdir/www/${:imgname}
}
}
:public method imgName {} {
return ${:imgname}
}
:public method render {} {
set defs [list ${:filename}]
foreach v [[current class] info variables] {
set varName [:info variable name $v]
if {$varName in {elements filename name period}} {
continue
}
if {[info exists :$varName]} {
switch -glob -- [lindex [:info variable parameter $v] 0] {
*:boolean {
if {[set :$varName]} {lappend defs --$varName}
}
*:0..n {
foreach e [set :$varName] {
lappend defs --$varName $e
}
}
default { lappend defs --$varName [set :$varName]}
}
}
}
switch ${:period} {
day {set scale 34560}
hours {set scale 1440}
minutes {set scale 60}
seconds -
default {set scale 1}
}
foreach e ${:elements} { $e configure -scale $scale }
lappend defs "COMMENT: \t\t Cur\tMin\t Avg\t Max\\l"
foreach e ${:elements} {
lappend defs {*}[$e render]
}
lassign [[$e cget -datasource] lastupdate] ds secs value
regsub -all {:} [clock format [string trimright $secs :]] {\:} ts
lappend defs "COMMENT:Last update\\: $ts\\r"
return $defs
}
}
nx::Class create ::rrd::Element -superclass ::rrd::VolatileObject {
}
nx::Class create ::rrd::DataElement -superclass ::rrd::Element {
:property name
:property datasource:required
:property {ds-name 42}
:property {scale 1}
:method init {} {
next
if {![info exists :name]} {
set :name [${:datasource} cget -name]
}
}
:public method render {} {
set rrdFile [${:datasource} cget -rrdFile]
regsub -all {[-]} ${:name} _ :name
lappend defs \
DEF:${:name}avg=${rrdFile}:${:ds-name}:AVERAGE \
DEF:${:name}cur=${rrdFile}:${:ds-name}:LAST \
DEF:${:name}min=${rrdFile}:${:ds-name}:MIN \
DEF:${:name}max=${rrdFile}:${:ds-name}:MAX \
CDEF:${:name}avgscaled=${:name}avg,${:scale},* \
CDEF:${:name}curscaled=${:name}cur,${:scale},* \
CDEF:${:name}minscaled=${:name}min,${:scale},* \
CDEF:${:name}maxscaled=${:name}max,${:scale},*
return $defs
}
:method gprintLegend {} {
return [list \
GPRINT:${:name}curscaled:LAST:%6.2lf%s\t \
GPRINT:${:name}minscaled:MIN:%6.2lf%s\t \
GPRINT:${:name}avgscaled:AVERAGE:%6.2lf%s\t \
GPRINT:${:name}maxscaled:MAX:%6.2lf%s\\l ]
}
}
nx::Class create ::rrd::DataLine -superclass ::rrd::DataElement {
:property {width 1}
:property color
:property legend
:public method render {} {
set defs [next]
if {[info exists :legend]} {
set legend "[format %-20s ${:legend}]\t"
} else {
set legend "[format %-20s ${:name}]\t"
}
lappend defs \
LINE${:width}:${:name}avgscaled#${:color}:$legend \
{*}[:gprintLegend]
return $defs
}
}
nx::Class create ::rrd::TrendLine -superclass ::rrd::DataElement {
:property {width 1}
:property color
:property {legend ""}
:property {window 1800}
:public method render {} {
if {[info exists :legend]} {
set legend "[format %-20s ${:legend}]\t"
} else {
set legend "[format %-20s ${:name}]\t"
}
lappend defs \
CDEF:${:name}trend=${:name}avgscaled,${:window},TREND \
LINE${:width}:${:name}trend#${:color}
return $defs
}
}
nx::Class create ::rrd::DataArea -superclass ::rrd::DataElement {
:property color
:property color2
:property legend
:property {gradheight 50}
:public method render {} {
set defs [next]
if {[info exists :legend]} {
set legend "[format %-20s ${:legend}]\t"
} else {
set legend "[format %-20s ${:name}]\t"
}
set colors ${:color}
if {[info exists :color2]} {append colors \#${:color2}}
lappend defs \
AREA:${:name}avgscaled#${colors}:$legend:gradheight=${:gradheight} \
{*}[:gprintLegend]
return $defs
}
}
nx::Class create ::rrd::DataSource {
:property name
:property baseResolution
:property rrdFile
:property {type DERIVE}
:method createRRDfile {} {
set durations [[:info parent] cget -durations]
foreach duration ${durations} {
set d [clock scan $duration -timezone :UTC -base 0]
set multiple($duration) [expr {$d / ${:baseResolution}}]
if {$multiple($duration) * ${:baseResolution} != $d} {
error "duration '$duration' is not a multiple of the base resolution ${:baseResolution} secs"
}
}
set RRA {}
foreach {res duration} ${durations} {
set m [expr {$multiple($duration)/$multiple($res)}]
if {$m * $multiple($res) != $multiple($duration)} {
error "duration '$duration' is not a multiple of the resolution $res"
}
lappend RRA \
"RRA:AVERAGE:0.5:$multiple($res):$m" \
"RRA:MIN:0.5:$multiple($res):$m" \
"RRA:MAX:0.5:$multiple($res):$m"
}
ns_log notice "try to create: Rrd::create ${:rrdFile} --step ${:baseResolution} DS:42:DERIVE:10m:0:U {*}$RRA"
Rrd::create ${:rrdFile} --step ${:baseResolution} "DS:42:${:type}:10m:0:U" {*}$RRA
}
:public method update {{-timestamp N} value} {
ns_log notice "Rrd::update ${:rrdFile} ${timestamp}:$value"
Rrd::update ${:rrdFile} ${timestamp}:$value
}
:public method lastupdate {} {
return [Rrd::lastupdate ${:rrdFile}]
}
:public method requireRRD {} {
if {![file exists ${:rrdFile}]} {
:createRRDfile
}
}
:public method deleteRRD {} {
if {[file exists ${:rrdFile}]} { file delete ${:rrdFile} }
}
:method init {} {
next
if {![info exists :name]} { set :name [namespace tail [self]] }
set :rrdFile [[:info parent] cget -rrdPath]_${:name}-d.rrd
append ::log "rrdFile of [self] is ${:rrdFile}<br>\n"
:requireRRD
}
}
nx::Class create ::rrd::Plugin {
:property vars
:property name
:property {rrdRoot /var/lib/munin/localdomain/localhost.localdomain}
:property rrdPath
:property {type DERIVE}
:property {durations {
"5 min" "2 days"
"30 min" "9 days"
"2 hours" "45 days"
"1 day" "450 days"
}}
:public object method update {plugins} {
foreach p $plugins {
if {[info commands $p] eq ""} {
ns_log warning "::rrd::Plugin: $p is not a command, ignore update"
continue
}
$p update
}
}
:method init {} {
next
if {![info exists :name]} { set :name [namespace tail [self]]}
set :rrdPath ${:rrdRoot}_${:name}
set :baseResolution [clock scan [lindex ${:durations} 0] -timezone :UTC -base 0]
foreach varName ${:vars} {
::rrd::DataSource create [self]::$varName -baseResolution ${:baseResolution} -type ${:type}
}
}
:public method baseResolution {} {
return ${:baseResolution}
}
:public method datasource {varName} {
if {[info commands [self]::$varName] ne ""} {
return [self]::$varName
} else {
error "no data source $varName exists for plugin ${:name}"
}
}
:public method datasources {} {
return [:info children -type ::rrd::DataSource]
}
:public method updateDataSource {varName value} {
[self]::$varName update $value
}
}
}
::xo::library source_dependent