Permalink
Fetching contributors…
Cannot retrieve contributors at this time
executable file 394 lines (300 sloc) 9.31 KB
#!/usr/bin/perl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/extlib/lib/perl5";
use lib "$FindBin::Bin/lib";
use File::Basename;
use Getopt::Long;
use Plack::Loader;
use Plack::Builder;
use Plack::Builder::Conditionals;
use Plack::Util;
use GrowthForecast;
use GrowthForecast::Web;
use GrowthForecast::Worker;
use IO::Socket::UNIX;
use Proclet;
use Starlet '0.21';
use File::ShareDir qw/dist_dir/;
use Cwd;
use File::Path qw/mkpath/;
use Log::Minimal;
use Pod::Usage;
use POSIX qw//;
my $port = 5125;
my $host = 0;
my @front_proxy;
my @allow_from;
Getopt::Long::Configure ("no_ignore_case");
GetOptions(
'port=s' => \$port,
'host=s' => \$host,
'socket=s' => \my $socket,
'front-proxy=s' => \@front_proxy,
'allow-from=s' => \@allow_from,
'disable-1min-metrics' => \my $disable_short,
'disable-subtract' => \my $disable_subtract,
'enable-float-number' => \my $enable_float_number,
'with-mysql=s' => \my $mysql,
'data-dir=s' => \my $data_dir,
'log-format=s' => \my $log_format,
'web-max-workers=i' => \my $web_max_workers,
'rrdcached=s' => \my $rrdcached,
'mount=s' => \my $mount,
'time-zone=s' => \my $timezone,
"h|help" => \my $help,
'v|version' => \my $version,
);
if ( $timezone ) {
eval {
$ENV{TZ} = $timezone;
POSIX::tzset;
};
if ( $@ ) {
die "Failed timezone set to '$timezone': $@";
}
}
if ( $version ) {
print "GrowthForecast version $GrowthForecast::VERSION\n\n";
print "Try `growthforecast.pl --help` for more options.\n";
exit 0;
}
if ( $help ) {
pod2usage(-verbose=>2,-exitval=>0);
}
if ( $mysql ) {
eval { require GrowthForecast::Data::MySQL };
die "Cannot load MySQL: $@" if $@;
}
my $root_dir = File::Basename::dirname(__FILE__);
if ( ! -f "$root_dir/lib/GrowthForecast.pm" ) {
$root_dir = dist_dir('GrowthForecast');
}
if ( !$data_dir ) {
$data_dir = $root_dir . '/data';
}
else {
$data_dir = Cwd::realpath($data_dir);
}
{
if ( ! -d $data_dir ) {
mkpath($data_dir) or die "cannot create data directory '$data_dir': $!";
}
open( my $fh, '>', "$data_dir/$$.tmp") or die "cannot create file in data_dir: $!";
close($fh);
unlink("$data_dir/$$.tmp");
}
my $sock;
if ( $socket ) {
if (-S $socket) {
warn "removing existing socket file:$socket";
unlink $socket
or die "failed to remove existing socket file:$socket:$!";
}
unlink $socket;
$sock = IO::Socket::UNIX->new(
Listen => Socket::SOMAXCONN(),
Local => $socket,
) or die "failed to listen to file $socket:$!";
$ENV{SERVER_STARTER_PORT} = $socket."=".$sock->fileno;
}
my $proclet = Proclet->new;
$proclet->service(
tag => 'worker_1min',
code => sub {
local $0 = "$0 (GrowthForecast::Worker 1min)";
my $worker = GrowthForecast::Worker->new(
root_dir => $root_dir,
data_dir => $data_dir,
mysql => $mysql,
float_number => $enable_float_number,
rrdcached => $rrdcached,
disable_subtract => $disable_subtract,
);
$worker->run('short');
}
) if !$disable_short;
$proclet->service(
tag => 'worker',
code => sub {
local $0 = "$0 (GrowthForecast::Worker)";
my $worker = GrowthForecast::Worker->new(
root_dir => $root_dir,
data_dir => $data_dir,
mysql => $mysql,
float_number => $enable_float_number,
rrdcached => $rrdcached,
disable_subtract => $disable_subtract,
);
$worker->run;
}
);
$proclet->service(
tag => 'web',
code => sub {
local $0 = "$0 (GrowthForecast::Web)";
my $web = GrowthForecast::Web->new(
root_dir => $root_dir,
data_dir => $data_dir,
short => !$disable_short,
mysql => $mysql,
float_number => $enable_float_number,
rrdcached => $rrdcached,
disable_subtract => $disable_subtract,
);
my $app = builder {
enable 'Lint';
enable 'StackTrace';
if ( $sock ) {
enable 'ReverseProxy';
}
elsif ( @front_proxy ) {
enable match_if addr(\@front_proxy), 'ReverseProxy';
}
if ( @allow_from ) {
enable match_if addr('!',\@allow_from), sub {
sub { [403,['Content-Type','text/plain'], ['Forbidden']] }
};
}
enable sub {
my $app = shift;
sub {
my $env = shift;
my $res = $app->($env);
Plack::Util::response_cb($res, sub {
my $res = shift;
Plack::Util::header_set($res->[1], 'X-Powered-By',
'GrowthForecast/'.$GrowthForecast::VERSION);
});
}
};
my $static_regexp = qr!^/(?:(?:css|fonts|js|images)/|favicon\.ico$)!;
enable 'Static',
path => $mount ? sub { s!^/$mount!!; $_ =~ $static_regexp } : $static_regexp,
root => $root_dir . '/public';
enable 'Scope::Container';
if ($log_format) {
my %args;
if ($log_format eq 'combined') {
%args = (combined => 1);
}
elsif ($log_format eq 'ltsv') {
%args = (ltsv => 1);
}
else {
%args = (format => $log_format);
}
enable 'AxsLog', %args;
}
if ($mount) {
mount "/$mount" => $web->psgi;
}
else {
$web->psgi;
}
};
my $loader = Plack::Loader->load(
'Starlet',
port => $port,
host => $host || 0,
max_workers => $web_max_workers || 4,
);
infof( "GrowthForecast::Web starts listen on %s:%s", $host || 0, $port );
$loader->run($app);
}
);
$proclet->run;
__END__
=head1 NAME
growthforecast.pl - Lightning Fast Graphing/Visualization
=head1 SYNOPSIS
% growthforecast.pl --data-dir=/path/to/dir
=head1 DESCRIPTION
GrowthForecast is graphing/visualization web tool built on RRDtool
=head1 INSTALL
=over 4
=item Install dependencies
To install growthforecast, these libraries are needed.
=over 4
=item * glib
=item * xml2
=item * pango
=item * cairo
=back
(CentOS) $ sudo yum groupinstall "Development Tools"
$ sudo yum install pkgconfig glib2-devel gettext libxml2-devel pango-devel cairo-devel
(Ubuntu) $ sudo apt-get build-dep rrdtool
=item Install GrowthForecast
$ cpanm GrowthForecast
It's recommended to using perlbrew
=back
=head1 OPTIONS
=over 4
=item --data-dir
A directory to store rrddata and metadata
=item --port
TCP port listen on. Default is 5125
=item --host
IP address to listen on
=item --socket
File path to UNIX domain socket to bind. If enabled unix domain socket, GrowthForecast does not bind any TCP port
=item --front-proxy
IP addresses or CIDR of reverse proxy
=item --allow-from
IP addresses or CIDR to allow access from.
Default is empty (allow access from any remote ip address)
=item --disable-1min-metrics
don't generate 1min rrddata and graph
Default is "1" (enabled)
=item --disable-subtract
Disable gmode `subtract`. Default is "1" (enabled)
=item --enable-float-number
Store numbers of graph data as float rather than integer.
Default is "0" (disabled)
=item --with-mysql
DB connection setting to store metadata. format like dbi:mysql:[dbname];hostname=[hostnaem]
Default is no mysql setting. GrowthForecast save metadata to SQLite
=item --web-max-workers
Number of web server processes. Default is 4
=item --rrdcached
rrdcached address. format is like either of
unix:</path/to/unix.sock>
/<path/to/unix.sock>
<hostname-or-ip>
[<hostname-or-ip>]:<port>
<hostname-or-ipv4>:<port>
See the manual of rrdcached for more details. Default does not use rrdcached.
=item --mount
Provide GrowthForecast with specify url path.
Default is empty ( provide GrowthForecast on root path )
=item --time-zone
Set the system time zone for GrowthForecast. Default is system timezone.
=item -v --version
Display version
=item -h --help
Display help
=back
=head1 MYSQL Setting
GrowthForecast uses SQLite as metadata by default. And also supports MySQL
GrowthForecast needs these MySQL privileges.
=over 4
=item * CREATE
=item * ALTER
=item * DELETE
=item * INSERT
=item * UPDATE
=item * SELECT
=back
Sample GRANT statement
mysql> GRANT statement sample> GRANT CREATE, ALTER, DELETE, INSERT, UPDATE, SELECT \\
ON growthforecast.* TO 'www'\@'localhost' IDENTIFIED BY foobar;
Give USERNAME and PASSWORD to GrowthForecast by environment value
$ MYSQL_USER=www MYSQL_PASSWORD=foobar growthforecast.pl \\
--data-dir /home/user/growthforecast \\
-with-mysql dbi:mysql:growthforecast;hostname=localhost
AUTHOR
Masahiro Nagano <kazeburo {at} gmail.com>
LICENSE
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.