From 18b47ccf9378ecb6e8f9c119781e27331a66a0da Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Mon, 3 Mar 2014 22:36:35 +0900 Subject: [PATCH 1/7] Support a virtical line tentatively --- lib/GrowthForecast/Data.pm | 40 ++++++++++++++++++ lib/GrowthForecast/Data/MySQL.pm | 11 +++++ lib/GrowthForecast/RRD.pm | 15 +++++++ lib/GrowthForecast/Web.pm | 69 +++++++++++++++++++++++++++++++- 4 files changed, 134 insertions(+), 1 deletion(-) diff --git a/lib/GrowthForecast/Data.pm b/lib/GrowthForecast/Data.pm index 02055ce..1bb87d0 100644 --- a/lib/GrowthForecast/Data.pm +++ b/lib/GrowthForecast/Data.pm @@ -108,6 +108,19 @@ CREATE TABLE IF NOT EXISTS complex_graphs ( UNIQUE (service_name, section_name, graph_name) ) EOF + + $dbh->do(<do(<dbh->query( + 'INSERT INTO vrules (graph_path,time,color,description) values (?,?,?,?)', + $graph_path, $time, $color, $desc + ); +} + +sub get_vrule { + my ($self, $from, $to, $graph_path) = @_; + my @vrules = (); + + my @gp = split '/', substr($graph_path, 1); + + my $rows = $self->dbh->select_all( + 'SELECT * FROM vrules WHERE (time BETWEEN ? and ?) AND graph_path in ("/",?,?,?)', + $from, $to, + "/$gp[0]", + "/$gp[0]/$gp[1]", + "/$gp[0]/$gp[1]/$gp[2]", + ); + push @vrules, @$rows; + + return @vrules; +} + 1; diff --git a/lib/GrowthForecast/Data/MySQL.pm b/lib/GrowthForecast/Data/MySQL.pm index a9fa746..adca54b 100644 --- a/lib/GrowthForecast/Data/MySQL.pm +++ b/lib/GrowthForecast/Data/MySQL.pm @@ -95,6 +95,17 @@ CREATE TABLE IF NOT EXISTS complex_graphs ( UNIQUE (service_name, section_name, graph_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 EOF + + $dbh->do(<{$_} } qw/gmode t from to width height/; $span ||= 'd'; $width ||= 390; @@ -366,6 +367,20 @@ sub graph { sprintf('PRINT:sumupmin:%%.8lf'); } + my $time_from = $period eq 'now' ? time() : + $period < 0 ? time() + $period : + $period; + my $time_to = $end eq 'now' ? time() : + $end < 0 ? time() + $end : + $end; + for my $vrule ($data->get_vrule($time_from, $time_to, '/'.join('/',@{$datas}{qw(service_name section_name graph_name)}))) { + push @opt, join(":", + 'VRULE', + join("", $vrule->{time}, $vrule->{color}), + ($vrule->{description}||()), + ); + } + my @graphv; eval { @graphv = RRDs::graph(map { Encode::encode_utf8($_) } @opt); diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index b7d9ac9..d661fbb 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -675,7 +675,8 @@ get '/{method:(?:xport|graph|summary)}/:service_name/:section_name/:graph_name' if ( $c->args->{method} eq 'graph' ) { my ($img,$data) = $self->rrd->graph( - $c->stash->{graph}, $result->valid->as_hashref + $c->stash->{graph}, $result->valid->as_hashref, + $self->data, ); $c->res->content_type('image/png'); $c->res->body($img); @@ -1155,5 +1156,71 @@ post '/json/edit/{type:(?:graph|complex)}/:id' => sub { $c->render_json({ error => 0 }); }; +post '/vrule/:service_name/:section_name/:graph_name' => sub { + my ( $self, $c ) = @_; + $self->add_vrule($c); +}; +post '/vrule/:service_name/:section_name' => sub { + my ( $self, $c ) = @_; + $self->add_vrule($c); +}; +post '/vrule/:service_name' => sub { + my ( $self, $c ) = @_; + $self->add_vrule($c); +}; +post '/vrule' => sub { + my ( $self, $c ) = @_; + $self->add_vrule($c); +}; + +sub add_vrule { + my ( $self, $c ) = @_; + my $result = $c->req->validator([ + 'time' => { + default => time(), + rule => [ + ['INT', 'a INT number is required for "time"'] + ], + }, + 'color' => { + default => '#FF0000', + rule => [ + [sub{ length($_[1]) == 0 || $_[1] =~ m!^#[0-9A-F]{6}$!i }, 'invalid color format'], + ], + }, + 'description' => { + default => '', + rule => [], + }, + ]); + + if ( $result->has_error ) { + my $res = $c->render_json({ + error => 1, + messages => $result->messages + }); + $res->status(400); + return $res; + } + + my $row; + eval { + my $graph_path = '/'.join('/', $c->args->{service_name}||(), $c->args->{section_name}||(), $c->args->{graph_name}||()); + $row = $self->data->update_vrule( + $graph_path, + $result->valid('time'), + $result->valid('color'), + $result->valid('description'), + ); + }; + if ( $@ ) { + die sprintf "Error:%s %s/%s/%s => %s,%s", + $@, $c->args->{service_name}, $c->args->{section_name}, $c->args->{graph_name}, + $result->valid('time'), $result->valid('color'); + } + + $c->render_json({error=>0, data => 'fixme'}); +}; + 1; From 13541ecc4fc552634857c41296c2fe122bb9e49f Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Tue, 4 Mar 2014 15:26:46 +0900 Subject: [PATCH 2/7] Add id column to vrules table --- lib/GrowthForecast/Data.pm | 1 + lib/GrowthForecast/Data/MySQL.pm | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/GrowthForecast/Data.pm b/lib/GrowthForecast/Data.pm index 1bb87d0..ff38d43 100644 --- a/lib/GrowthForecast/Data.pm +++ b/lib/GrowthForecast/Data.pm @@ -111,6 +111,7 @@ EOF $dbh->do(<do(< Date: Tue, 4 Mar 2014 16:48:13 +0900 Subject: [PATCH 3/7] Add GF::Data to instance variables of GF::RRD --- lib/GrowthForecast/RRD.pm | 3 +-- lib/GrowthForecast/Web.pm | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/GrowthForecast/RRD.pm b/lib/GrowthForecast/RRD.pm index 2b63433..d19a068 100644 --- a/lib/GrowthForecast/RRD.pm +++ b/lib/GrowthForecast/RRD.pm @@ -271,7 +271,6 @@ sub graph { my $datas = shift; my @datas = ref($datas) eq 'ARRAY' ? @$datas : ($datas); my $args = shift; - my $data = shift; my ($a_gmode, $span, $from, $to, $width, $height) = map { $args->{$_} } qw/gmode t from to width height/; $span ||= 'd'; $width ||= 390; @@ -373,7 +372,7 @@ sub graph { my $time_to = $end eq 'now' ? time() : $end < 0 ? time() + $end : $end; - for my $vrule ($data->get_vrule($time_from, $time_to, '/'.join('/',@{$datas}{qw(service_name section_name graph_name)}))) { + for my $vrule ($self->{data}->get_vrule($time_from, $time_to, '/'.join('/',@{$datas}{qw(service_name section_name graph_name)}))) { push @opt, join(":", 'VRULE', join("", $vrule->{time}, $vrule->{color}), diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index d661fbb..509e6d2 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -33,6 +33,7 @@ sub rrd { root_dir => $self->root_dir, rrdcached => $self->rrdcached, disable_subtract => $self->disable_subtract, + data => $self->data, ); $self->{__rrd}; } From f4b9e74e4c8dffc17789748e63d3367828f6238d Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Tue, 4 Mar 2014 18:11:07 +0900 Subject: [PATCH 4/7] Change end point of posting to vrule prepare to implement vrule summary --- lib/GrowthForecast/Web.pm | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index 509e6d2..420c535 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -1157,21 +1157,17 @@ post '/json/edit/{type:(?:graph|complex)}/:id' => sub { $c->render_json({ error => 0 }); }; -post '/vrule/:service_name/:section_name/:graph_name' => sub { - my ( $self, $c ) = @_; - $self->add_vrule($c); +post '/vrule/api/:service_name/:section_name/:graph_name' => sub { + my ( $self, $c ) = @_; $self->add_vrule($c); }; -post '/vrule/:service_name/:section_name' => sub { - my ( $self, $c ) = @_; - $self->add_vrule($c); +post '/vrule/api/:service_name/:section_name' => sub { + my ( $self, $c ) = @_; $self->add_vrule($c); }; -post '/vrule/:service_name' => sub { - my ( $self, $c ) = @_; - $self->add_vrule($c); +post '/vrule/api/:service_name' => sub { + my ( $self, $c ) = @_; $self->add_vrule($c); }; -post '/vrule' => sub { - my ( $self, $c ) = @_; - $self->add_vrule($c); +post '/vrule/api' => sub { + my ( $self, $c ) = @_; $self->add_vrule($c); }; sub add_vrule { From 47cff5772aa6453e2e1f66afcc1f5f8a6ec6038e Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Tue, 4 Mar 2014 19:39:42 +0900 Subject: [PATCH 5/7] Support retrieve vrule definitions API GET /vrule/summary/service/section/graph --- lib/GrowthForecast/Data.pm | 45 +++++++++++++++++++++++++++++-- lib/GrowthForecast/RRD.pm | 8 +----- lib/GrowthForecast/Web.pm | 54 +++++++++++++++++++++++++++++++++++++- 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/lib/GrowthForecast/Data.pm b/lib/GrowthForecast/Data.pm index ff38d43..0fec9e8 100644 --- a/lib/GrowthForecast/Data.pm +++ b/lib/GrowthForecast/Data.pm @@ -547,17 +547,58 @@ sub update_vrule { 'INSERT INTO vrules (graph_path,time,color,description) values (?,?,?,?)', $graph_path, $time, $color, $desc ); + + my $row = $self->dbh->select_row( + 'SELECT * FROM vrules WHERE graph_path = ? AND time = ? AND color = ? AND description = ?', + $graph_path, $time, $color, $desc, + ); + + return $row; } +# "$span" is a parameter named "t", +# "$from" and "$to" are paramters same nameed. sub get_vrule { - my ($self, $from, $to, $graph_path) = @_; + my ($self, $span, $from, $to, $graph_path) = @_; + + my($from_time, $to_time) = (0, time); + # same rule as GrowthForecast::RRD#calc_period + if ( $span eq 'all' ) { + $from_time = 0; + $to_time = 4294967295; # unsigned int max + } elsif ( $span eq 'c' || $span eq 'sc' ) { + my $from_time = HTTP::Date::str2time($from); + die "invalid from date: $from" unless $from_time; + my $to_time = $to ? HTTP::Date::str2time($to) : time; + die "invalid to date: $to" unless $to_time; + die "from($from) is newer than to($to)" if $from_time > $to_time; + } elsif ( $span eq 'h' || $span eq 'sh' ) { + $from_time = time -1 * 60 * 60 * 2; + } elsif ( $span eq 'n' || $span eq 'sn' ) { + $from_time = time -1 * 60 * 60 * 14; + } elsif ( $span eq 'w' ) { + $from_time = time -1 * 60 * 60 * 24 * 8; + } elsif ( $span eq 'm' ) { + $from_time = time -1 * 60 * 60 * 24 * 35; + } elsif ( $span eq 'y' ) { + $from_time = time -1 * 60 * 60 * 24 * 400; + } elsif ( $span eq '3d' ) { + $from_time = time -1 * 60 * 60 * 24 * 3; + } elsif ( $span eq '8h' ) { + $from_time = time -1 * 8 * 60 * 60; + } elsif ( $span eq '4h' ) { + $from_time = time -1 * 4 * 60 * 60; + } else { + $from_time = time -1 * 60 * 60 * 33; # 33 hours + } + my @vrules = (); my @gp = split '/', substr($graph_path, 1); my $rows = $self->dbh->select_all( 'SELECT * FROM vrules WHERE (time BETWEEN ? and ?) AND graph_path in ("/",?,?,?)', - $from, $to, + $from_time, $to_time, "/$gp[0]", "/$gp[0]/$gp[1]", "/$gp[0]/$gp[1]/$gp[2]", diff --git a/lib/GrowthForecast/RRD.pm b/lib/GrowthForecast/RRD.pm index d19a068..79fb5da 100644 --- a/lib/GrowthForecast/RRD.pm +++ b/lib/GrowthForecast/RRD.pm @@ -366,13 +366,7 @@ sub graph { sprintf('PRINT:sumupmin:%%.8lf'); } - my $time_from = $period eq 'now' ? time() : - $period < 0 ? time() + $period : - $period; - my $time_to = $end eq 'now' ? time() : - $end < 0 ? time() + $end : - $end; - for my $vrule ($self->{data}->get_vrule($time_from, $time_to, '/'.join('/',@{$datas}{qw(service_name section_name graph_name)}))) { + for my $vrule ($self->{data}->get_vrule($span, $period, $end, '/'.join('/',@{$datas}{qw(service_name section_name graph_name)}))) { push @opt, join(":", 'VRULE', join("", $vrule->{time}, $vrule->{color}), diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index 420c535..24c63cc 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -1216,7 +1216,59 @@ sub add_vrule { $result->valid('time'), $result->valid('color'); } - $c->render_json({error=>0, data => 'fixme'}); + $c->render_json({error=>0, data => $row}); +}; + +get '/vrule/summary/:service_name/:section_name/:graph_name' => sub { + my ( $self, $c ) = @_; $self->summarize_vrule($c); +}; +get '/vrule/summary/:service_name/:section_name' => sub { + my ( $self, $c ) = @_; $self->summarize_vrule($c); +}; +get '/vrule/summary/:service_name' => sub { + my ( $self, $c ) = @_; $self->summarize_vrule($c); +}; +get '/vrule/summary' => sub { + my ( $self, $c ) = @_; $self->summarize_vrule($c); +}; + +sub summarize_vrule { + my ( $self, $c ) = @_; + my $result = $c->req->validator([ + 't' => { + default => 'all', + rule => [ + [['CHOICE',qw/all y m w 3d s3d d sd 8h s8h 4h s4h h sh n sn c sc/],'invalid drawing term'], + ], + }, + 'from' => { + default => localtime(time-86400*8)->strftime('%Y/%m/%d %T'), + rule => [ + [sub{ HTTP::Date::str2time($_[1]) }, 'invalid From datetime'], + ], + }, + 'to' => { + default => localtime()->strftime('%Y/%m/%d %T'), + rule => [ + [sub{ HTTP::Date::str2time($_[1]) }, 'invalid To datetime'], + ], + }, + ]); + + if ( $result->has_error ) { + my $res = $c->render_json({ + error => 1, + messages => $result->messages + }); + $res->status(400); + return $res; + } + + my $graph_path = '/'.join('/', $c->args->{service_name}||(), $c->args->{section_name}||(), $c->args->{graph_name}||()); + + my @vrules = $self->data->get_vrule($result->valid('t'), $result->valid('from'), $result->valid('to'), $graph_path); + + $c->render_json(\@vrules); }; 1; From ad02485d66a4e3bd8c1ec8b171fd3c3de257482d Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Tue, 4 Mar 2014 19:48:32 +0900 Subject: [PATCH 6/7] Support vrule_legend parameter in query string --- lib/GrowthForecast/RRD.pm | 2 +- lib/GrowthForecast/Web.pm | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/GrowthForecast/RRD.pm b/lib/GrowthForecast/RRD.pm index 79fb5da..c079994 100644 --- a/lib/GrowthForecast/RRD.pm +++ b/lib/GrowthForecast/RRD.pm @@ -370,7 +370,7 @@ sub graph { push @opt, join(":", 'VRULE', join("", $vrule->{time}, $vrule->{color}), - ($vrule->{description}||()), + ($args->{vrule_legend} ? ($vrule->{description}||()) : ()), ); } diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index 24c63cc..5e6309c 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -583,6 +583,12 @@ sub graph_validator { [['CHOICE', qw/AVERAGE MAX/], 'invalid consolidation function'], ], }, + 'vrule_legend' => { + default => 1, + rule => [ + [['CHOICE',qw/0 1/],'invalid vrule_legend flag'], + ], + }, ]; } From 7987706b73b45022e1a2b345fb7a5fab967c19b7 Mon Sep 17 00:00:00 2001 From: HIROSE Masaaki Date: Tue, 4 Mar 2014 19:52:34 +0900 Subject: [PATCH 7/7] Do eval in calling get_vrule in summarize_vrule --- lib/GrowthForecast/Web.pm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/GrowthForecast/Web.pm b/lib/GrowthForecast/Web.pm index 5e6309c..62b1687 100644 --- a/lib/GrowthForecast/Web.pm +++ b/lib/GrowthForecast/Web.pm @@ -1272,7 +1272,15 @@ sub summarize_vrule { my $graph_path = '/'.join('/', $c->args->{service_name}||(), $c->args->{section_name}||(), $c->args->{graph_name}||()); - my @vrules = $self->data->get_vrule($result->valid('t'), $result->valid('from'), $result->valid('to'), $graph_path); + my @vrules; + eval { + @vrules = $self->data->get_vrule($result->valid('t'), $result->valid('from'), $result->valid('to'), $graph_path); + }; + if ( $@ ) { + die sprintf "Error:%s %s/%s/%s => %s,%s,%s", + $@, $c->args->{service_name}, $c->args->{section_name}, $c->args->{graph_name}, + $result->valid('t'), $result->valid('from') , $result->valid('to'); + } $c->render_json(\@vrules); };