Changeset 3404

Show
Ignore:
Timestamp:
03/07/10 13:45:47 (5 months ago)
Author:
steve.schnepp
Message:

- fixed & cleaned the cgi Expires/LastModified
- added a cgitmpdir to make the location of the temporary png configurable
- fixed comparison pages in CGI mode as (closes #831)
- hostname checks are case in-sensitive now : every hostname is converted to lowercase before use (fixes #450)
- adds an "aligned" suboption to update_rate. Enables to align RRD updates to granularity.
- Implementation of dirtyconfig (fixes #836)
- Converts the standard df plugin for dirtyconfig
- custom format for graph_data_size
- per field & plugin graph_data_size
- handle the last line of multiline options (fixes #855)
- Use a fqn to be able to CGI-graph multigraph plugins (should close #832)
- Initial add of update_rate. Still quite rough (uses tie() and files in /tmp)
- adds SSH transport (closes #842)
- Use Fast::CGI for CGI also
- use the CGI log file for the munin-graph process (fix perms issues)
- bugix the build system in case Munin::Defaults.pm is not already installed
- use directly GraphOld?.pm without invoking munin-graph
- add a custom response HTTP header to see how long it took to generate the graph if needed
- fixed the *4 to report the real number of graphs generated

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk

    • Property svn:ignore changed from
      *-stamp
      build
      *.class
      to
      *-stamp
      build
      *.class
      debian
  • trunk/Makefile

    r3265 r3404  
    3333        test clean \ 
    3434        clean-% test-% build-% install-% \ 
    35         tags 
     35        tags \ 
     36        infiles 
    3637 
    3738.SECONDARY: node/Build master/Build plugins/Build 
     
    8788        $(CHMOD) 0755 $(DBDIR) 
    8889 
    89         for p in master/www/*.tmpl master/www/*.png master/www/*.css resources/favicon.ico; do \ 
     90        for p in master/www/*.tmpl master/www/*.png master/www/*.css master/www/*.js resources/favicon.ico; do \ 
    9091                $(INSTALL) -m 0644 "$$p" $(CONFDIR)/templates/ ; \ 
    9192        done 
     
    9697 
    9798        $(INSTALL) -m 0644 master/www/definitions.html $(CONFDIR)/templates/ 
     99        $(INSTALL) -m 0644 master/www/dynazoom.html $(CONFDIR)/templates/ 
    98100        $(INSTALL) -m 0755 master/DejaVuSansMono.ttf $(LIBDIR)/ 
    99101        $(INSTALL) -m 0755 master/DejaVuSans.ttf $(LIBDIR)/ 
     
    108110        $(INSTALL) -m 0755 build/master/_bin/munin-html $(LIBDIR)/ 
    109111        $(INSTALL) -m 0755 build/master/_bin/munin-limits $(LIBDIR)/ 
    110         $(INSTALL) -m 0755 build/master/_bin/munin-cgi-graph $(CGIDIR)/ 
    111         $(INSTALL) -m 0755 build/master/_bin/munin-fastcgi-graph $(CGIDIR)/ 
     112        $(INSTALL) -m 0755 build/master/_bin/munin-cgi-graph $(CGIDIR)/munin-cgi-graph 
    112113 
    113114# Not ready to be installed yet  
     
    180181###################################################################### 
    181182 
     183# Dummy rule to enable parallel building 
     184infiles: $(INFILES) 
     185 
    182186ifeq ($(JCVALID),yes) 
    183 build: $(INFILES) build-master build-common-prime build-node build-plugins build-plugins-java build-man 
     187build: infiles build-master build-common-prime build-node build-plugins build-plugins-java build-man 
    184188else 
    185 build: $(INFILES) build-master build-common-prime build-node build-plugins build-man 
     189build: infiles build-master build-common-prime build-node build-plugins build-man 
    186190endif 
    187191 
     
    226230build-common-pre: common/Build 
    227231        cd common && $(PERL) Build code 
     232 
     233common/blib/lib/Munin/Common/Defaults.pm: common/lib/Munin/Common/Defaults.pm build-common-pre 
    228234        rm -f common/blib/lib/Munin/Common/Defaults.pm 
    229  
    230 common/blib/lib/Munin/Common/Defaults.pm: common/lib/Munin/Common/Defaults.pm build-common-pre 
    231235        $(PERL) -pe 's{(PREFIX     \s+=\s).*}{\1q{$(PREFIX)};}x;   \ 
    232236                  s{(CONFDIR    \s+=\s).*}{\1q{$(CONFDIR)};}x;     \ 
  • trunk/common

    • Property svn:ignore set to
      Build
      _build
      blib
  • trunk/common/lib/Munin/Common/Config.pm

    r3378 r3404  
    3838        "onlynullcdef", "group_order", "pipe", "pipe_command", 
    3939        "unknown_limit", "num_unknowns", "dropdownlimit", 
    40         "max_graph_jobs", "munin_cgi_graph_jobs" ); 
     40        "max_graph_jobs", "munin_cgi_graph_jobs", 
     41        "cgitmpdir", 
     42        ); 
    4143 
    4244my %bools = map { $_ => 1} qw(yes no true false on off 1 0); 
  • trunk/common/lib/Munin/Common/Defaults.pm

    r2951 r3404  
    2626our $MUNIN_HTMLDIR    = ''; 
    2727our $MUNIN_CGIDIR     = ''; 
     28our $MUNIN_CGITMPDIR     = ''; 
    2829our $MUNIN_DBDIR      = ''; 
    2930our $MUNIN_PLUGSTATE  = '';  
  • trunk/master

    • Property svn:ignore set to
      Build
      _build
      blib
  • trunk/master/_bin/munin-cgi-graph.in

    r3304 r3404  
    1 #!@@PERL@@ -Tw 
     1#!@@PERL@@ -Tw 
    22# -*- perl -*- 
    33# 
    4 # Copyright (C) 2004 Jimmy Olsen 
     4# Copyright (C) 2004-2009 Jimmy Olsen,  
    55# 
    66# This program is free software; you can redistribute it and/or 
     
    2020# $Id$ 
    2121# 
    22 # Please see http://munin.projects.linpro.no/wiki/CgiHowto for how to 
    23 # use this, and how to convert it to fastcgi which will improve speed. 
     22 
    2423# 
    2524 
     
    3231use POSIX qw(strftime); 
    3332use IPC::SysV qw(IPC_CREAT); 
     33use CGI::Fast; 
     34use CGI::Carp 'fatalsToBrowser'; 
    3435 
    3536my $GRAPHER = "$Munin::Common::Defaults::MUNIN_LIBDIR/munin-graph"; 
     
    5859my $dom   = ""; 
    5960my $lock  = ""; 
    60 my $IPC_KEY = 89340; 
    61  
     61my $IPC_KEY = 9340; 
     62 
     63# NOTE: You need to restart apache if the config file changes! 
    6264my $config = &munin_readconfig ($conffile); 
    6365 
    64 my $path = $ENV{PATH_INFO} || ""; 
    65 $path =~ s/^\///; 
    66 ($dom, $host, $serv) = split /\//, $path; 
    67 ($serv, $scale) = split /-/, $serv, 2; 
    68 $scale =~ s/\.png$//; 
    69  
    70 &verify_parameters ($dom, $host, $serv, $scale); 
    71  
    72 my $filename = get_picture_filename ($config, $dom, $host, $serv, $scale); 
    73  
    74 my $time = time; 
    75  
    76 # Get semaphore handle - Before graphing as recommended by snide. 
    77 my $semid = undef; 
    78  
    79 sem_setup(); 
    80  
    81 my $no_cache = defined($ENV{HTTP_CACHE_CONTROL}) && $ENV{HTTP_CACHE_CONTROL} =~ /no-cache/i; 
    82  
    83 if ($no_cache or (! &graph_usable($filename,$time) )) { 
    84     exit 0 unless draw_graph_or_complain($host, $serv, $TIMES{$scale}); 
    85     goto draw; 
    86 
    87  
    88 # At this time the file exists.  But may be old.  Or not. 
    89  
    90 my @stats         = stat ($filename); 
    91 my $last_modified = strftime ("%a, %d %b %Y %H:%M:%S %Z", localtime ($stats[9])); 
    92 # "Expires" has to use last modified time as base: 
    93 my $expires       = strftime ("%a, %d %b %Y %H:%M:%S GMT",  
    94                               gmtime($stats[9]+($period{$scale}-($stats[9]%$period{$scale})))); 
    95  
    96 # Check for If-Modified-Since and send 304 if not changed: 
    97 if (defined $ENV{HTTP_IF_MODIFIED_SINCE} and  
    98     !&modified ($ENV{HTTP_IF_MODIFIED_SINCE}, $stats[9]-1)) { 
    99     print "Status: 304\n"; 
     66# BEGIN FAST-CGI LOOP: 
     67while (new CGI::Fast) { 
     68    my $path = $ENV{PATH_INFO} || ""; 
     69    ($dom, $host, $serv, $scale) = $path =~ m#^/(.*)/([^/]+)/(\w+)-([\w=,]+)\.png#; ## avoid bug in vim 
     70 
     71    my $pinpoint; 
     72    if ($scale =~ /pinpoint=(\d+),(\d+)/) { 
     73            $pinpoint = [ $1, $2, ];  
     74    } 
     75   
     76    if (! &verify_parameters ($dom, $host, $serv, $scale)) { 
     77        print "Status: 500\n"; 
     78        print "Content-Type: text/html\n"; 
     79        print "\n"; 
     80        print "Invalid parameters!"; 
     81        next; 
     82    } 
     83   
     84    my $filename = get_picture_filename ($config, $dom, $host, $serv, $scale); 
     85   
     86    my $time = time; 
     87   
     88    # If a "Cache-Control: no-cache" header gets send, we regenerate the image in every case: 
     89    my $no_cache = $pinpoint || defined($ENV{HTTP_CACHE_CONTROL}) && $ENV{HTTP_CACHE_CONTROL} =~ /no-cache/i; 
     90    print STDERR "no_cache:$no_cache\n"; 
     91 
     92    # Compute the cache values 
     93    my $graph_ttl = $pinpoint ? 1 : $period{$scale}; 
     94    my $graph_last_expires = $time - $time % $graph_ttl; 
     95  
     96    my $graph_epoch = (! $no_cache) && file_newer_than($filename, $graph_last_expires); 
     97    if ($graph_epoch) { 
     98        # The graph is fresh enough. Sending either IMS if asked, or just skip generation  
     99        # Check for If-Modified-Since and send 304 if not changed: 
     100        if (defined $ENV{HTTP_IF_MODIFIED_SINCE} &&  
     101                ! rfctime_newer_than($ENV{HTTP_IF_MODIFIED_SINCE}, $graph_epoch)) { 
     102                print "Status: 304\n"; 
     103                print "Content-Type: image/png\n"; 
     104                print "\n"; 
     105                 
     106                # We replied, continue with the next request  
     107                next; 
     108        } 
     109    } else { 
     110        # Should generate it 
     111        my $scale_options; 
     112        if ($pinpoint) { 
     113                $scale_options = [ "--pinpoint=" . $pinpoint->[0] . "," . $pinpoint->[1] ]; 
     114        } else { 
     115                $scale_options = $TIMES{$scale}; 
     116        } 
     117        next unless draw_graph_or_complain($dom, $host, $serv, $scale_options, $filename, "$config->{logdir}/munin-cgi-graph.log"); 
     118    } 
     119 
     120    # At this time the file exists and should be served 
     121    my @stats       = stat ($filename); 
     122    my $mtime_epoch = $stats[9]; 
     123    my $last_modified = get_w3c_date_from_epoch($mtime_epoch); 
     124 
     125    # "Expires" has to use last modified time as base: 
     126    my $graph_next_expires = $mtime_epoch - ($mtime_epoch % $graph_ttl) + $graph_ttl; 
     127    my $expires       = get_w3c_date_from_epoch($graph_next_expires); 
     128     
     129 
     130    # Sending headers 
     131    print "Status: 200\n"; 
    100132    print "Content-Type: image/png\n"; 
    101     print "Expires: ", $expires, "\n"; 
    102     print "Last-Modified: $last_modified\n"; 
     133    print "Content-Length: $stats[7]\n"; 
     134 
     135    # Conditionaly add timing informations 
     136    print "Expires: $expires\n" if $expires; 
     137    print "Last-Modified: $last_modified\n" if $last_modified; 
    103138    print "\n"; 
    104     exit 0; 
    105 
    106  
    107 draw: 
    108  
    109     @stats = stat ($filename) unless @stats; 
    110  
    111 $last_modified = strftime ("%a, %d %b %Y %H:%M:%S %Z", localtime ($stats[9])) 
    112     unless defined($last_modified);  
    113  
    114 $expires       = strftime ("%a, %d %b %Y %H:%M:%S GMT",  
    115                              gmtime($stats[9]+($period{$scale}-($stats[9]%$period{$scale})))) 
    116     unless defined($expires); 
    117  
    118 print "Content-Type: image/png\n"; 
    119 print "Expires: ", strftime ("%a, %d %b %Y %H:%M:%S GMT", gmtime(time+($period{$scale}-($time%$period{$scale})))), "\n"; 
    120 print "Last-Modified: $last_modified\n"; 
    121 print "\n"; 
    122  
    123 &graph ($filename); 
    124  
    125 # # # # # # # # # # END OF MAIN 
    126  
    127 sub sem_setup { 
    128     # Try to police the number of concurrent rrdgraph instances.  
    129  
    130     # Fox kindly submitted a patch to convert to SysV IPC semaphores. 
    131     # Lovely! (ticket #499). 
    132  
    133     $semid = semget($IPC_KEY, 0, 0 ); 
    134  
    135     if(!defined($semid)) { 
    136         # Or create it if needed 
    137         $semid = semget($IPC_KEY, 1 , oct(666) | IPC_CREAT ); 
    138  
    139         die "Creating semaphore: $!" unless defined($semid); 
    140  
    141         my $max_cgi_graph_jobs = &munin_get ($config, "max_cgi_graph_jobs" , 6, $dom); 
    142  
    143         # And initialize to max_cgi_graph_jobs 
    144         my $opstring = pack("s!s!s!",0, $max_cgi_graph_jobs,0); 
    145         semop($semid,$opstring) || die "$!"; 
    146     } 
    147 
    148  
    149  
    150 sub sem_get { 
    151     # Call this before doing heavy work. 
    152     # Decrement, or lock/hang/yield if already 0 
    153     my $opstring = pack("s!s!s!",0, -1, 0); 
    154     semop($semid,$opstring); 
    155 
    156  
    157  
    158 sub sem_release { 
    159     # Call this after doing heavy work. 
    160     # Increment (and release waiting processes). 
    161     my $opstring = pack("s!s!s!",0, 1, 0); 
    162     semop($semid,$opstring); 
    163 
    164  
    165  
    166 sub graph { 
    167     # This just serves the file, no file is made. 
     139 
     140    # Sending graph data 
     141    send_graph_data($filename); 
     142
     143# END FAST-CGI LOOP 
     144 
     145sub get_w3c_date_from_epoch { 
     146        my $epoch = shift; 
     147        print STDERR "get_w3c_date_from_epoch($epoch)\n"; 
     148        return strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($epoch)); 
     149
     150 
     151sub send_graph_data { 
     152    # Serve the graph contents. 
    168153    my $filename = shift; 
    169154 
    170     open (my $GRAPH, '<', $filename)  
    171         or die "Warning: Could not open picture file \"$filename\" for reading: $!\n"; 
    172     print while (<$GRAPH>); 
    173     close ($GRAPH); 
     155    open (GRAPH_PNG_FILE, '<', $filename) or die "Warning: Could not open picture file \"$filename\" for reading: $!\n"; 
     156    print while (<GRAPH_PNG_FILE>); 
     157    close (GRAPH_PNG_FILE) 
    174158} 
    175159 
     
    182166    my $scale   = shift; 
    183167 
    184     return "$config->{'htmldir'}/$domain/$name/$service-$scale.png"; 
    185 
    186  
    187  
    188 sub logger_open { 
    189     my $dirname = shift; 
    190  
    191     if (!$log->opened) 
    192     { 
    193         unless (open ($log, '>>', "$dirname/munin-cgi-graph.log")) 
    194         { 
    195             print STDERR "Warning: Could not open log file \"$dirname/munin-cgi-graph.log\" for writing: $!"; 
    196         } 
    197     } 
     168    my $cgi_tmp_dir = $config->{cgitmpdir} || "/tmp/munin-cgi-tmp"; 
     169 
     170    return "$cgi_tmp_dir/$domain/$name/$service-$scale.png"; 
    198171} 
    199172 
     
    240213        { 
    241214                print STDERR "Warning: Request for graph without specifying domain. Bailing out.\n"; 
    242                 exit 1
     215                return 0
    243216        } 
    244217        if (!$host) 
    245218        { 
    246219                print STDERR "Warning: Request for graph without specifying host. Bailing out.\n"; 
    247                 exit 1
     220                return 0
    248221        } 
    249222        if (!$serv) 
    250223        { 
    251224                print STDERR "Warning: Request for graph without specifying service. Bailing out.\n"; 
    252                 exit 1
     225                return 0
    253226        } 
    254227 
     
    256229        { 
    257230                print STDERR "Warning: Request for graph without specifying scale. Bailing out.\n"; 
    258                 exit 1
     231                return 0
    259232        } 
    260233        else 
    261234        { 
    262                 if (!defined $TIMES{$scale}
     235                if (!defined $TIMES{$scale} && $scale !~ /pinpoint=\d+,\d+/
    263236                { 
    264237                        print STDERR "Warning: Weird scale setting \"$scale\". Bailing out.\n"; 
    265                         exit 1
     238                        return 0
    266239                } 
    267240        } 
    268 
    269  
    270  
    271 sub graph_usable { 
    272     # Check how old the graph is and return 1 if it's new enough and 0 otherwise. 
    273     my ($filename, $time) = @_; 
     241        return 1; 
     242
     243 
     244 
     245sub file_newer_than { 
     246    my $filename = shift; 
     247    my $time     = shift; 
    274248 
    275249    if (-f $filename) { 
    276250        my @stats = stat (_); 
    277         # $stats[9] holds the "last update" time and this needs to be in the last update period: 
    278         if ($stats[9] > ($time - $time%$period{$scale})) { 
    279 #print STDERR ("Graph unexpired for $scale. ($stats[9] , $time, ". ($time%$period{$scale}). ", ". ($time - $time%$period{$scale}). ").\n"); 
    280             return 1; 
     251        # $stats[9] holds the "last update" time and this needs  
     252        # to be in the last update period 
     253        my $last_update = $stats[9]; 
     254        if ($last_update > $time) { 
     255            return $last_update; 
    281256        } else { 
    282 #print STDERR ("Graph expired for $scale. ($stats[9] , $time, ". ($time%$period{$scale}). ", ". ($time - $time%$period{$scale}). ").\n"); 
    283257            return 0; 
    284258        } 
    285259    } 
     260 
     261    # No file found 
    286262    return 0; 
    287263} 
     
    289265 
    290266sub draw_graph { 
     267    # Draw a new graph - use semaphore to avoid too many concurrent munin-graph calls. 
     268    my $dom  = shift; 
    291269    my $host  = shift; 
    292270    my $serv  = shift; 
    293271    my $scale = shift; 
    294272 
    295     $serv =~ s{[^\w_/"'\[\]\(\)\+=-]}{_}g; $serv =~ /^(.+)$/; $serv = $1; #" 
     273    my $filename = shift; 
     274    my $logfile = shift; 
     275 
     276    $serv =~ s{[^\w_\/"'\[\]\(\)+=-]}{_}g; $serv =~ /^(.+)$/; $serv = $1; #" 
    296277    # . needs to be legal in host names 
    297     $host =~ s{[^\w_/"'\[\]\(\)\.+=-]}{_}g; $host =~ /^(.+)$/; $host = $1; #" 
    298  
    299     my @params = ($GRAPHER); 
     278    $host =~ s{[^\w_\/"'\[\]\(\)\.+=-]}{_}g; $host =~ /^(.+)$/; $host = $1; #" 
     279 
     280    my $fqn = "root/$dom/$host/$serv"; 
     281 
     282    my @params = (); 
    300283    push @params, @$scale; 
    301     push @params, "--skip-locking", "--skip-stats", "--nolazy", "--list-images"; 
    302     push @params, "--host", $host, "--service", $serv; 
    303     push @params, "STDERR>&STDOUT"; 
     284    push @params, "--host", $host, "--only-fqn", $fqn; 
     285    push @params, "--no-fork"; # FastCgi forks for us 
     286    push @params, "--skip-locking", "--skip-stats", "--nolazy"; 
     287    push @params, "--output-file", $filename; 
     288    push @params, "--log-file", $logfile; 
     289 
     290    push @params, "--size_x", CGI::param("size_x") if (CGI::param("size_x")); 
     291    push @params, "--size_y", CGI::param("size_y") if (CGI::param("size_y")); 
     292 
     293    push @params, "--upper_limit", CGI::param("upper_limit") if (CGI::param("upper_limit")); 
     294    push @params, "--lower_limit", CGI::param("lower_limit") if (CGI::param("lower_limit")); 
     295 
     296 
    304297 
    305298    my $file = "/dev/null"; 
    306     my $IN; 
    307     sem_get(); 
    308  
    309     # Note: This open is an implicit fork 
    310     if (! open ($IN, "-|")) {  
    311         %ENV=(); 
    312         exec @params; 
    313     } 
    314     $file = join (' ', <$IN>); 
    315     chomp($file); 
    316  
    317     close ($IN); 
    318     sem_release(); 
    319  
    320     return $file; 
     299 
     300    # Use directly GraphOld; 
     301    use Munin::Master::GraphOld; 
     302 
     303    graph_startup(\@params); 
     304    graph_main(); 
     305 
     306    return $filename; 
    321307} 
    322308 
    323309 
    324310sub draw_graph_or_complain { 
     311    use Time::HiRes qw(gettimeofday tv_interval); 
     312    my $t0 = [ gettimeofday ]; 
    325313    my $ret = draw_graph(@_); 
     314    my $graph_duration = tv_interval($t0); 
    326315 
    327316    if (! -f $ret) { 
    328         ::logger ("Warning: Could not draw graph \"$host-$serv-$scale.png\": $ret"); 
     317        ::logger("Warning: Could not draw graph \"$host-$serv-$scale.png\": $ret"); 
    329318        print "Status: 500\n"; 
    330319        print "Content-Type: image/png\n"; 
    331320        print "\n"; 
    332        return 0; 
     321        return 0; 
    333322    } else { 
     323        print "X-Graph-Duration: $graph_duration\n"; 
    334324        return $ret; 
    335325    } 
     
    337327 
    338328 
    339 sub modified { 
    340     # See if file has been modified since "the last time". 
    341  
     329sub rfctime_newer_than { 
     330    # See if the file has been modified since "the last time". 
    342331    # Format of since_string If-Modified-Since: Wed, 23 Jun 2004 16:11:06 GMT 
    343  
    344332    my $since_string = shift; 
    345333    my $created      = shift; 
  • trunk/master/lib/Munin/Master/GraphOld.pm

    r3288 r3404  
    8787my $cron           = 0; 
    8888my $list_images    = 0; 
     89my $output_file    = undef; 
     90my $log_file       = undef; 
    8991my $skip_locking   = 0; 
    9092my $skip_stats     = 0; 
     
    100102    "sumweek" => 1 
    101103); 
     104$draw{"pinpoint"} = 0; # XXX - Add a dummy pinpoint value 
     105 
     106my ($size_x, $size_y); 
     107my ($lower_limit, $upper_limit); 
    102108 
    103109my %PALETTE;    # Hash of available palettes 
     
    129135    "week"  => "-8d", 
    130136    "month" => "-33d", 
    131     "year"  => "-400d" 
     137    "year"  => "-400d", 
     138    "pinpoint"  => "dummy", 
    132139); 
    133140 
     
    146153my @limit_hosts    = (); 
    147154my @limit_services = (); 
     155my $only_fqn; 
    148156 
    149157my $watermark = "Munin " . $Munin::Common::Defaults::MUNIN_VERSION; 
     
    158166# stats file handle 
    159167my $STATS; 
    160 my $DEBUG; 
     168my $DEBUG = 0; 
     169my $pinpoint = undef; 
     170 
     171my %init_draw = %draw; 
     172my @init_limit_hosts = @limit_hosts; 
     173my @init_limit_services = @limit_services; 
    161174 
    162175sub graph_startup { 
     
    166179    # Do once pr. run, pr possebly once pr. graph in the case of 
    167180    # munin-cgi-graph 
     181     
     182    # Localise the stuff, overwise it will be stacked up with CGI 
     183    %draw = %init_draw; 
     184    @limit_hosts = @init_limit_hosts; 
     185    @limit_services = @init_limit_services; 
    168186 
    169187    # Get options 
     
    176194                "host=s"        => \@limit_hosts, 
    177195                "service=s"     => \@limit_services, 
     196                "only-fqn=s"     => \$only_fqn, 
    178197                "config=s"      => \$conffile, 
    179198                "stdout!"       => \$stdout, 
     
    182201                "month!"        => \$draw{'month'}, 
    183202                "year!"         => \$draw{'year'}, 
     203                "pinpoint=s"    => \$draw{'pinpoint'}, 
    184204                "sumweek!"      => \$draw{'sumweek'}, 
    185205                "sumyear!"      => \$draw{'sumyear'}, 
     206                "size_x=i"      => \$size_x, 
     207                "size_y=i"      => \$size_y, 
     208                "upper_limit=s" => \$upper_limit, 
     209                "lower_limit=s" => \$lower_limit, 
    186210                "list-images!"  => \$list_images, 
     211                "o|output-file=s"  => \$output_file, 
     212                "l|log-file=s"  => \$log_file, 
    187213                "skip-locking!" => \$skip_locking, 
    188214                "skip-stats!"   => \$skip_stats, 
     
    207233    $config = &munin_config($conffile); 
    208234 
    209     logger_open($config->{'logdir'}); 
     235    # untaint the $log_file variable 
     236    $log_file = $1 if ($log_file && $log_file =~ m/(.*)/); 
     237 
     238    logger_open($config->{'logdir'}, $log_file); 
    210239    logger_debug() if $DEBUG; 
     240     
     241    # XXX - Special hack^h^h^h^h treatment for --pinpoint 
     242    if ($draw{'pinpoint'} && $draw{'pinpoint'} =~ m/^(\d+),(\d+)$/ ) { 
     243            %draw = ( "pinpoint" => $draw{'pinpoint'} ); # "pinpoint" replaces all the other timing options 
     244            $pinpoint = { "start" => $1, "end" => $2, }; # preparsed values 
     245    } 
    211246 
    212247    my $palette = &munin_get($config, "palette", "default"); 
     
    282317    my $scale   = shift; 
    283318 
    284     return (munin_get($service, "graph_title", $service) . " - by $scale"); 
     319    my $scale_text; 
     320    if ($pinpoint) { 
     321            my $start_text = localtime($pinpoint->{"start"}); 
     322            my $end_text = localtime($pinpoint->{"end"}); 
     323            $scale_text = "from $start_text to $end_text"; 
     324    } else { 
     325        $scale_text = "by " . $scale; 
     326    } 
     327 
     328    return (munin_get($service, "graph_title", $service) . " - $scale_text"); 
    285329} 
    286330 
     
    330374 
    331375    # Picture filename 
    332     push @$result, munin_get_picture_filename($service, $scale, $sum || undef); 
     376    push @$result, get_picture_filename($service, $scale, $sum || undef); 
    333377 
    334378    # Title 
     
    336380 
    337381    # When to start the graph 
    338     push @$result, "--start", $times{$scale}; 
     382    if ($pinpoint) { 
     383        push @$result, "--start", $pinpoint->{start}; 
     384        push @$result, "--end", $pinpoint->{end}; 
     385    } else { 
     386        push @$result, "--start", $times{$scale}; 
     387    } 
    339388 
    340389    # Custom graph args, vlabel and graph title 
     
    346395    } 
    347396 
    348     push @$result, "--height", munin_get($service, "graph_height", "175"); 
    349     push @$result, "--width",  munin_get($service, "graph_width",  "400"); 
     397    push @$result, "--height", ($size_y || munin_get($service, "graph_height", "175")); 
     398    push @$result, "--width",  ($size_x || munin_get($service, "graph_width",  "400")); 
     399 
     400    push @$result,"--rigid" if (defined $lower_limit || defined $upper_limit); 
     401 
    350402    push @$result, "--imgformat", "PNG"; 
    351403    push @$result, "--lazy" if ($force_lazy); 
     
    10881140    } 
    10891141 
     1142    my $nb_graphs_drawn = 0; 
    10901143    for my $time (keys %times) { 
    10911144        next unless ($draw{$time}); 
    1092         my $picfilename = munin_get_picture_filename($service, $time); 
     1145        my $picfilename = get_picture_filename($service, $time); 
    10931146        (my $picdirname = $picfilename) =~ s/\/[^\/]+$//; 
    10941147 
     
    11191172                . "\\r"); 
    11201173 
    1121         if (time - 300 < $lastupdate) { 
     1174        if (time - 300 < $lastupdate && ! $pinpoint) { 
    11221175            if (@added) { # stop one period earlier if it's a .sum or .stack 
    11231176                push @complete, "--end", 
     
    11461199        } 
    11471200 
     1201        # Surcharging the graphing limits 
     1202        my ($upper_limit_overrided, $lower_limit_overrided); 
     1203        for (my $index = 0; $index <= $#complete; $index++) { 
     1204                if ($complete[$index] =~ /^(--upper-limit|-u)$/ && (defined $upper_limit)) { 
     1205                        $upper_limit = get_scientific($upper_limit); 
     1206                        $complete[$index + 1] = $upper_limit; 
     1207                        $upper_limit_overrided = 1; 
     1208                } 
     1209                if ($complete[$index] =~ /^(--lower-limit|-l)$/ && (defined $lower_limit)) { 
     1210                        $lower_limit = get_scientific($lower_limit); 
     1211                        $complete[$index + 1] = $lower_limit; 
     1212                        $lower_limit_overrided = 1; 
     1213                } 
     1214        } 
     1215 
     1216        # Add the limit if not present 
     1217        if (defined $upper_limit && ! $upper_limit_overrided) { 
     1218                push @complete, "--upper-limit", $upper_limit; 
     1219        } 
     1220        if (defined $lower_limit && ! $lower_limit_overrided) { 
     1221                push @complete, "--lower-limit", $lower_limit; 
     1222        } 
     1223 
     1224        $nb_graphs_drawn ++; 
    11481225        RRDs::graph(@complete); 
    11491226        if (my $ERROR = RRDs::error) { 
     
    11621239 
    11631240            # utime $lastupdate, $lastupdate, 
    1164             # munin_get_picture_filename($service, $time); 
     1241            # et_picture_filename($service, $time); 
    11651242 
    11661243            if ($list_images) { 
    11671244                # Command-line option to list images created 
    1168                 print munin_get_picture_filename ($service, $time), "\n"; 
     1245                print get_picture_filename ($service, $time), "\n"; 
    11691246            } 
    11701247        } 
     
    11731250    if (munin_get_bool($service, "graph_sums", 0)) { 
    11741251        foreach my $time (keys %sumtimes) { 
    1175             my $picfilename = munin_get_picture_filename($service, $time, 1); 
     1252            my $picfilename = get_picture_filename($service, $time, 1); 
    11761253            (my $picdirname = $picfilename) =~ s/\/[^\/]+$//; 
    11771254            next unless ($draw{"sum" . $time}); 
     
    11791256            push @rrd_sum, @{get_header($service, $time, 1)}; 
    11801257 
    1181             if (time - 300 < $lastupdate) { 
     1258            if (time - 300 < $lastupdate && ! $pinpoint) { 
    11821259                if (@added) { # stop 5 minutes earlier if it's a .sum or .stack 
    11831260                    push @rrd_sum, "--end", 
     
    12711348            # Make sure directory exists 
    12721349            munin_mkdir_p($picdirname, oct(777)); 
    1273  
     1350     
     1351            $nb_graphs_drawn ++; 
    12741352            RRDs::graph(@rrd_sum); 
    12751353 
    12761354            if (my $ERROR = RRDs::error) { 
    12771355                ERROR "[RRD ERROR(sum)] Unable to graph " 
    1278                     . munin_get_picture_filename($service, $time) 
     1356                    . get_picture_filename($service, $time) 
    12791357                    . ": $ERROR"; 
    12801358            } 
    12811359            elsif ($list_images) { 
    12821360                # Command-line option to list images created 
    1283                 print munin_get_picture_filename ($service, $time, 1), "\n"; 
     1361                print get_picture_filename ($service, $time, 1), "\n"; 
    12841362            } 
    12851363        } # foreach (keys %sumtimes) 
     
    12871365 
    12881366    $service_time = sprintf("%.2f", (Time::HiRes::time - $service_time)); 
    1289     INFO "Graphed service : $sname ($service_time sec * 4)"; 
     1367    INFO "Graphed service : $sname ($service_time sec for $nb_graphs_drawn graphs)"; 
    12901368    print $STATS "GS|$service_time\n" unless $skip_stats; 
    12911369 
     
    13401418} 
    13411419 
     1420sub ends_with { 
     1421        my ($src, $searched) = @_; 
     1422        DEBUG "[DEBUG] ends_with($src, $searched)\n"; 
     1423 
     1424        my $is_ending = (substr($src, - length($searched)) eq $searched); 
     1425        return $is_ending;  
     1426} 
     1427 
    13421428 
    13431429sub skip_service { 
    13441430    my $service = shift; 
    1345     my $sname   = munin_get_node_name($service); 
     1431    my $fqn   = munin_get_node_fqn($service); 
     1432 
     1433    # Skip if we've limited services with the omnipotent cli option only-fqn 
     1434    return 1 if ($only_fqn and ! ends_with($fqn, $only_fqn)); 
     1435    DEBUG "[DEBUG] $fqn is in ($only_fqn)\n"; 
    13461436 
    13471437    # Skip if we've limited services with cli options 
    1348     return 1 if (@limit_services and !grep /^$sname$/, @limit_services); 
     1438    return 1 if (@limit_services and ! (grep { ends_with($fqn, $_) } @limit_services)); 
     1439    DEBUG "[DEBUG] $fqn is in (" . join(",", @limit_services) . ")\n"; 
    13491440 
    13501441    # Always graph if --force is present 
     
    14071498} 
    14081499 
     1500# Wrapper for munin_get_picture_filename to handle pinpoint 
     1501sub get_picture_filename { 
     1502        if (defined $output_file) { return $output_file; } 
     1503        # delegate if not overriden 
     1504        return munin_get_picture_filename(@_); 
     1505} 
     1506 
    14091507sub escape { 
    14101508    my $text = shift; 
     
    14131511    $text =~ s/:/\\:/g; 
    14141512    return $text; 
     1513} 
     1514 
     1515sub get_scientific { 
     1516        my $value = shift; 
     1517        $value =~ s/m/e-03/; 
     1518        $value =~ s/k/e+03/; 
     1519        $value =~ s/M/e+06/; 
     1520        $value =~ s/G/e+09/; 
     1521        return $value; 
    14151522} 
    14161523 
     
    14441551    --[no]list-images   List the filenames of the images created.  
    14451552                        [--nolist-images] 
     1553    --output-file  -o   Output graph file. (used for CGI graphing) 
     1554    --log-file     -l   Output log file. (used for CGI graphing) 
    14461555    --[no]day           Create day-graphs.   [--day] 
    14471556    --[no]week          Create week-graphs.  [--week] 
     
    14511560    --[no]sumyear       Create summarised year-graphs.  [--sumyear] 
    14521561 
     1562    --pinpoint <start,stop> Create custom-graphs. <start,stop> is the standard unix Epoch. [not active] 
     1563    --size_x <pixels>   Sets the X size of the graph in pixels [175] 
     1564    --size_y <pixels>   Sets the Y size of the graph in pixels [400] 
     1565    --lower_limit <lim> Sets the lower limit of the graph 
     1566    --upper_limit <lim> Sets the upper limit of the graph 
     1567 
    14531568"; 
    14541569    exit 0; 
  • trunk/master/lib/Munin/Master/HTMLOld.pm

    r3302 r3404  
    415415    # NOTE: The templates have hardcoded path to definitions.html, and it is not right, esp. when 
    416416    # we have nested groups and nested services. 
    417     my @files = ("style.css", "logo.png", "logo-h.png", "definitions.html", "favicon.ico"); 
     417    my @files = ( 
     418            "style.css", "logo.png", "logo-h.png", "definitions.html", "favicon.ico", 
     419            "dynazoom.html", "formatdate.js", "querystring.js", 
     420    ); 
    418421 
    419422    foreach my $file ((@files)) { 
     
    933936        $srv{'imgyear'}  = $config->{'cgiurl_graph'} . "/$path-year.png"; 
    934937 
     938        $srv{'cimgday'}   = $config->{'cgiurl_graph'} . "/$path-day.png"; 
     939        $srv{'cimgweek'}  = $config->{'cgiurl_graph'} . "/$path-week.png"; 
     940        $srv{'cimgmonth'} = $config->{'cgiurl_graph'} . "/$path-month.png"; 
     941        $srv{'cimgyear'}  = $config->{'cgiurl_graph'} . "/$path-year.png"; 
     942 
    935943        if (munin_get_bool($service, "graph_sums", 0)) { 
    936944            $srv{'imgweeksum'} 
     
    957965    } 
    958966 
     967    # Compute the ZOOM urls 
     968    { 
     969        my $epoch_now = time; 
     970        # The intervals are a bit larger, just like the munin-graph 
     971        my $start_day = $epoch_now - (3600 * 30); 
     972        my $start_week = $epoch_now - (3600 * 24 * 8); 
     973        my $start_month = $epoch_now - (3600 * 24 * 33); 
     974        my $start_year = $epoch_now - (3600 * 24 * 400); 
     975        my $size_x = 800; 
     976        my $size_y = 400; 
     977        my $common_url = "$root_path/dynazoom.html?plugin_name=$path&size_x=$size_x&size_y=$size_y"; 
     978        $srv{zoomday} = "$common_url&start_epoch=$start_day&stop_epoch=$epoch_now"; 
     979        $srv{zoomweek} = "$common_url&start_epoch=$start_week&stop_epoch=$epoch_now"; 
     980        $srv{zoommonth} = "$common_url&start_epoch=$start_month&stop_epoch=$epoch_now"; 
     981        $srv{zoomyear} = "$common_url&start_epoch=$start_year&stop_epoch=$epoch_now"; 
     982    } 
     983 
    959984    for my $scale (@times) { 
     985        # Don't try to find the size if cgi is enabled,  
     986        # otherwise old data might pollute   
     987        next if ($method eq "cgi"); 
    960988        if (my ($w, $h) 
    961989            = get_png_size(munin_get_picture_filename($service, $scale))) { 
     
    969997        $srv{imgyearsum} = "$srv{node}-year-sum.png"; 
    970998        for my $scale (["week", "year"]) { 
     999            next if ($method eq "cgi"); 
    9711000            if (my ($w, $h) 
    9721001                = get_png_size(munin_get_picture_filename($service, $scale, 1))) 
  • trunk/master/lib/Munin/Master/Logger.pm

    r3091 r3404  
    7373my $logdir = undef; 
    7474my $logopened = 0; 
    75 my $me = basename($PROGRAM_NAME); 
     75my $me = $1 if basename($PROGRAM_NAME) =~ m/(.*)/; # Fast untaint $PROGRAM_NAME 
    7676 
    7777sub _warn_catcher { 
     
    9393    } 
    9494 
     95    my $log_filename = shift || "$dirname/$me.log"; 
     96 
    9597    if (!$logopened) { 
    9698        # I'm a bit uncertain about the :utf8 bit. 
    9799        Log::Log4perl->easy_init( { level    => $INFO, 
    98                                     file     => ":utf8>>$dirname/$me.log" } ); 
     100                                    file     => ":utf8>>$log_filename" } ); 
    99101        # warn "Logging to $dirname/$me.log"; 
    100102        $logopened = 1; 
  • trunk/master/lib/Munin/Master/Node.pm

    r3271 r3404  
    2929        host    => $host, 
    3030        tls     => undef, 
    31         socket  => undef, 
    32         master_capabilities => qw(multigraph), 
     31        reader  => undef, 
     32        writer  => undef, 
     33        master_capabilities => "multigraph dirtyconfig", 
    3334        io_timeout => 120, 
    3435        configref => $configref, 
     
    5960        if !defined($self->{address}); 
    6061 
    61     if (! ( $self->{socket} = IO::Socket::INET->new( 
    62                 PeerAddr  => $self->{address}, 
    63                 PeerPort  => $self->{port}, 
     62    # Check if it's an URI or a plain host 
     63    use URI; 
     64 
     65    my $uri = new URI($self->{address}); 
     66 
     67    # If the scheme is not defined, it's a plain host.  
     68    # Prefix it with munin:// to be able to parse it like others 
     69    $uri = new URI("munin://" . $self->{address}) unless $uri->scheme; 
     70    LOGCROAK("[FATAL] '$self->{address}' is not a valid address!") unless $uri->scheme; 
     71 
     72    if ($uri->scheme eq "munin") { 
     73        $self->{reader} = $self->{writer} = IO::Socket::INET->new( 
     74                PeerAddr  => $uri->host, 
     75                PeerPort  => ($uri->port || $self->{port}), 
    6476                LocalAddr => $config->{local_address}, 
    6577                Proto     => 'tcp',  
    66                 Timeout   => $config->{timeout}) ) ) { 
    67         ERROR "Failed to connect to node $self->{address}:$self->{port}/tcp : $!"; 
    68         return 0; 
     78                Timeout   => $config->{timeout} 
     79        ); 
     80        if (! $self->{reader} ) { 
     81                ERROR "Failed to connect to node $self->{address}:$self->{port}/tcp : $!"; 
     82                return 0; 
     83        } 
     84    } elsif ($uri->scheme eq "ssh") { 
     85            my $user_part = ($uri->user) ? ($uri->user . "@") : ""; 
     86            my $remote_connection_cmd = "/usr/bin/ssh $user_part" . $uri->host ." " . $uri->path; 
     87            # Open a double pipe 
     88            use IPC::Open2; 
     89 
     90            $self->{reader} = new IO::Handle(); 
     91            $self->{writer} = new IO::Handle(); 
     92 
     93            my $pid = open2($self->{reader}, $self->{writer}, $remote_connection_cmd); 
     94            ERROR "Failed to connect to node $self->{address} : $!" unless $pid; 
     95    } else { 
     96            ERROR "Unknown scheme : " . $uri->scheme; 
     97            return 0; 
    6998    } 
    7099 
     
    98127        DEBUG        => $config->{debug}, 
    99128        logger       => \&logger, 
    100         read_fd      => fileno($self->{socket}), 
     129        read_fd      => fileno($self->{reader}), 
    101130        read_func    => sub { _node_read_single($self) }, 
    102131        tls_ca_cert  => $config->{tls_ca_certificate}, 
     
    106135        tls_vdepth   => $config->{tls_verify_depth}, 
    107136        tls_verify   => $config->{tls_verify_certificate}, 
    108         write_fd     => fileno($self->{socket}), 
     137        write_fd     => fileno($self->{writer}), 
    109138        write_func   => sub { _write_socket_single($self, @_) }, 
    110139    }); 
     
    122151    my ($self) = @_; 
    123152 
    124     close $self->{socket}; 
    125     $self->{socket} = undef; 
     153    close $self->{reader}; 
     154    close $self->{writer}; 
     155    $self->{reader} = undef; 
     156    $self->{writer} = undef; 
    126157} 
    127158 
     
    437468        } 
    438469        else { 
    439             print { $self->{socket} } $text; 
     470            print { $self->{writer} } $text; 
    440471        } 
    441472    }); 
     
    457488      } 
    458489      else { 
    459           $res = readline $self->{socket}; 
     490          $res = readline $self->{reader}; 
    460491      } 
    461492      chomp $res if defined $res; 
     
    483514            my $line = $self->{tls} && $self->{tls}->session_started() 
    484515                ? $self->{tls}->read() 
    485                 : readline $self->{socket}; 
     516                : readline $self->{reader}; 
    486517            last unless defined $line; 
    487518            last if $line =~ /^\.\n$/; 
     
    496527    return @array; 
    497528} 
     529 
     530# Defines the URL::scheme for munin 
     531package URI::munin; 
     532 
     533# We are like telnet 
     534require URI::telnet; 
     535@URI::munin::ISA=qw(URI::telnet); 
     536 
     537# munin://HOST[:PORT] 
     538 
     539sub default_port { 4949 } 
    498540 
    4995411; 
  • trunk/master/lib/Munin/Master/UpdateWorker.pm

    r3254 r3404  
    8484                } 
    8585 
    86                 my %service_data = $self->{node}->fetch_service_data($plugin); 
     86                # Check if this plugin has already sent its data via a dirtyconfig 
     87                my %service_data = $self->handle_dirty_config(\%service_config); 
     88 
     89                # Check if this plugin has to be updated 
     90                my $update_rate = get_global_service_value(\%service_config, $plugin, "update_rate", 0);  
     91                my ($update_rate_in_seconds, $is_update_aligned) = parse_update_rate($update_rate); 
     92                # default is 0 sec : always update when asked 
     93                DEBUG "[DEBUG] update_rate $update_rate_in_seconds for $plugin on $nodedesignation"; 
     94                if ($update_rate_in_seconds  
     95                        && is_fresh_enough($nodedesignation, $plugin, $update_rate_in_seconds)) { 
     96                    # It's fresh enough, skip this $service 
     97                    DEBUG "[DEBUG] $plugin is fresh enough, not updating it"; 
     98                    next; 
     99                } 
     100 
     101                if (! %service_data) { 
     102                        %service_data = $self->{node}->fetch_service_data($plugin); 
     103                } 
     104 
     105                # If update_rate is aligned, round the "when" for alignement 
     106                if ($is_update_aligned) { 
     107                        foreach my $service (keys %service_data) { 
     108                                my $current_service_data = $service_data{$service}; 
     109                                foreach my $field (keys %$current_service_data) { 
     110                                        my $when = $current_service_data->{$field}->{when}; 
     111                                        my $rounded_when = round_to_granularity($when, $update_rate_in_seconds); 
     112                                        $current_service_data->{$field}->{when} = $rounded_when; 
     113                                } 
     114                        } 
     115                } 
     116 
    87117 
    88118                # Since different plugins can populate multiple 
     
    148178        service_configs => \%all_service_configs, 
    149179    } 
     180} 
     181 
     182sub get_global_service_value { 
     183        my ($service_config, $service, $conf_field_name, $default) = @_; 
     184        foreach my $array (@{$service_config->{global}{$service}}) { 
     185                my ($field_name, $field_value) = @$array; 
     186                if ($field_name eq $conf_field_name) { 
     187                        return $field_value; 
     188                } 
     189        } 
     190 
     191        return $default; 
     192} 
     193 
     194sub is_fresh_enough { 
     195        my ($nodedesignation, $service, $update_rate_in_seconds) = @_; 
     196 
     197        my $key = "$nodedesignation/$service"; 
     198        DEBUG "is_fresh_enough asked for $key with a rate of $update_rate_in_seconds"; 
     199 
     200        my %last_updated; 
     201        # XXX - ugly hack. Should be refactored to use a a common state provider 
     202 
     203        use Fcntl;   # For O_RDWR, O_CREAT, etc. 
     204        use NDBM_File; 
     205        tie(%last_updated, 'NDBM_File', '/tmp/munin_plugins_last_updated', O_RDWR|O_CREAT, 0666) or ERROR "$!"; 
     206        DEBUG "last_updated{$key}: " . $last_updated{$key}; 
     207        my @last = split(/ /, $last_updated{$key}); 
     208    
     209        use Time::HiRes qw(gettimeofday tv_interval);    
     210        my $now = [ gettimeofday ]; 
     211 
     212        my $age = tv_interval(\@last, $now);     
     213        DEBUG "last: " . Dumper(\@last) . ", now: " . Dumper($now) . ", age: $age"; 
     214        my $is_fresh_enough = ($age < $update_rate_in_seconds); 
     215        DEBUG "is_fresh_enough  $is_fresh_enough"; 
     216 
     217        if (! $is_fresh_enough) { 
     218                DEBUG "new value: " . join(" ", @$now); 
     219                $last_updated{$key} = join(" ", @$now); 
     220        } 
     221 
     222        untie(%last_updated); 
     223 
     224        return $is_fresh_enough; 
     225} 
     226 
     227sub parse_update_rate { 
     228        my ($update_rate_config) = @_; 
     229 
     230        my ($is_update_aligned, $update_rate_in_sec); 
     231        if ($update_rate_config =~ m/(\d+[a-z]?) (aligned)?/) { 
     232                $update_rate_in_sec = to_sec($1); 
     233                $is_update_aligned = $2; 
     234        } else { 
     235                return (0, 0); 
     236        } 
     237 
     238        return ($update_rate_in_sec, $is_update_aligned); 
     239} 
     240 
     241sub round_to_granularity { 
     242        my ($when, $granularity_in_sec) = @_; 
     243        $when = time if ($when eq "N"); # N means "now" 
     244 
     245        my $rounded_when = $when - ($when % $granularity_in_sec); 
     246        return $rounded_when; 
     247} 
     248 
     249sub handle_dirty_config { 
     250        my ($self, $service_config) = @_; 
     251         
     252        my %service_data; 
     253 
     254        my $services = $service_config->{global}{multigraph}; 
     255        foreach my $service (@$services) { 
     256                my $service_data_source = $service_config->{"data_source"}->{$service}; 
     257                foreach my $field (keys %$service_data_source) { 
     258                        my $field_value = $service_data_source->{$field}->{"value"}; 
     259                        # If not present, ignore 
     260                        next if (! defined $field_value); 
     261 
     262                        DEBUG "[DEBUG] handle_dirty_config:$service, $field, $field_value"; 
     263                        # Moves the "value" to the service_data 
     264                        $service_data{$service}->{$field} = { 
     265                                "value" => $field_value, 
     266                                "when" => "N", 
     267                        }; 
     268 
     269                        delete($service_data_source->{$field}{value}); 
     270                } 
     271        } 
     272 
     273        return %service_data; 
    150274} 
    151275 
     
    345469        for my $ds_name (keys %{$service_config}) { 
    346470            $self->_set_rrd_data_source_defaults($service_config->{$ds_name}); 
    347  
    348             unless (defined($service_config->{$ds_name}{label})) { 
     471            my $ds_config = $service_config->{$ds_name}; 
     472 
     473            unless (defined($ds_config->{label})) { 
    349474                ERROR "[ERROR] Unable to update $service on $nodedesignation -> $ds_name: Missing data source configuration attribute: label"; 
    350475                next; 
    351476            } 
    352  
    353             my $rrd_file  
    354                 = $self->_create_rrd_file_if_needed($service, $ds_name,  
    355                                                     $service_config->{$ds_name}); 
     477             
     478            # Sets the DS resolution, searching in that order :  
     479            # - per field  
     480            # - per plugin 
     481            # - globally 
     482            my $configref = $self->{node}{configref}; 
     483            $ds_config->{graph_data_size} ||= $configref->{"$service.$ds_name.graph_data_size"}; 
     484            $ds_config->{graph_data_size} ||= $configref->{"$service.graph_data_size"}; 
     485            $ds_config->{graph_data_size} ||= $config->{graph_data_size}; 
     486 
     487            DEBUG "[DEBUG] asking for a rrd of size : " . $ds_config->{graph_data_size}; 
     488            my $rrd_file = $self->_create_rrd_file_if_needed($service, $ds_name, $ds_config); 
    356489 
    357490            if (defined($service_data) and defined($service_data->{$ds_name})) { 
     
    414547                                $file); 
    415548         
    416     DEBUG "[DEBUG] Made rrd filename: $file\n"; 
     549    DEBUG "[DEBUG] rrd filename: $file\n"; 
    417550 
    418551    return $file; 
     
    430563                $ds_config->{type}, $ds_config->{min}, $ds_config->{max}), 
    431564    ); 
    432              
    433     my $resolution = $config->{graph_data_size}; 
     565 
     566    my $resolution = $ds_config->{graph_data_size}; 
     567    my $update_rate = $ds_config->{update_rate} || 300; # 5 min per default  
    434568    if ($resolution eq 'normal') { 
    435569        push (@args, 
     
    452586              "RRA:MIN:0.5:1:115200", 
    453587              "RRA:MAX:0.5:1:115200");  
    454     } 
     588    } elsif ($resolution =~ /^custom (.+)/) { 
     589        # Parsing resolution to achieve computer format as defined on the RFC : 
     590        # FULL_NB, MULTIPLIER_1 MULTIPLIER_1_NB, ... MULTIPLIER_NMULTIPLIER_N_NB  
     591        my @resolutions_computer = parse_custom_resolution($1, $update_rate); 
     592        foreach my $resolution_computer(@resolutions_computer) { 
     593            my ($multiplier, $multiplier_nb) = @{$resolution_computer}; 
     594            # Always add 10% to the RRA size, as specified in  
     595            # http://munin.projects.linpro.no/wiki/format-graph_data_size 
     596            $multiplier_nb += int ($multiplier_nb / 10) || 1; 
     597            push (@args,  
     598                "RRA:AVERAGE:0.5:$multiplier:$multiplier_nb", 
     599                "RRA:MIN:0.5:$multiplier:$multiplier_nb", 
     600                "RRA:MAX:0.5:$multiplier:$multiplier_nb" 
     601            );  
     602        } 
     603    } 
     604    DEBUG "[DEBUG] RRDs::create @args"; 
    455605    RRDs::create @args; 
    456606    if (my $ERROR = RRDs::error) { 
     
    459609} 
    460610 
     611sub parse_custom_resolution { 
     612        my @elems = split(',\s*', shift); 
     613        my $update_rate = shift; 
     614 
     615        DEBUG "[DEBUG] update_rate: $update_rate"; 
     616 
     617        my @computer_format; 
     618        foreach my $elem (@elems) { 
     619                if ($elem =~ m/(\d+) (\d+)/) { 
     620                        # nothing to do, already in computer format 
     621                        push @computer_format, [$1, $2]; 
     622                } elsif ($elem =~ m/(\w+) for (\w+)/) { 
     623                        my $nb_sec = to_sec($1); 
     624                        my $for_sec = to_sec($2); 
     625                         
     626                        my $multiplier = int ($nb_sec / $update_rate); 
     627                        my $multiplier_nb = int ($for_sec / $nb_sec); 
     628 
     629                        DEBUG "[DEBUG] $elem" 
     630                                . " -> nb_sec:$nb_sec, for_sec:$for_sec" 
     631                                . " -> multiplier:$multiplier, multiplier_nb:$multiplier_nb" 
     632                        ; 
     633                        push @computer_format, [$multiplier, $multiplier_nb]; 
     634                } 
     635        } 
     636 
     637        return @computer_format; 
     638} 
     639 
     640# return the number of seconds  
     641# for the human readable format 
     642# s : second,  m : minute, h : hour 
     643# d : day, w : week, t : month, y : year 
     644sub to_sec { 
     645        my $secs_table = { 
     646                "s" => 1, 
     647                "m" => 60, 
     648                "h" => 60 * 60, 
     649                "d" => 60 * 60 * 24, 
     650                "w" => 60 * 60 * 24 * 7, 
     651                "t" => 60 * 60 * 24 * 31, # a month always has 31 days 
     652                "y" => 60 * 60 * 24 * 365, # a year always has 365 days  
     653        }; 
     654 
     655        my ($target) = @_; 
     656        if ($target =~ m/(\d+)([smhdwty])/i) { 
     657                return $1 * $secs_table->{$2};   
     658        } else { 
     659                # no recognised unit, return the int value as seconds 
     660                return int $target; 
     661        } 
     662} 
     663 
     664sub to_mul { 
     665        my ($base, $target) = @_; 
     666        my $target_sec = to_sec($target); 
     667        if ($target %% $base != 0) { 
     668                return 0; 
     669        } 
     670 
     671        return round($target / $base);  
     672} 
     673 
     674sub to_mul_nb { 
     675        my ($base, $target) = @_; 
     676        my $target_sec = to_sec($target); 
     677        if ($target %% $base != 0) { 
     678                return 0; 
     679        } 
     680} 
    461681 
    462682sub _update_rrd_file { 
  • trunk/master/lib/Munin/Master/Utils.pm

    r3236 r3404  
    5555           'munin_get_node_name', 
    5656           'munin_get_parent_name', 
     57           'munin_get_node_fqn', 
    5758           'munin_get_node_loc', 
    5859           'munin_get_node', 
     
    518519} 
    519520 
     521sub munin_get_node_fqn 
     522{ 
     523    my $hash = shift; 
     524 
     525    if (ref ($hash) eq "HASH") { 
     526        my $fqn = ""; 
     527        if (defined $hash->{'#%#name'}) { 
     528                $fqn = $hash->{'#%#name'};  
     529        } 
     530        if (defined $hash->{'#%#parent'}) { 
     531                # Recursively prepend the parent, concatenation with / 
     532                $fqn = munin_get_node_fqn ($hash->{'#%#parent'}) . "/" . $fqn; 
     533        } 
     534        return $fqn; 
     535    } else {  
     536        return; 
     537    } 
     538} 
    520539 
    521540sub munin_get_picture_loc { 
  • trunk/master/www/munin-serviceview.tmpl

    r2956 r3404  
    1515      <!-- Table row: Day image --> 
    1616      <!-- Note, the class of the img does not work to set border width and color.  Could be something to do with the table? --> 
    17       <td><img src="<TMPL_VAR NAME="IMGDAY">" alt="daily graph" <TMPL_IF NAME="IMGDAYWIDTH">width="<TMPL_VAR NAME="IMGDAYWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGDAYHEIGHT">height="<TMPL_VAR NAME="IMGDAYHEIGHT">"</TMPL_IF>/></td> 
     17      <td><a href="<TMPL_VAR NAME="ZOOMDAY">"><img src="<TMPL_VAR NAME="IMGDAY">" alt="daily graph" <TMPL_IF NAME="IMGDAYWIDTH">width="<TMPL_VAR NAME="IMGDAYWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGDAYHEIGHT">height="<TMPL_VAR NAME="IMGDAYHEIGHT">"</TMPL_IF>/></a></td> 
    1818      <!-- cont'd: Week image --> 
    19       <td><img src="<TMPL_VAR NAME="IMGWEEK">" alt="weekly graph" <TMPL_IF NAME="IMGWEEKWIDTH">width="<TMPL_VAR NAME="IMGWEEKWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGWEEKHEIGHT">height="<TMPL_VAR NAME="IMGWEEKHEIGHT">"</TMPL_IF>/></td> 
     19      <td><a href="<TMPL_VAR NAME="ZOOMWEEK">"><img src="<TMPL_VAR NAME="IMGWEEK">" alt="weekly graph" <TMPL_IF NAME="IMGWEEKWIDTH">width="<TMPL_VAR NAME="IMGWEEKWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGWEEKHEIGHT">height="<TMPL_VAR NAME="IMGWEEKHEIGHT">"</TMPL_IF>/></a></td> 
    2020    </tr> 
    2121    <tr> 
    2222      <!-- New table row: Month image --> 
    23       <td><img src="<TMPL_VAR NAME="IMGMONTH">" alt="monthly graph" <TMPL_IF NAME="IMGMONTHWIDTH">width="<TMPL_VAR NAME="IMGMONTHWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGMONTHHEIGHT">height="<TMPL_VAR NAME="IMGMONTHHEIGHT">"</TMPL_IF>/></td> 
     23      <td><a href="<TMPL_VAR NAME="ZOOMMONTH">"><img src="<TMPL_VAR NAME="IMGMONTH">" alt="monthly graph" <TMPL_IF NAME="IMGMONTHWIDTH">width="<TMPL_VAR NAME="IMGMONTHWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGMONTHHEIGHT">height="<TMPL_VAR NAME="IMGMONTHHEIGHT">"</TMPL_IF>/></a></td> 
    2424      <!-- cont'd: Year image --> 
    25       <td><img src="<TMPL_VAR NAME="IMGYEAR">" alt="yearly graph" <TMPL_IF NAME="IMGYEARWIDTH">width="<TMPL_VAR NAME="IMGYEARWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGYEARHEIGHT">height="<TMPL_VAR NAME="IMGYEARHEIGHT">"</TMPL_IF>/></td> 
     25      <td><a href="<TMPL_VAR NAME="ZOOMYEAR">"><img src="<TMPL_VAR NAME="IMGYEAR">" alt="yearly graph" <TMPL_IF NAME="IMGYEARWIDTH">width="<TMPL_VAR NAME="IMGYEARWIDTH">" </TMPL_IF> <TMPL_IF NAME="IMGYEARHEIGHT">height="<TMPL_VAR NAME="IMGYEARHEIGHT">"</TMPL_IF>/></a></td> 
    2626    </tr> 
    2727    <!-- .sum graphs.  One of the least used features of munin? --> 
  • trunk/node

    • Property svn:ignore set to
      Build
      _build
      blib
  • trunk/node/lib/Munin/Node/Server.pm

    r3164 r3404  
    8989                || (split /\s+/, ($host_name || ''))[1] 
    9090                || $config->{fqdn}; 
     91         
     92        # hostname checks are case in-sensitive,  
     93        # so store everything in lowercase 
     94        $node = lc($node); 
    9195 
    9296        print STDERR "\tAdding to node $node\n" if $config->{DEBUG}; 
     
    164168    logger ("DEBUG: Running command \"$_\".") if $config->{DEBUG}; 
    165169    if (/^list\s*([0-9a-zA-Z\.\-]+)?/i) { 
    166         _list_services($session, $1); 
     170        _list_services($session, lc($1)); 
    167171    } 
    168172    elsif (/^cap\s?(.*)/i) { 
  • trunk/plugins

    • Property svn:ignore set to
      Build
      _build
      blib