#!/usr/bin/env perl #------------- #--- MODEL --- #------------- package PGagent::Model; use strict; use warnings; use File::stat; use Data::Dumper; use DBI; use Mojo::UserAgent; use POSIX; use Socket; sub new { my ($class, $app, $pghost, $username, $password) = @_; my $self = { app => $app, pghost => $pghost, dsn => "dbi:Pg:dbname=postgres;host=$pghost", username => $username, password => $password, }; bless $self, $class; return $self; } sub username { return shift->{username}; } sub password { return shift->{password}; } sub dsn { return shift->{dsn}; } sub pghost { return shift->{pghost}; } sub app { return shift->{app}; } sub storeAlive { my ($self, $hostname) = @_; return undef unless $hostname; my $ua = Mojo::UserAgent->new(max_redirects => 2) or return undef; my $tx = $ua->get("http://$hostname:3002/hello"); my $res; eval { $res = decode_json($tx->result->body); }; return 1;# if $res->{'message'} eq 'hello'; return undef; } sub storePut { my ($self, $datafile, $storename, $storeuser, $storepwd) = @_; my $ua = Mojo::UserAgent->new(max_redirects => 5); return undef unless $datafile; return undef unless $storename; return undef unless -f $datafile; return undef unless -r $datafile; return undef unless $self->storeAlive($storename); $self->app->log->info(" --- Start upload $datafile to store $storename"); my $tx = $ua->post("http://$storeuser:$storepwd\@$storename:3002/store/put" => form => { data => { file => $datafile }} ); $self->app->log->info(" --- End upload $datafile to store $storename"); my $res; eval { $res = $tx->result; }; return $res->body if $res; return undef; } # ---- agent::model db* --- sub dbExist { my ($self, $dbname) = @_; return undef unless $dbname; my $dblist = $self->dbList; foreach my $db (@{$dblist}) { return 1 if $db->{"name"} eq $dbname; } return undef; } sub dbSize { my ($self, $dbname) = @_; return undef unless $dbname; return -1 unless $self->dbExist($dbname); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return -1; my $query = "select pg_database_size('$dbname');"; my $sth = $db->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_array; $sth->finish; $db->disconnect; my $dbsize = int($row); return $dbsize; } sub dbList { my $self = shift; my $db = DBI->connect($self->dsn, $self->username, $self->password) or return -1; my $query = "select d.datname as name, pg_database_size(d.datname) as size, u.usename as owner from pg_database d, pg_user u where d.datdba = u.usesysid;"; my $sth = $db->prepare($query); my $rows = $sth->execute or return -1; my @dblist; while (my $row = $sth->fetchrow_hashref) { push @dblist, $row; } $sth->finish; $db->disconnect; return \@dblist; } sub dbCreate { my ($self, $dbname) = @_; return undef unless $dbname; return 1 if $self->dbExist($dbname); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "create database $dbname;"; my $rows = $db->do($query) or return undef; $db->disconnect; return 1 if $rows; return undef; } sub dbDrop { my ($self, $dbname) = @_; return undef unless $self->dbExist($dbname); return undef unless $dbname; my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "drop database $dbname;"; my $rows = $db->do($query); $db->disconnect; return 1 if $rows; return undef; } sub dbRename { my ($self, $dbname, $newname) = @_; return undef unless $self->dbExist($dbname); return undef unless $dbname; return undef unless $newname; my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "alter database $dbname rename to $newname;"; my $rows = $db->do($query); $db->disconnect; return 1 if $rows; return undef; } sub dbCopy { my ($self, $dbname, $newname) = @_; return undef unless $dbname; return undef unless $newname; return undef unless $self->dbExist($dbname); return undef if $self->dbExist($newname); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "create database $newname template $dbname;"; my $rows = $db->do($query) or return undef; $db->disconnect; return 1 if $rows; return undef; } sub dbDump { my ($self, $dbname) = @_; return undef unless $dbname; return undef unless $self->dbExist($dbname); my $workdir = "/".$self->app->config("workdir"); do { $self->app->log->info("Error: Workdir $workdir not exist"); return undef; } unless -d $workdir; do { $self->app->log->info("Error: Cannot write to workdir $workdir"); return undef; } unless -w $workdir; my $hostname = $self->app->config("hostname"); my $timestamp = strftime("%Y%m%d-%H%M%S-%Z", localtime(time)); my $dbdump = "$workdir/$dbname--$timestamp--$hostname.sqlz"; my $password = $self->password; my $pghost = $self->pghost; my $username = $self->username; my $dbsize = $self->dbSize($dbname); $self->app->log->info("--- Start dump database $dbname with db size $dbsize"); my $out = qx/PGPASSWORD=$password pg_dump -h $pghost -U $username -Fc -f $dbdump $dbname 2>&1/; my $retcode = $?; my $dumpstat = stat($dbdump); my $dumpsize = $dumpstat->size; $self->app->log->info("--- End dump database $dbname to $dbdump with size $dumpsize"); return $dbdump if $retcode == 0; return undef; } #select d.datname as dbname, u.usename as username from pg_database d, pg_user u where d.datdba = u.usesysid order by dbname; sub dbOwner { my ($self, $dbname) = @_; return undef unless $dbname; return undef unless $self->dbExist($dbname); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "select u.usename as username, d.datname as dbname from pg_database d, pg_user u where d.datdba = u.usesysid and d.datname = '$dbname' limit 1;"; my $sth = $db->prepare($query); my $rows = $sth->execute or return undef; my $row = $sth->fetchrow_hashref; $db->disconnect; return $row if $row; return undef; } sub roleList { my ($self) = @_; my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "select usename as rolename from pg_user;"; my $sth = $db->prepare($query); my $rows = $sth->execute or return undef; my @rolelist; while (my $row = $sth->fetchrow_hashref) { push @rolelist, $row; } $sth->finish; $db->disconnect; return \@rolelist; } sub roleExist { my ($self, $rolename) = @_; return undef unless $rolename; my $rolelist = $self->roleList; foreach my $role (@{$rolelist}) { return 1 if $rolename eq $role->{'rolename'}; } return undef; } sub roleCreate { my ($self, $rolename, $password) = @_; return undef unless $password; return undef unless $rolename; return undef unless $self->roleExist($rolename); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "create user '$rolename' encrypted password '$password';"; my $rows = $db->do($query); $db->disconnect; return 1 if $rows; return undef; } sub roleDrop { my ($self, $rolename) = @_; return 1; } sub rolePassword { my ($self, $rolename, $password) = @_; return undef unless $password; return undef unless $rolename; return undef unless $self->roleExist($rolename); my $db = DBI->connect($self->dsn, $self->username, $self->password) or return undef; my $query = "alter role $rolename encrypted password '$password';"; my $rows = $db->do($query); $db->disconnect; return 1 if $rows; return undef; } 1; #------------------ #--- CONTROLLER --- #------------------ package PGagent::Controller; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Util qw(dumper); use Mojo::JSON qw(encode_json decode_json); use Mojo::IOLoop::Subprocess; use Apache::Htpasswd; sub hello { my $self = shift; $self->render(json => { message => 'hello' } ); } sub confDump { my $self = shift; $self->render(json => $self->app->config); } sub dbList { my $self = shift; $self->render(json => { dblist => $self->app->model->dbList } ); } sub dbSize { my $self = shift; my $dbname = $self->req->param('dbname'); return $self->render(json => { result => "mistake", dbname => $dbname }) unless $dbname; my $dbsize = $self->app->model->dbSize($dbname); $self->render(json => { name => $dbname, size => $dbsize }); } sub dbCreate { my $self = shift; my $dbname = $self->req->param('dbname'); return $self->render(json => { result => "mistake", dbname => $dbname }) unless $dbname; my $res = $self->app->model->dbCreate($dbname); return $self->render(json => { result => "success", dbname => $dbname }) if $res; return $self->render(json => { result => "mistake", dbname => $dbname }); } sub dbDrop { my $self = shift; my $dbname = $self->req->param('dbname'); return $self->render(json => { result => "mistake", dbname => $dbname }) unless $dbname; my $res = $self->app->model->dbDrop($dbname); return $self->render(json => { result => "success", dbname => $dbname }) if $res; return $self->render(json => { result => "mistake", dbname => $dbname }); } sub dbRename { my $self = shift; my $dbname = $self->req->param('dbname'); my $newname = $self->req->param('newname'); return $self->render(json => { result => "mistake", dbname => $dbname }) unless $dbname; return $self->render(json => { result => "mistake", dbname => $dbname }) unless $newname; my $res = $self->app->model->dbRename($dbname, $newname); my $dbsize = $self->app->model->dbSize($dbname) if $res; return $self->render(json => {result => "success", dbname => $dbname, dbsize => $dbsize}) if $res; return $self->render(json => { result => "mistake", dbname => $dbname }); } sub dbCopy { my $self = shift; my $dbname = $self->req->param('dbname'); my $newname = $self->req->param('newname'); return $self->render(json => { result => "mistake", dbname => $dbname }) unless $dbname; return $self->render(json => { result => "mistake", dbname => $dbname }) unless $newname; my $res = $self->app->model->dbCopy($dbname, $newname); my $dbsize = $self->app->model->dbSize($dbname) if $res; return $self->render(json => {result => "success", dbname => $dbname, dbsize => $dbsize}) if $res; return $self->render(json => { result => "mistake", dbname => $dbname }); } sub dbExist { my $self = shift; my $dbname = $self->req->param('dbname'); return $self->render(json => { result => "mistake", dbname => '' }) unless $dbname; my $res = $self->app->model->dbExist($dbname); my $dbsize = $self->app->model->dbSize($dbname) if $res; return $self->render(json => { result => "success", dbname => $dbname, dbsize => $dbsize}) if $res; return $self->render(json => { result => "mistake", dbname => $dbname }); } sub dbDump { my $self = shift; my $dbname = $self->req->param('dbname'); my $storename = $self->req->param('store'); my $storelogin = $self->req->param('storelogin'); my $storepwd = $self->req->param('storepwd'); my $master = $self->req->param('master'); my $jobid = $self->req->param('jobid'); my $magic = $self->req->param('magic'); return $self->render(json => { response => "mistake", dbname => '' }) unless $dbname; return $self->render(json => { response => "mistake", dbname => $dbname }) unless $self->app->model->dbExist($dbname); ## my $res = $self->app->model->dbDump($dbname); my $subprocess = Mojo::IOLoop::Subprocess->new; $self->app->log->info(" -- Begin dump database $dbname in subprocess"); $subprocess->run( sub { my $subprocess = shift; my $filename = $self->app->model->dbDump($dbname); do { unlink $filename; return undef; } unless $filename; my $storeRes = $self->app->model->storePut($filename, $storename, $storelogin, $storepwd); do { unlink $filename; return undef; } unless $storeRes; unlink $filename; return 1; }, sub { my ($subprocess, $err, @results) = @_; my $pid = $subprocess->pid; $self->app->log->info(" -- End dump subprocess $pid for dump database $dbname"); } ); $subprocess->ioloop->start unless $subprocess->ioloop->is_running; $self->render(json => { result => "success", $dbname => $dbname } ); } sub roleList { my $self = shift; $self->render(json => { result => "success", role => $self->app->model->roleList } ); } sub roleExist { my $self = shift; my $rolename = $self->req->param('rolename'); return $self->render(json => { result => "mistake", rolename => undef } ) unless $rolename; my $res = $self->app->model->roleExist($rolename); return $self->render(json => { result => "success", rolename => $rolename } ) if $res; $self->render(json => { result => "mistake", rolename => $rolename } ); } sub rolePassword { my $self = shift; my $rolename = $self->req->param('rolename'); return $self->render(json => { result => "mistake", rolename => undef } ) unless $rolename; $self->render(json => { result => "success", rolename => $rolename } ); } sub roleCreate { my $self = shift; my $rolename = $self->req->param('rolename'); return $self->render(json => { result => "mistake", rolename => undef } ) unless $rolename; $self->render(json => { result => "success", rolename => $rolename } ); } sub roleDrop { my $self = shift; my $rolename = $self->req->param('rolename'); return $self->render(json => { result => "mistake", rolename => undef } ) unless $rolename; $self->render(json => { result => "success", rolename => $rolename } ); } 1; #------------ #--- APP --- #------------ package PGagent; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious'; sub startup { my $self = shift; } 1; #------------ #--- MAIN --- #------------ use strict; use warnings; use utf8; use Mojo::Server::Prefork; use Mojo::IOLoop::Subprocess; use Mojo::Util qw(monkey_patch b64_encode b64_decode md5_sum getopt dumper); use Sys::Hostname qw(hostname); use File::Basename qw(basename dirname); use Apache::Htpasswd; use Cwd qw(getcwd abs_path); my $appfile = abs_path(__FILE__); my $appdir = dirname($appfile); my $appname = dirname($appfile).'/'.basename($appfile, ".pl"); $0 = $appname; my $server = Mojo::Server::Prefork->new; my $app = $server->build_app('PGagent'); my $wd = getcwd; $app->config( hostname => hostname, workdir => "/data/pgagent", listenIPv4 => "0.0.0.0", listenIPv6 => "[::]", listenPort => "3001", pghost => "127.0.0.1", pguser => "postgres", pgpasswd => "password", pwdfile => "$appname.pw", pidfile => "$appname.pid", logfile => "$appname.log", conffile => "$appname.conf", ); my $conffile = $app->config('conffile'); do { $app->log->debug("Load configuration from $conffile "); my $config = $app->plugin( 'JSONConfig', { file => $conffile } ); } if -r $conffile; $app->helper( model => sub { state $model = PGagent::Model->new( $app, $app->config("pghost"), $app->config("pguser"), $app->config("pgpasswd") ); } ); my $r = $app->routes; $r->add_condition( auth => sub { my ($route, $c, $captures, $hash) = @_; my $authStr = $c->req->headers->authorization; return undef unless $authStr; my ($authType, $encAuthPair) = split / /, $authStr; return undef unless ($authType eq 'Basic' && $encAuthPair); my ($username, $password) = split /:/, b64_decode($encAuthPair); $c->app->log->debug("Receive auth pair: '$username:$password'"); return undef unless ($username && $password); my $passwdFile = $c->app->config('pwdfile'); my $result = undef; eval { my $ht = Apache::Htpasswd->new($passwdFile); $result = $ht->htCheckPassword($username, $password); }; do { $c->app->log->debug($@); return undef; } if $@; return 1 if $result; return undef; } ); $r->any('/hello')->to('Controller#hello'); $r->any('/db/list')->to('Controller#dbList'); $r->any('/db/create')->to('Controller#dbCreate'); $r->any('/db/drop')->to('Controller#dbDrop'); $r->any('/db/rename')->to('Controller#dbRename'); $r->any('/db/copy')->to('Controller#dbCopy'); $r->any('/db/size')->to('Controller#dbSize'); $r->any('/db/dump')->to('Controller#dbDump'); $r->any('/db/exist')->to('Controller#dbExist'); $r->any('/conf/dump')->to('Controller#confDump'); $r->any('/db/owner')->to('Controller#dbOwner'); $r->any('/role/list')->to('Controller#roleList'); $r->any('/role/exist')->to('Controller#roleExist'); $r->any('/role/password')->to('Controller#rolePassword'); $r->any('/role/create')->to('Controller#roleCreate'); $r->any('/role/drop')->to('Controller#roleDrop'); #$app->mode($app->config("mode")); $app->secrets([ md5_sum(localtime(time)) ]); $app->hook(before_dispatch => sub { my $c = shift; my $remoteIP = $c->tx->remote_address; $c->app->log->info($remoteIP.' '.$c->req->method.' '.$c->req->url->to_abs->to_string); }); $app->helper('reply.exception' => sub { my $c = shift; return $c->rendered(404); }); $app->helper('reply.not_found' => sub { my $c = shift; return $c->rendered(404); }); $server->listen([ "http://".$app->config("listenIPv4").":".$app->config("listenPort"), "http://".$app->config("listenIPv6").":".$app->config("listenPort") ]); $server->heartbeat_interval(3); $server->heartbeat_timeout(60); $server->pid_file($app->config('pidfile')); my $daemon = 0; if ($daemon) { my $pid = fork; if ($pid == 0) { # setuid($appUID) if $appUID; # setgid($appGID) if $appGID; $app->log(Mojo::Log->new( path => $app->config('logfile') )); open (my $STDOUT2, '>&', STDOUT); open (STDOUT, '>>', '/dev/null'); open (my $STDERR2, '>&', STDERR); open (STDERR, '>>', '/dev/null'); chdir($appdir); local $SIG{HUP} = sub { $app->log->info('Catch HUP signal'); $app->log(Mojo::Log->new(path => $app->config('logfile'))); }; $server->run; } } else { # setuid($appUID) if $appUID; # setgid($appGID) if $appGID; $server->run; } #EOF
#!/usr/bin/env perl package PGmaster::Model; use strict; use warnings; use File::stat; use DBI; use Mojo::UserAgent; use Mojo::JSON qw(encode_json decode_json); use Mojo::Util qw(md5_sum dumper); use Socket; use POSIX; sub new { my ($class, $app, $dbhost, $dbuser, $dbpasswd, $dbname) = @_; my $self = { app => $app, dbhost => $dbhost, dsn => "dbi:Pg:dbname=$dbname;host=$dbhost", dbuser => $dbuser, dbpasswd => $dbpasswd, dbname => $dbname }; bless $self, $class; return $self; } sub dbuser { return shift->{dbuser}; } sub dbpasswd { return shift->{dbpasswd}; } sub dsn { return shift->{dsn}; } sub dbhost { return shift->{dbhost}; } sub app { return shift->{app}; } sub hostnameAlive { my ($self, $hostname) = @_; return undef unless gethostbyname($hostname); return 1; } sub parseLabel { my ($self, $label) = @_; return undef unless $label; $label =~ s/.sqlz$//; my ($dbname, $Y, $M, $D, $h, $m, $s, $tz, $host) = $label =~ /([_a-z0-9]{1,64})--([0-9]{4})([0-9]{2})([0-9]{2})-([0-9]{2})([0-9]{2})([0-9]{2})-([A-Z]{2,3})--([_\-a-zA-Z0-9\.]{1,64})/g; my %data; $data{'dbname'} = $dbname; $data{'timestamp'} = "$Y-$M-$D $h:$m:$s $tz"; $data{'source'} = $host; return \%data; } sub timestamp { my $self = shift; return strftime("%Y-%m-%d %H:%M:%S %Z", localtime(time)); } sub sizeHR { my ($self, $size) = @_; return $size if $size < 1024; return int($size/1024+0.5)."k" if ($size < 1024*1024 && $size > 1024); return int($size/(1024*1024)+0.5)."M" if ($size < 1024*1024*1024 && $size > 1024*1024); return int($size/(1024*1024*1024)+0.5)."G" if ($size < 1024*1024*1024*1024 && $size > 1024*1024*1024); } sub sizeWP { my ($self, $size) = @_; my $out = $size; $out =~ s/(\d{9})$/.$1/g if $size > 1000*1000*1000; $out =~ s/(\d{6})$/.$1/g if $size > 1000*1000; $out =~ s/(\d{3})$/.$1/g if $size > 1000; return $out; } sub sizeR { my ($self, $size) = @_; return int($size/(1024*1024)+0.5); } #--- model::agent --- sub agentList { my ($self, $id) = @_; my $where = ''; $where = "where id = $id" if $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from agent $where order by hostname;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub agentExist { my ($self, $hostname) = @_; return undef unless $hostname; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id from agent where hostname = '$hostname' limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; my $id = $row->{'id'}; return $id if $id; return undef; } sub agentHostname { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select hostname from agent where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; my $hostname = $row->{'hostname'}; return $hostname if $hostname; return undef; } sub agentProfile { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from agent where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; return $row if $row; return undef; } sub agentNextID { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id from agent order by id desc limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; my $id = $row->{'id'}; $id++; $sth->finish; $dbi->disconnect; return $id; } sub agentAdd { my ($self, $hostname, $username, $password) = @_; return undef unless $hostname; return undef unless $username; return undef unless $password; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $id = $self->agentNextID; my $query = "insert into agent (id, hostname, username, password) values ($id, '$hostname', '$username', '$password');"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $id if $rows*1 > 0; return undef; } sub agentDelete { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "delete from agent where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $rows*1; } sub agentUpdate { my ($self, $id, $hostname, $username, $password) = @_; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "update agent set hostname = '$hostname', username = '$username', password = '$password' where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $rows*1; } sub agentAlive { my ($self, $id) = @_; return undef unless $id; my $hostname = $self->agentHostname($id) or return undef; my $ua = Mojo::UserAgent->new(max_redirects => 2) or return undef; my $tx = $ua->get("http://$hostname:3001/hello"); my $res; eval { $res = decode_json($tx->result->body); }; return 1 if $res->{'message'} eq 'hello'; return undef; } sub agentDBList { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from db where agentid = $id order by name;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub agentDBUpdate { my ($self, $id) = @_; return undef unless $id; # my $dblist = $self->agentDBList($id); my $hostname = $self->agentHostname($id) or return undef; my $ua = Mojo::UserAgent->new(max_redirects => 2) or return undef; my $tx = $ua->get("http://$hostname:3001/db/list"); my $res; eval { $res = decode_json($tx->result->body); }; return undef if $@; my $dblist = $res->{'dblist'}; return undef unless $dblist; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "delete from db where agentid = $id;"; my $rows = $dbi->do($query) or return undef; return undef unless $rows; foreach my $rec (@{$dblist}) { my $dbname = $rec->{'name'}; my $dbsize = $rec->{'size'}; my $dbowner = $rec->{'owner'}; my $query = "insert into db (agentid, name, size, owner, type) values ($id, '$dbname', $dbsize, '$dbowner', 'pgsql');"; my $rows = $dbi->do($query) or return undef; } $dbi->disconnect; return $dblist; } # --- model::store --- sub storeHostname { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select hostname from store where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; my $hostname = $row->{'hostname'}; return $hostname if $hostname; return undef; } sub storeProfile { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from store where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; return $row if $row; return undef; } sub storeAlive { my ($self, $id) = @_; return undef unless $id; my $hostname = $self->storeHostname($id) or return undef; my $ua = Mojo::UserAgent->new(max_redirects => 2) or return undef; my $tx = $ua->get("http://$hostname:3002/hello"); my $res; eval { $res = decode_json($tx->result->body); }; return 1 if $res->{'message'} eq 'hello'; return undef; } sub storeList { my ($self, $id) = @_; my $where = ''; $where = "where id = '$id'" if $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd); my $query = "select * from store $where order by hostname;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub storeExist { my ($self, $hostname) = @_; return undef unless $hostname; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id from store where hostname = '$hostname' limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; my $id = $row->{'id'}; return $id if $id; return undef; } sub storeNextID { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd); my $query = "select id from store order by id desc limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; my $id = $row->{'id'}; $id++; $sth->finish; $dbi->disconnect; return $id; } sub storeAdd { my ($self, $hostname, $username, $password) = @_; return undef unless $hostname; return undef unless $username; return undef unless $password; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $id = $self->storeNextID; my $query = "insert into store (id, hostname, username, password) values ($id, '$hostname', '$username', '$password');"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $id if $rows*1 > 0; return undef; } sub storeDelete { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "delete from store where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $rows*1; } sub storeUpdate { my ($self, $id, $hostname, $username, $password) = @_; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "update store set hostname = '$hostname', username = '$username', password = '$password' where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $rows*1; } sub storeDataList { my ($self, $id) = @_; return undef unless $id; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from data where storeid = $id order by name;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub storeDataUpdate { my ($self, $id) = @_; return undef unless $id; my $hostname = $self->storeHostname($id) or return undef; my $ua = Mojo::UserAgent->new(max_redirects => 2) or return undef; my $tx = $ua->get("http://$hostname:3002/store/list"); my $res = undef; eval { $res = decode_json($tx->result->body); }; return undef if $@; my $datalist = $res->{'datalist'}; return undef unless $datalist; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "delete from data where storeid = $id;"; my $rows = $dbi->do($query) or return undef; return undef unless $rows; my @list; foreach my $rec (@{$datalist}) { my $dataname = $rec->{'name'}; my $datasize = $rec->{'size'}; my $datamtime = $rec->{'mtime'}; # Dataname pattern for dumps dbname--timestamp--sourcehost.ext next unless $dataname =~ m/.+--.+--.+\.(sqlz|sql|sql.gz|sql.xz)/; my $label = $self->app->model->parseLabel($dataname); my $dbname = $label->{'dbname'} || 'undef'; my $source = $label->{'source'} || 'undef'; my $stamp = $label->{'timestamp'} || '1970-01-01 00:00:00 UTC'; my $query = "insert into data (storeid, name, size, mtime, type, dbname, source, stamp) values ($id, '$dataname', $datasize, '$datamtime', 'pgsql', '$dbname', '$source', '$stamp');"; my $rows = $dbi->do($query) or return undef; push @list, $rec; } $dbi->disconnect; return \@list; } sub storeDataFree { }; sub dataList { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select s.hostname as store, d.* from data d, store s where s.id = d.storeid order by d.dbname;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub jobList { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select * from job order by id;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } # --- model::job ---- sub jobCreate { my ($self, @args) = @_; my $id = $self->jobNextID; my $begin = strftime("%Y-%m-%d %H:%M:%S %Z", localtime(time)); my $stop = '1970-01-01 00:00:00 UTC'; my $type = "undef"; my $author = "undef"; my $sourceid = 0; my $destid = 0; my $status = "undef"; my $error = "undef"; my $message = "Job record created"; my $magic = md5_sum(localtime(time)); my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "insert into job (id, begin, stop, author, type, sourceid, destid, status, error, message, magic) values ($id, '$begin', '$stop', '$author', '$type', $sourceid, $destid, '$status', '$error', '$message', '$magic');"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $id if $rows*1 > 0; return undef; } sub jobList { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id, to_char(begin, 'YYYY-MM-DD HH24:MI:SS TZ') as begin, to_char(stop, 'YYYY-MM-DD HH24:MI:SS TZ') as stop, author, type, sourceid, destid, status, error, message, magic from job order by id desc limit 100;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my @list; while (my $row = $sth->fetchrow_hashref) { push @list, $row; } $sth->finish; $dbi->disconnect; return \@list; } sub jobNextID { my $self = shift; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id from job order by id desc limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; my $id = $row->{'id'}; $id++; $sth->finish; $dbi->disconnect; return $id; } sub jobExist { my ($self, $id) = @_; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id from job where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; $id = $row->{'id'}; return $id if $id; return undef; } sub jobUpdate { my ($self, $id, %args) = @_; return undef unless $id; return undef unless $self->jobExist($id); my $jobProfile = $self->jobProfile($id); my $args = \%args; my $begin = $args->{'begin'} || $jobProfile->{'begin'}; my $stop = $args->{'stop'} || $jobProfile->{'stop'}; my $type = $args->{'type'} || $jobProfile->{'type'}; my $author = $args->{'author'} || $jobProfile->{'author'}; my $sourceid = $args->{'sourceid'} || $jobProfile->{'sourceid'}; my $destid = $args->{'destid'} || $jobProfile->{'destid'}; my $status = $args->{'status'} || $jobProfile->{'status'}; my $error = $args->{'error'} || $jobProfile->{'error'}; my $message = $args->{'message'} || $jobProfile->{'message'}; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "update job set begin = '$begin', stop = '$stop', author = '$author', type = '$type', sourceid = $sourceid, destid = $destid, status = '$status', error = '$error', message = '$message' where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $id if $rows; return undef; } sub jobDelete { my ($self, $id) = @_; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "delete from job where id = $id;"; my $rows = $dbi->do($query) or return undef; $dbi->disconnect; return $rows if $rows; return undef; } sub jobProfile { my ($self, $id) = @_; my $dbi = DBI->connect($self->dsn, $self->dbuser, $self->dbpasswd) or return undef; my $query = "select id, to_char(begin, 'YYYY-MM-DD HH24:MI:SS TZ') as begin, to_char(stop, 'YYYY-MM-DD HH24:MI:SS TZ') as stop, author, type, sourceid, destid, status, error, message, magic from job where id = $id limit 1;"; my $sth = $dbi->prepare($query); my $rows = $sth->execute; my $row = $sth->fetchrow_hashref; $sth->finish; $dbi->disconnect; return $row if $row; return undef; } 1; #------------------ #--- CONTROLLER --- #------------------ package PGmaster::Controller; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Util qw(md5_sum dumper); use Mojo::JSON qw(encode_json decode_json); sub hello { my $self = shift; $self->render(template => 'hello'); # my $id = $self->app->model->jobCreate; # my $idn = $self->app->model->jobUpdate($id, status => 'AAAAAAAAAAAAAAA!!!', type => 'OOOOOOOOOOOO!'); # my $job = $self->app->model->jobProfile($id); # # my $list = $self->app->model->jobList; # $self->render(text => dumper($list)); } #--- controller::agent --- sub agentList { my $self = shift; $self->render(template => 'agentList'); } sub agentFormAdd { my $self = shift; $self->render(template => 'agentFormAdd'); } sub agentFormUpdate { my $self = shift; $self->render(template => 'agentFormUpdate', req => $self->req); } sub agentAdd { my $self = shift; $self->render(template => 'agentAdd', req => $self->req); } sub agentUpdate { my $self = shift; $self->render(template => 'agentUpdate', req => $self->req); } sub agentDelete { my $self = shift; $self->render(template => 'agentDelete', req => $self->req); } sub agentAlive { my $self = shift; my $id = $self->req->param('id'); $self->render(template => 'agentAlive', req => $self->req); } sub agentDBList { my $self = shift; my $id = $self->req->param('id'); $self->render(template => 'agentDBList', req => $self->req); } sub agentDBUpdate { my $self = shift; $self->render(template => 'agentDBUpdate', req => $self->req); } # --- controller::store ---- sub storeList { my $self = shift; $self->render(template => 'storeList'); } sub storeFormAdd { my $self = shift; $self->render(template => 'storeFormAdd'); } sub storeFormUpdate { my $self = shift; $self->render(template => 'storeFormUpdate', req => $self->req); } sub storeAdd { my $self = shift; $self->render(template => 'storeAdd', req => $self->req); } sub storeUpdate { my $self = shift; $self->render(template => 'storeUpdate', req => $self->req); } sub storeDelete { my $self = shift; $self->render(template => 'storeDelete', req => $self->req); } sub storeDataList { my $self = shift; my $id = $self->req->param('id'); $self->render(template => 'storeDataList', req => $self->req); } sub storeDataUpdate { my $self = shift; $self->render(template => 'storeDataUpdate', req => $self->req); } # ---- controller::db req handlers--- sub dbCreate { my $self = shift; $self->render(template => 'dbCreate', req => $self->req); } sub dbDrop { my $self = shift; $self->render(template => 'dbDrop', req => $self->req); } sub dbRename { my $self = shift; $self->render(template => 'dbRename', req => $self->req); } sub dbCopy { my $self = shift; $self->render(template => 'dbCopy', req => $self->req); } sub dbDump { my $self = shift; $self->render(template => 'dbDump', req => $self->req); } sub dbRestore { my $self = shift; $self->render(template => 'dbRestore', req => $self->req); } sub dataList { my $self = shift; $self->render(template => 'dataList', req => $self->req); } sub jobList { my $self = shift; $self->render(template => 'jobList', req => $self->req); } 1; #----------- #--- APP --- #----------- package PGmaster; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious'; sub startup { my $self = shift; } 1; #------------ #--- MAIN --- #------------ use strict; use warnings; use utf8; use Mojo::Server::Prefork; use Mojo::IOLoop::Subprocess; use Mojo::Util qw(monkey_patch b64_encode b64_decode md5_sum getopt dumper); use File::Basename; use Sys::Hostname qw(hostname); my $server = Mojo::Server::Prefork->new; my $appname = basename(__FILE__, ".pl"); my $workdir = dirname(__FILE__); my $app = $server->build_app('PGmaster'); $app->config( hostname => hostname, libdir => "$workdir", pidfile => "$appname.pid", logfile => "$appname.log", mode => "production", dbhost => "127.0.0.1", dbname => "dumper", dbuser => "postgres", dbpasswd => "password" ); $app->helper( model => sub { state $model = PGmaster::Model->new( $app, $app->config("dbhost"), $app->config("dbuser"), $app->config("dbpasswd"), $app->config("dbname"), ); } ); # --- log all request --- $app->hook(before_dispatch => sub { my $c = shift; my $remoteIP = $c->tx->remote_address; $c->app->log->debug($remoteIP.' '.$c->req->method.' '.$c->req->url->to_abs->to_string); }); # --- log target request --- $app->hook(after_render => sub { my ($c, $output, $format) = @_; my $remote_ip = $c->tx->remote_address; my $method = $c->req->method; # $c->app->log->info($remote_ip.' '.$c->req->method.' '.$c->req->url->to_abs->to_string); my $base = $c->req->url->base->to_string; my $path = $c->req->url->path->to_string; $c->app->log->info('Processed '.$remote_ip.' '.$method.' '.$base.''.$path); }); $app->max_request_size(1024*1024*1024); $app->moniker($appname); #$app->mode('production'); $app->secrets([ md5_sum(localtime(time)) ]); #$app->log(Mojo::Log->new(path => $app->config('logfile'))); $app->static->paths->[0] = $app->config('libdir').'/public'; $app->renderer->paths->[0] = $app->config('libdir').'/templs'; my $r = $app->routes; $r->any('/db/create')->to('Controller#dbCreate'); $r->any('/db/drop')->to('Controller#dbDrop'); $r->any('/db/copy')->to('Controller#dbCopy'); $r->any('/db/rename')->to('Controller#dbRename'); $r->any('/db/dump')->to('Controller#dbDump'); $r->any('/db/restore')->to('Controller#dbRestore'); $r->any('/hello')->to('Controller#hello'); $r->any('/agent/list')->to('Controller#agentList'); $r->any('/agent/form/add')->to('Controller#agentFormAdd'); $r->any('/agent/form/update')->to('Controller#agentFormUpdate'); $r->any('/agent/add')->to('Controller#agentAdd'); $r->any('/agent/update')->to('Controller#agentUpdate'); $r->any('/agent/delete')->to('Controller#agentDelete'); $r->any('/agent/alive')->to('Controller#agentAlive'); $r->any('/agent/dblist')->to('Controller#agentDBList'); $r->any('/agent/dbupdate')->to('Controller#agentDBUpdate'); $r->any('/store/list')->to('Controller#storeList'); $r->any('/store/form/add')->to('Controller#storeFormAdd'); $r->any('/store/form/update')->to('Controller#storeFormUpdate'); $r->any('/store/add')->to('Controller#storeAdd'); $r->any('/store/update')->to('Controller#storeUpdate'); $r->any('/store/delete')->to('Controller#storeDelete'); $r->any('/store/datalist')->to('Controller#storeDataList'); $r->any('/store/dataupdate')->to('Controller#storeDataUpdate'); $r->any('/db/create')->to('Controller#dbCreate'); $r->any('/db/drop')->to('Controller#dbDrop'); $r->any('/db/copy')->to('Controller#dbCopy'); $r->any('/db/rename')->to('Controller#dbRename'); $r->any('/db/dump')->to('Controller#dbDump'); $r->any('/db/restore')->to('Controller#dbRestore'); $r->any('/data/list')->to('Controller#dataList'); $r->any('/job/list')->to('Controller#jobList'); $server->listen(["http://0.0.0.0:3000"]); $server->pid_file($app->config('pidfile')); $server->heartbeat_interval(3); $server->heartbeat_timeout(60); $server->run; #EOF
#!/usr/bin/env perl #---------------------------------------- #--- BIG CONTROLLER --- #--- OOO, IT IS MEET LAUF CONTROLLER --- #---------------------------------------- package PGstore::Controller; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Util qw(quote b64_encode b64_decode md5_sum dumper url_escape); use Mojo::JSON qw(encode_json decode_json); use File::Basename; use Encode qw(encode decode_utf8 ); use Filesys::Df; use File::stat; use POSIX; sub renderFile { my $self = shift; my %args = @_; utf8::decode($args{filename}) if $args{filename} && !utf8::is_utf8($args{filename}); utf8::decode($args{filepath}) if $args{filepath} && !utf8::is_utf8($args{filepath}); my $filename = $args{filename}; my $status = $args{status} || 200; my $content_disposition = $args{content_disposition} || 'attachment'; my $cleanup = $args{cleanup} // 0; # Content type based on format my $content_type; $content_type = $self->app->types->type( $args{format} ) if $args{format}; $content_type ||= 'application/octet-stream'; # Create asset my $asset; if ( my $filepath = $args{filepath} ) { unless ( -f $filepath && -r $filepath ) { $self->app->log->error("Cannot read file [$filepath]. error [$!]"); return $self->rendered(404); } $filename ||= fileparse($filepath); $asset = Mojo::Asset::File->new( path => $filepath ); $asset->cleanup($cleanup); } elsif ( $args{data} ) { $filename ||= $self->req->url->path->parts->[-1] || 'download'; $asset = Mojo::Asset::Memory->new(); $asset->add_chunk( $args{data} ); } else { $self->app->log->error('You must provide "data" or "filepath" option'); return; } # Set response headers my $headers = $self->res->content->headers(); $filename = quote($filename); # quote the filename, per RFC 5987 $filename = encode("UTF-8", $filename); $headers->add( 'Content-Type', $content_type . '; name=' . $filename ); $headers->add( 'Content-Disposition', $content_disposition . '; filename=' . $filename ); # Range, partially based on Mojolicious::Static if ( my $range = $self->req->headers->range ) { my $start = 0; my $size = $asset->size; my $end = $size - 1 >= 0 ? $size - 1 : 0; # Check range if ( $range =~ m/^bytes=(\d+)-(\d+)?/ && $1 <= $end ) { $start = $1; $end = $2 if defined $2 && $2 <= $end; $status = 206; $headers->add( 'Content-Length' => $end - $start + 1 ); $headers->add( 'Content-Range' => "bytes $start-$end/$size" ); } else { # Not satisfiable return $self->rendered(416); } # Set range for asset $asset->start_range($start)->end_range($end); } else { $headers->add( 'Content-Length' => $asset->size ); } # Stream content directly from file $self->res->content->asset($asset); return $self->rendered($status); } sub hello { my $self = shift; $self->render(json => { message => "hello", result => "success" }); } sub confDump { my $self = shift; $self->render(json => { config => $self->app->config, result => "success" }); } sub storeList { my $self = shift; my $datadir = $self->app->config("datadir"); return $self->render(json => { datalist => undef, result => 'mistake' }) unless -d $datadir; return $self->render(json => { datalist => undef, result => 'mistake' }) unless -r $datadir; opendir(my $dh, $datadir); my @list; while (my $name = readdir($dh)) { next if ($name =~ m/^\./); my $datafile = "$datadir/$name"; next if -d $datafile; next unless -r $datafile; my $st = stat($datafile); my $mtime = strftime("%Y-%m-%d %H:%M:%S %Z", localtime($st->mtime)); push (@list, { name => $name, mtime => $mtime, size => $st->size }); } closedir $dh; $self->render(json => { datalist => \@list, result => 'success' }); } sub storeGet { my $self = shift; my $dataname = $self->req->param('dataname'); my $datadir = $self->app->config("datadir"); my $file = "/$datadir/$dataname"; return $self->rendered(404) unless -r $file; return $self->rendered(404) if -d $file; $self->renderFile(filepath => "$file"); } sub storePut { my $self = shift; my $datadir = $self->app->config("datadir"); return $self->render(json => { datalist => undef, result => 'mistake', message => "Datadir not exist" }) unless -d $datadir; return $self->render(json => { datalist => undef, result => 'mistake', message => "Datadir not writable" }) unless -w $datadir; return $self->rendered(406) if $self->req->is_limit_exceeded; my @filenames; my $uploads = $self->req->uploads; foreach my $upload (@{$uploads}) { my $dataname = $upload->filename =~ s/[^\w\d\.]+/_/gr; my $datasize = $upload->size; my $df = df("/$datadir", 1); return $self->rendered(406) if $df->{bfree}+1024 < $datasize; my $datafile = "/$datadir/$dataname"; $upload->move_to($datafile); # Check real file size my $st = stat($datafile); my $realsize = $st->size; $self->app->log->info("Dataset $dataname size $datasize was uploaded to $datafile with size $realsize"); do { unlink $dataname; $self->app->log->info("Error: Dataset $dataname was deleted since datafile size not equal HTTP requested size"); next; } if $datasize != $realsize; push @filenames, { name => $dataname, size => $datasize, realsize => $realsize, realname => $datafile }; } $self->render(json => { datalist => \@filenames , result => "success"}); } sub storeFree { my $self = shift; my $datadir = $self->app->config("datadir"); return $self->render(json => { total => 0, free => 0, result => 'mistake', message => 'Datadir not exist' }) unless -d $datadir; return $self->render(json => { total => 0, free => 0, result => 'mistake', message => 'Connot read datadir' }) unless -r $datadir; my $df = df("/$datadir", 1); $self->render(json => { total => $df->{blocks}, free => $df->{bfree}, result => 'success' }); } sub storeDelete { my $self = shift; my $datadir = "/".$self->app->config("datadir"); my $dataname = $self->req->param('dataname'); return $self->render(json => { dataname => $dataname, result => 'mistake' }) unless $dataname; return $self->render(json => { dataname => $dataname, result => 'mistake', message => 'Datadir not exist' }) unless -d $datadir; return $self->render(json => { dataname => $dataname, result => 'mistake', message => 'Cannot read datadir' }) unless -w $datadir; my $datafile = "$datadir/$dataname"; return $self->render(json => { dataname => $dataname, result => 'success', size => -1, message => 'The dataset not exist' }) unless -f $datafile; my $st = stat($datafile); my $datasize = $st->size; return $self->render(json => { dataname => $dataname, result => 'success', size => $datasize }) if unlink($datafile); $self->render(json => { dataname => $dataname, result => 'mistake', size => $datasize }); } sub storeLink { my $self = shift; $self->render(json => { result => 'success' }); } sub storeRename { my $self = shift; $self->render(json => { result => 'success' }); } sub storeMigrate { my $self = shift; $self->render(json => { result => 'success' }); } 1; #----------- #--- APP --- #----------- package PGstore; use utf8; use strict; use warnings; use Mojo::Base 'Mojolicious'; sub startup { my $self = shift; } 1; #------------ #--- MAIN --- #------------ use strict; use warnings; use utf8; use Mojo::Server::Prefork; use Mojo::IOLoop::Subprocess; use Mojo::Util qw(monkey_patch b64_encode b64_decode md5_sum getopt dumper); use Sys::Hostname qw(hostname); use File::Basename qw(basename dirname); use Apache::Htpasswd; use Cwd qw(getcwd abs_path); use EV; my $appfile = abs_path(__FILE__); my $appdir = dirname($appfile); my $appname = dirname($appfile).'/'.basename($appfile, ".pl"); $0 = $appname; my $server = Mojo::Server::Prefork->new; my $app = $server->build_app('PGstore'); my $wd = getcwd; $app->config( hostname => hostname, datadir => "/data/pgstore", listenIPv4 => "0.0.0.0", listenIPv6 => "[::]", listenPort => "3002", pghost => "127.0.0.1", pguser => "postgres", pgpasswd => "password", pwdfile => "$appname.pw", pidfile => "$appname.pid", logfile => "$appname.log", conffile => "$appname.conf", maxrequestsize => 1024*1024*1024, ); $ENV{MOJO_TMPDIR} = $app->config("datadir"); $app->max_request_size($app->config("maxrequestsize")); $app->moniker($appname); $app->secrets([ md5_sum(localtime(time)) ]); my $conffile = $app->config('conffile'); do { $app->log->debug("Load configuration from $conffile "); my $config = $app->plugin( 'JSONConfig', { file => $conffile } ); } if -r $conffile; #$app->helper( # model => sub { # state $model = PGstore::Model->new($app); # } #); my $r = $app->routes; $r->any('/hello')->to('Controller#hello'); $r->any('/store/list')->to('Controller#storeList'); $r->any('/store/get')->to('Controller#storeGet'); $r->any('/store/put')->to('Controller#storePut'); $r->any('/store/free')->to('Controller#storeFree'); $r->any('/store/delete')->to('Controller#storeDelete'); $r->any('/conf/dump')->to('Controller#confDump'); $r->any('/store/link')->to('Controller#storeLink'); $r->any('/store/rename')->to('Controller#storeRename'); $r->any('/store/migrate')->to('Controller#storeMigrate'); $r->add_condition( auth => sub { my ($route, $c, $captures, $hash) = @_; my $authStr = $c->req->headers->authorization; return undef unless $authStr; my ($authType, $encAuthPair) = split / /, $authStr; return undef unless ($authType eq 'Basic' && $encAuthPair); my ($username, $password) = split /:/, b64_decode($encAuthPair); $c->app->log->debug("Receive auth pair: '$username:$password'"); return undef unless ($username && $password); my $passwdFile = $c->app->config('pwdfile'); my $result = undef; eval { my $ht = Apache::Htpasswd->new($passwdFile); $result = $ht->htCheckPassword($username, $password); }; do { $c->app->log->debug($@); return undef; } if $@; return 1 if $result; return undef; } ); #$app->mode($app->config("mode")); $app->secrets([ md5_sum(localtime(time)) ]); $app->hook(before_dispatch => sub { my $c = shift; my $remoteIP = $c->tx->remote_address; $c->app->log->info($remoteIP.' '.$c->req->method.' '.$c->req->url->to_abs->to_string); }); $app->helper('reply.exception' => sub { my $c = shift; return $c->rendered(404); }); $app->helper('reply.not_found' => sub { my $c = shift; return $c->rendered(404); }); $server->listen([ "http://".$app->config("listenIPv4").":".$app->config("listenPort"), "http://".$app->config("listenIPv6").":".$app->config("listenPort") ]); $server->heartbeat_interval(3); $server->heartbeat_timeout(60); $server->pid_file($app->config('pidfile')); my $daemon = 0; if ($daemon) { my $pid = fork; if ($pid == 0) { # setuid($appUID) if $appUID; # setgid($appGID) if $appGID; $app->log(Mojo::Log->new( path => $app->config('logfile') )); open (my $STDOUT2, '>&', STDOUT); open (STDOUT, '>>', '/dev/null'); open (my $STDERR2, '>&', STDERR); open (STDERR, '>>', '/dev/null'); chdir($appdir); local $SIG{HUP} = sub { $app->log->info('Catch HUP signal'); $app->log(Mojo::Log->new(path => $app->config('logfile'))); }; $server->run; } } else { # setuid($appUID) if $appUID; # setgid($appGID) if $appGID; $server->run; } #EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $hostname = $req->param('hostname'); % my $username = $req->param('username'); % my $password = $req->param('password'); % my $agentID = $self->app->model->agentExist($hostname); % do { <div class="callout alert"> Agent <%= $hostname %> yet exist. </div> % } if $agentID; % do { % my $agentID = $self->app->model->agentAdd($hostname, $username, $password); % do { <div class="callout success"> Agent <%= $hostname %> was added successful with id <%= $agentID %>. </div> <div class="text-center"> <a class="button" href="/agent/list">Agent List</a> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> <a class="button" href="/agent/form/add">Add yet</a> </div> % } if $agentID; % do { <div class="callout alert"> Agent <%= $hostname %> was added unsuccessful. </div> <div class="text-center"> <a class="button" href="/agent/list">Agent List</a> <a class="button" href="/agent/form/add">Add yet</a> </div> % } unless $agentID; % } unless $agentID; %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $model = $self->app->model; % my $agentID = $req->param('id'); % my $agentHostname = $model->agentHostname($agentID); % my $agentAlive = $model->agentAlive($agentID); % unless ($agentAlive) { <div class="callout alert"> Oops...Agent <%= $agentHostname %> not responded. Use cached database list. </div> % } % if ($agentAlive) { % $model->agentDBUpdate($agentID); % } % my $dbList = $self->app->model->agentDBList($agentID); % my $storeList = $self->app->model->storeList; % my $num = 1; % foreach my $db (@{$dbList}) { % my $dbname = $db->{'name'}; % my $dbsize = $db->{'size'}; % my $dbowner = $db->{'owner'}; %# --- dump database --- <div class="reveal" id="modal-dump-<%= $dbname %>" data-reveal> <div class="text-center"> <h5>Dump database <%= $dbname %> on <%= $agentHostname %></h5> </div> <form accept-charset="UTF-8" action="/db/dump" method="get" data-abide novalidate> <input type="hidden" name="agentid" value="<%= $agentID %>" /> <input type="hidden" name="dbname" value="<%= $dbname %>" /> <label>Store hostname <select name="storeid" id="store-hostname" required> <option value=""></option> % foreach my $store (@{$storeList}) { % my $storeName = $store->{'hostname'}; % my $storeID = $store->{'id'}; <option value="<%= $storeID %>"><%= $storeName %></option> % } </select> </label> <p class="text-center"> <button type="submit" class="button success">Accept</button> <button class="button" data-close="modal-dump-<%= $dbname %>" type="button">Escape</button> </p> </form> <button class="close-button" data-close="modal-dump-<%= $dbname %>" type="button">×</button> </div> %# --- drop database --- <div class="reveal" id="modal-drop-<%= $dbname %>" data-reveal> <div class="text-center"> <h5>Drop database <%= $dbname %> on <%= $agentHostname %> </h5> </div> <form accept-charset="UTF-8" action="/db/drop" method="get" data-abide novalidate> <input type="hidden" name="agentid" value="<%= $agentID %>" /> <input type="hidden" name="dbname" value="<%= $dbname %>" /> <p class="text-center"> <button type="submit" class="button alert">Accept</button> <button class="button" data-close="modal-drop-<%= $dbname %>" type="button">Escape</button> </p> </form> <button class="close-button" data-close="modal-drop-<%= $dbname %>" type="button">×</button> </div> %# --- rename database --- <div class="reveal" id="modal-rename-<%= $dbname %>" data-reveal> <div class="text-center"> <h5>Rename database <%= $dbname %> on <%= $agentHostname %> </h5> </div> <form accept-charset="UTF-8" action="/db/rename" method="get" data-abide novalidate> <input type="hidden" name="agentid" value="<%= $agentID %>" /> <input type="hidden" name="dbname" value="<%= $dbname %>" /> <label>New name <input type="text" name="newname" value="<%= $dbname %>" placeholder="new db name" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{3,64}"/> <span class="form-error">mandatory, 3 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button success">Accept</button> <button class="button" data-close="modal-rename-<%= $dbname %>" type="button">Escape</button> </p> </form> <button class="close-button" data-close="modal-rename-<%= $dbname %>" type="button">×</button> </div> %# --- copy database --- <div class="reveal" id="modal-copy-<%= $dbname %>" data-reveal> <div class="text-center"> <h5>Copy database <%= $dbname %> on <%= $agentHostname %> </h5> </div> <form accept-charset="UTF-8" action="/db/copy" method="get" data-abide novalidate> <input type="hidden" name="agentid" value="<%= $agentID %>" /> <input type="hidden" name="dbname" value="<%= $dbname %>" /> <label>Copy name <input type="text" name="newname" value="<%= $dbname %>_copy" placeholder="db name of copy" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{3,64}"/> <span class="form-error">mandatory, 3 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button success">Accept</button> <button class="button" data-close="modal-copy-<%= $dbname %>" type="button">Escape</button> </p> </form> <button class="close-button" data-close="modal-copy-<%= $dbname %>" type="button">×</button> </div> %# --- operation modal --- <div class="reveal" id="modal-<%= $dbname %>" data-reveal> <div class="text-center"> <h5><i class="fi-database" style="font-size:1.3em;"></i> Database <%= $dbname %> on <%= $agentHostname %> </h5> <div class="stacked-for-small button-group"> <a class="button" href="#" data-open="modal-dump-<%= $dbname %>"><i class="fi-archive" style="font-size:1.3em;"></i> Dump</a> <a class="button" href="#" data-open="modal-rename-<%= $dbname %>"><i class="fi-pencil" style="font-size:1.3em;"></i> Rename</a> <a class="button" href="#" data-open="modal-copy-<%= $dbname %>"><i class="fi-page-copy" style="font-size:1.3em;"></i> Copy</a> <a class="button alert" href="#" data-open="modal-drop-<%= $dbname %>"><i class="fi-trash" style="font-size:1.3em;"></i> Drop</a> </div> </div> <button class="close-button" data-close="modal-<%= $dbname %>" type="button">×</button> </div> % $num++; % } <div class="text-center"> <h5> Agent <%= $agentHostname%> database list <a href="/agent/dblist?id=<%= $agentID %>"><i class="fi-refresh" style="font-size:1.3rem;"></i></a> </h5> </div> <table id="table" class="display" > <thead> <tr> <td>#</td> <td>dbname</td> <td>size</td> <td>owner</td> </tr> </thead> <tbody> % $num = 1; % foreach my $db (@{$dbList}) { % my $dbname = $db->{'name'}; % my $dbsize = $db->{'size'}; % my $dbowner = $db->{'owner'}; <tr> <td><%= $num %></td> <td><a href="#" data-open="modal-<%= $dbname %>"><%= $dbname %></a></td> <td><%= $dbsize %></td> <td><%= $dbowner %></td> </tr> % $num++; % } </tbody> </table> <script> $.extend(true, $.fn.dataTable.defaults, { "searching": false, "ordering": true } ); $(document).ready(function() { $('#table').DataTable(); }); </script> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $id = $req->param('id'); % my $model = $self->app->model; % my $agentHostname = $model->agentHostname($id); % my $res = $model->hostnameAlive($agentHostname); % my $dbList; % do { $dbList = $model->agentDBUpdate($id); } if $res; % do { <div class="callout alert"> Oops...Agent hostname <%= $agentHostname %> not resolved. </div> % } unless $dbList; % do { <div class="callout success"> Agent <%= $agentHostname %> DB list updated </div> % } if $dbList; <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $id %>">DBList</a> <a class="button hollow" href="/agent/dbupdate?id=<%= $id %>">DBLUpdate</a> </div> % do { <table id="table" class="display" > <thead> <tr> <td>#</td> <td>name</td> <td>size</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $db (@{$dbList}) { % my $name = $db->{'name'}; % my $size = $db->{'size'}; % my $owner = $db->{'owner'}; <tr> <td><%= $num %></td> <td><%= $name %></td> <td><%= $size %></td> </tr> % $num++; % } </tbody> </table> % } if $dbList; %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $id = $req->param('id'); % my $res = $self->app->model->agentDelete($id); % do { <div class="callout success"> Agent id <%= $id %> was deleted successful. </div> % } if $res; % do { <div class="callout alert"> Agent id <%= $id %> was deleted unsuccessful. </div> % } unless $res; <div class="text-center"> <a class="button hollow" href="/agent/list">Agent List</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; <div class="row columns"> <form accept-charset="UTF-8" id="user-create-form" action="/agent/add" method="post" data-abide novalidate> <h5>Add DB agent</h5> <label>hostname <input type="text" name="hostname" placeholder="agent hostname" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{3,64}"/> <span class="form-error">mandatory, 3 or more letter</span> </label> <label>username <input type="text" name="username" placeholder="login" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <label>password <input type="text" name="password" placeholder="password" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button">Accept</button> </p> </form> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $id = $req->param('id'); % my $agent = pop @{$self->app->model->agentList($id)}; % my $hostname = $agent->{'hostname'}; % my $username = $agent->{'username'}; % my $password = $agent->{'password'}; <form accept-charset="UTF-8" action="/agent/update" method="post" data-abide novalidate> <h5>Update DB agent <%= $agent->{"hostname"} %></h5> <input type="hidden" name="id" value="<%= $id %>"/> <label>hostname <input type="text" name="hostname" placeholder="hostname" value="<%= $hostname %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory parameter, 5 or more letter</span> </label> <label>username <input type="text" name="username" placeholder="username" value="<%= $username %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <label>password <input type="password" name="password" placeholder="password" value="<%= $password %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button">Accept</button> </p> </form> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(b64_encode b64_decode md5_sum dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $agentList = $self->app->model->agentList; % foreach my $agent (@{$agentList}) { % my $agentID = $agent->{'id'}; % my $hostname = $agent->{'hostname'}; <div class="reveal" id="modal-delete-<%= $agentID %>" data-reveal> <div class="text-center"> <h5>Do you want to delete agent <%= $hostname %>?</h5> <a class="button hollow" data-close aria-label="Close modal">Escape</a> <a class="button hollow alert" href="/agent/delete?id=<%= $agentID %>">Delete</a> </div> <button class="close-button" data-close aria-label="Close modal" type="button"> <span aria-hidden="true">×</span> </button> </div> <div class="reveal" id="modal-create-<%= $agentID %>" data-reveal> <h5>Create new database on agent [<%= $hostname %>] </h5> <form accept-charset="UTF-8" action="/db/create" method="get" data-abide novalidate> <input type="hidden" name="agentid" value="<%= $agentID %>" /> <label>New name <input type="text" name="dbname" value="new_empty_db" placeholder="empty_db" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{3,64}"/> <span class="form-error">mandatory, 3 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button">Accept</button> </p> </form> <button class="close-button" data-close="modal-create-<%= $agentID %>" type="button">×</button> </div> <div class="reveal" id="modal-<%= $agentID %>" data-reveal> <div class="text-center"> <h5>Agent <%= $hostname %></h5> <div class="stacked-for-small button-group"> <a class="button" href="/agent/form/update?id=<%= $agentID %>">Reconf</a> <a class="button" href="/agent/dblist?id=<%= $agentID %>">DBList</a> <a class="button" href="#" data-open="modal-create-<%= $agentID %>">DBCreate</a> <a class="button" href="/agent/dbupdate?id=<%= $agentID %>">DBUpdate</a> <a class="button alert" href="#" data-open="modal-delete-<%= $agentID %>">Delete</a> </div> </div> <button class="close-button" data-close aria-label="Close modal" type="button"> <span aria-hidden="true">×</span> </button> </div> % } <div class="text-center"> <h5>Agent list <a href="/agent/list"><i class="fi-refresh" style="font-size:1.3rem;"></i></a></h5> </div> <table id="table" class="display" > <thead> <tr> <td>#</td> <td>hostname</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $agent (@{$agentList}) { % my $agentID = $agent->{'id'}; % my $hostname = $agent->{'hostname'}; <tr> <td><%= $num %></td> <td><a href="#" data-open="modal-<%= $agentID %>"><%= $hostname %></a></td> </tr> % $num++; % } </tbody> </table> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $agentID = $req->param('id'); % my $hostname = $req->param('hostname'); % my $username = $req->param('username'); % my $password = $req->param('password'); % my $res = $self->app->model->agentUpdate($agentID, $hostname, $username, $password); % do { <div class="callout success"> Agent <%= $hostname %> was updated successful. </div> % } if $res; % do { <div class="callout alert"> Agent <%= $hostname %> was updated unsuccessful. </div> % } unless $res; <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $dataList = $self->app->model->dataList; <div class="text-center"> <h5>Generic dataset list</h5> </div> <table id="table" class="display" > <thead> <tr> <td>#</td> <td>store</td> <td>dbdump</td> <td>date</td> <td>size</td> <td>source</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $data (@{$dataList}) { % my $filename = $data->{'name'}; % my $dbname = $data->{'dbname'}; % my $stamp = $data->{'stamp'}; % my $size = $data->{'size'}; % my $store = $data->{'store'}; % my $storeID = $data->{'storeid'}; % my $source = $data->{'source'}; <tr> <td><%= $num %></td> <td><a href="/store/datalist?id=<%= $storeID %>"><%= $store %></a></td> <td><%= $dbname %></td> <td><%= $stamp %></td> <td><%= $size %></td> <td><%= $source %></td> </tr> % $num++; % } </tbody> </table> <script> $.extend(true, $.fn.dataTable.defaults, { "searching": true, "ordering": true, "lengthChange": false, }); $(document).ready(function() { $('#table').DataTable({ "info": false, "processing": true }); }); </script> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Mojo::JSON qw(j encode_json decode_json); % use utf8; % use strict; % my $model = $self->app->model; % my $dbname = $req->param('dbname') || undef; % my $agentID = $req->param('agentid') || undef; % my $newname = $req->param('newname') || undef; % my $agentProfile = $model->agentProfile($agentID); % my $agentHostname = $agentProfile->{'hostname'} || ''; % my $agentUsername = $agentProfile->{'username'} || ''; % my $agentPassword = $agentProfile->{'password'} || ''; % my $jobID = $model->jobCreate; % $model->jobUpdate($jobID, type => 'dbcopy', sourceid => $agentID, destid => $agentID); % my $jobMagic = $model->jobProfile($jobID)->{'magic'}; % my $ua = Mojo::UserAgent->new(max_redirects => 5); % my $param = "dbname=$dbname"; % $param = $param."&newname=$newname"; % my $tx = $ua->get("http://$agentHostname:3001/db/copy?$param"); % my $body = '{"result":"mistake"}'; % eval { $body = $tx->result->body }; % my $res = decode_json($body); % do { % $model->jobUpdate($jobID, status => 'done', error => 'noerr', stop => $model->timestamp); <div class="callout success"> Agent <%= $agentHostname %> copy database <%= $dbname %> to <%= $newname %> as job <%= $jobID %>. </div> % $model->agentDBUpdate($agentID); % } if $res->{'result'} eq 'success'; % do { % $model->jobUpdate($jobID, status => 'pushjob', error => 'pusherr', stop => $model->timestamp); <div class="callout alert"> Agent <%= $agentHostname %> cannot copy <%= $dbname %>. Job <%= $jobID %> was cancel. </div> % } if $res->{'result'} ne 'success'; <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Mojo::JSON qw(j encode_json decode_json); % use utf8; % use strict; % my $model = $self->app->model; % my $dbname = $req->param('dbname') || undef; % my $agentID = $req->param('agentid') || undef; % my $agentProfile = $model->agentProfile($agentID); % my $agentHostname = $agentProfile->{'hostname'} || ''; % my $agentUsername = $agentProfile->{'username'} || ''; % my $agentPassword = $agentProfile->{'password'} || ''; % my $jobID = $model->jobCreate; % $model->jobUpdate($jobID, type => 'dbdrop', sourceid => $agentID, destid => $agentID); % my $jobMagic = $model->jobProfile($jobID)->{'magic'}; % my $ua = Mojo::UserAgent->new(max_redirects => 5); % my $param = "dbname=$dbname"; % my $tx = $ua->get("http://$agentHostname:3001/db/drop?$param"); % my $body = '{"result":"mistake"}'; % eval { $body = $tx->result->body }; % my $res = decode_json($body); % do { % $model->jobUpdate($jobID, status => 'done', error => 'noerr', stop => $model->timestamp); <div class="callout success"> Agent <%= $agentHostname %> drop database <%= $dbname %> as job <%= $jobID %>. </div> % $model->agentDBUpdate($agentID); % } if $res->{'result'} eq 'success'; % do { % $model->jobUpdate($jobID, status => 'pushjob', error => 'pusherr', stop => $model->timestamp); <div class="callout alert"> Agent <%= $agentHostname %> cannot drop <%= $dbname %>. Job <%= $jobID %> was cancel. </div> % } if $res->{'result'} ne 'success'; <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Mojo::JSON qw(j encode_json decode_json); % use utf8; % use strict; % my $model = $self->app->model; % my $dbname = $req->param('dbname') || undef; % my $agentID = $req->param('agentid') || undef; % my $storeID = $req->param('storeid') || undef; % my $agentProfile = $model->agentProfile($agentID); % my $agentHostname = $agentProfile->{'hostname'} || ''; % my $agentUsername = $agentProfile->{'username'} || ''; % my $agentPassword = $agentProfile->{'password'} || ''; % my $storeProfile = $model->storeProfile($storeID); % my $storeHostname = $storeProfile->{'hostname'} || ''; % my $storeUsername = $storeProfile->{'username'} || ''; % my $storePassword = $storeProfile->{'password'} || ''; % my $masterHostname = $self->app->config('hostname'); % my $jobID = $model->jobCreate; % $model->jobUpdate($jobID, type => 'dbdump', sourceid => $agentID, destid => $storeID); % my $jobMagic = $model->jobProfile($jobID)->{'magic'}; % my $agentAlive = $model->agentAlive($agentID); % unless ($agentAlive) { <div class="callout alert"> Oops...Agent <%= $agentHostname %> not respond. Please, check agent configuration. </div> % $model->jobUpdate($jobID, error => 'connecterr', stop => $model->timestamp); % } % my $storeAlive = $model->storeAlive($storeID); % unless ($storeAlive) { <div class="callout alert"> Oops.. Store <%= $storeHostname %> not respond. Please, check store configuration. </div> % $model->jobUpdate($jobID, error => 'connecterr', stop => $model->timestamp); % } % if ($agentAlive && $storeAlive) { % my $ua = Mojo::UserAgent->new(max_redirects => 5); % my $param = "dbname=$dbname"; % $param = $param."&store=$storeHostname"; % $param = $param."&storelogin=$storeUsername"; % $param = $param."&storepwd=$storePassword"; % $param = $param."&master=$masterHostname"; % $param = $param."&jobid=$jobID"; % $param = $param."&magic=$jobMagic"; % $model->jobUpdate($jobID, status => 'pushjob'); % my $tx = $ua->get("http://$agentUsername:$agentPassword\@$storeHostname:3001/db/dump?$param"); % my $body = '{"result":"mistake"}'; % eval { $body = $tx->result->body }; % my $res = decode_json($body); % if ( $res->{'result'} eq 'success' ) { <div class="callout success"> Agent <%= $agentHostname %> now create dump of database <%= $dbname %> as job <%= $jobID %>. </div> % } % if ( $res->{'result'} ne 'success' ) { <div class="callout alert"> Agent <%= $agentHostname %> not correct responded for the request. Job <%= $jobID %> was cancel. </div> % $model->jobUpdate($jobID, error => 'pushjoberr', stop => $model->timestamp); % } % } <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> <a class="button hollow" href="/store/datalist?id=<%= $storeID %>">DataList</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Mojo::JSON qw(j encode_json decode_json); % use utf8; % use strict; % my $model = $self->app->model; % my $dbname = $req->param('dbname') || undef; % my $agentID = $req->param('agentid') || undef; % my $newname = $req->param('newname') || undef; % my $agentProfile = $model->agentProfile($agentID); % my $agentHostname = $agentProfile->{'hostname'} || ''; % my $agentUsername = $agentProfile->{'username'} || ''; % my $agentPassword = $agentProfile->{'password'} || ''; <div class="text-center"> <a class="button hollow" href="/agent/dblist?id=<%= $agentID %>">DBList</a> </div> % my $jobID = $model->jobCreate; % $model->jobUpdate($jobID, type => 'dbrename', sourceid => $agentID, destid => $agentID); % my $jobMagic = $model->jobProfile($jobID)->{'magic'}; % my $ua = Mojo::UserAgent->new(max_redirects => 5); % my $param = "dbname=$dbname"; % $param = $param."&newname=$newname"; % my $tx = $ua->get("http://$agentHostname:3001/db/rename?$param"); % my $body = '{"result":"mistake"}'; % eval { $body = $tx->result->body }; % my $res = decode_json($body); % do { % $model->jobUpdate($jobID, status => 'done', error => 'noerr', stop => $model->timestamp); <div class="callout success"> Agent <%= $agentHostname %> rename database <%= $dbname %> to <%= $newname %> as job <%= $jobID %>. </div> % $model->agentDBUpdate($agentID); % } if $res->{'result'} eq 'success'; % do { % $model->jobUpdate($jobID, status => 'pushjob', error => 'pusherr', stop => $model->timestamp); <div class="callout alert"> Agent <%= $agentHostname %> cannot rename <%= $dbname %>. Job <%= $jobID %> was cancel. </div> % } if $res->{'result'} ne 'success'; %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; %#EOF
%# %# $Id: exception.html.ep 627 2017-04-15 13:02:08Z ziggi $ %# % layout 'default'; % title 'Error'; <h5>Oops... Exception</h5> <pre> %= $exception </pre> %#EOF
%# %# $Id: exception.html.ep 627 2017-04-15 13:02:08Z ziggi $ %# % layout 'default'; % title 'Error'; <h5>Oops... Exception</h5> <pre> %= $exception </pre> %#EOF
% layout 'default'; % title 'Dumper Hello'; % use Mojo::Util qw(b64_encode b64_decode md5_sum dumper); % use Encode qw(decode encode); % use utf8; % use strict; <h5>Hello, body! How are you there?</h5> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use utf8; % use strict; % use Switch; % use Mojo::Util qw(dumper); % my $model = $self->app->model; % my $jobList = $model->jobList; <div class="text-center"> <h5> Job list <a href="/job/list"><i class="fi-refresh" style="font-size:1.3rem;"></i></a> </h5> </div> <table id="table" class="display" > <thead> <tr> <td>#</td> <td>type</td> <td>status</td> <td>err</td> <td>source</td> <td>dest</td> <td>start</td> <td>last</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $job (@{$jobList}) { % my $jobID = $job->{'id'}; % my $type = $job->{'type'}; % my $sourceID = $job->{'sourceid'}; % my $destID = $job->{'destid'}; % my $sourcehost = ''; % my $desthost = ''; % my $status = $job->{'status'}; % my $start = $job->{'begin'}; % my $stop = $job->{'stop'}; % my $error = $job->{'error'} || 'noerr'; % if ($type eq 'dbdump') { % $sourcehost = $model->agentProfile($sourceID)->{'hostname'} if $sourceID; % $desthost = $model->storeProfile($destID)->{'hostname'} if $destID; % } % if ($type eq 'dbcopy' || $type eq 'dbrename' || $type eq 'dbdrop') { % $sourcehost = $model->agentProfile($sourceID)->{'hostname'} if $sourceID; % $desthost = ''; % } % $error = '' if ($error eq 'noerr' || $error eq 'undef'); % $stop = '' if ($stop =~ /1970-01-01/); <tr> <td><%= $jobID %></td> <td><%= $type %></td> <td><%= $status %></td> <td><%= $error %></td> <td><%= $sourcehost %></td> <td><%= $desthost %></td> <td><%= $start %></td> <td><%= $stop %></td> </tr> % $num++; % } </tbody> </table> <script> $.extend(true, $.fn.dataTable.defaults, { "searching": false, "ordering": true, "lengthChange": false, }); %#$(document).ready(function() { %# $('#table').DataTable({ %# "info": false, %# "processing": true %# }); %#}); </script> %#EOF
%# %# $Id: not_found.html.ep 627 2017-04-15 13:02:08Z ziggi $ %# % layout 'default'; % title 'Dumper'; <h5>404 Page not found</h5> %#EOF
%# %# $Id: not_found.html.ep 627 2017-04-15 13:02:08Z ziggi $ %# % layout 'default'; % title 'Dumper'; <h5>404 Page not found</h5> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $hostname = $req->param('hostname'); % my $username = $req->param('username'); % my $password = $req->param('password'); % my $id = $self->app->model->storeExist($hostname); % do { <div class="callout alert"> Store <%= $hostname %> yet exist. </div> % } if $id; % do { % my $id = $self->app->model->storeAdd($hostname, $username, $password); % do { <div class="callout success"> Store <%= $hostname %> was added successful with id <%= $id %>. </div> % } if $id; % do { <div class="callout alert"> Store <%= $hostname %> was added unsuccessful =( </div> % } unless $id; % } unless $id; <div class="text-center"> <a class="button" href="/store/list">List</a> <a class="button" href="/store/form/add">Add yet</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $storeID = $req->param('id'); % my $storeHostname = $self->app->model->storeHostname($storeID); % my $storeAlive = $self->app->model->storeAlive($storeID); % unless ($storeAlive) { <div class="callout alert"> Oops.. Store <%= $storeHostname %> not respond. Please, check store configuration. Now will use cached dataset list. </div> % } % if ($storeAlive) { % $self->app->model->storeDataUpdate($storeID); % } % my $dataList = $self->app->model->storeDataList($storeID); <div class="text-center"> <h5> Store <%= $storeHostname%> dataset list <a href="/store/datalist?id=<%= $storeID %>"> <i class="fi-refresh" style="font-size:1.3rem;"></i> </a> </h5> </div> <table id="table" class="display" > <thead> <tr> <td>#</td> <td>dbname</td> <td>date</td> <td>size</td> <td>source</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $data (@{$dataList}) { % my $filename = $data->{'name'}; % my $dbname = $data->{'dbname'}; % my $stamp = $data->{'stamp'}; % my $size = $data->{'size'}; % my $source = $data->{'source'}; <tr> <td><%= $num %></td> <td><%= $dbname %></td> <td><%= $stamp %></td> <td><%= $size %></td> <td><%= $source %></td> </tr> % $num++; % } </tbody> </table> <script> $.extend(true, $.fn.dataTable.defaults, { "searching": true, "ordering": true, "lengthChange": false } ); $(document).ready(function() { $('#table').DataTable(); }); </script> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $id = $req->param('id'); % my $model = $self->app->model; % my $storeHostname = $model->storeHostname($id); % my $res = $model->hostnameAlive($storeHostname); % my $dataList; % do { $dataList = $model->storeDataUpdate($id); } if $res; % do { <div class="callout alert"> Oops...Store hostname <%= $storeHostname %> not resolved. </div> % } unless $dataList; % do { <div class="callout success"> Store <%= $storeHostname %> data list updated </div> % } if $dataList; <div class="text-center"> <a class="button hollow" href="/store/dataupdate?id=<%= $id %>">DataUpdate</a> <a class="button hollow" href="/store/form/update?id=<%= $id %>">Reconf</a> <a class="button hollow" href="/store/datalist?id=<%= $id %>">DataList</a> </div> % do { <table id="table" class="display" > <thead> <tr> <td>#</td> <td>name</td> <td>size</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $db (@{$dataList}) { % my $name = $db->{'name'}; % my $size = $db->{'size'}; % my $owner = $db->{'owner'}; <tr> <td><%= $num %></td> <td><%= $name %></td> <td><%= $size %></td> </tr> % $num++; % } </tbody> </table> % } if $dataList; %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $id = $req->param('id'); % my $res = $self->app->model->storeDelete($id); % do { <div class="callout success"> Store id <%= $id %> was deleted successful. </div> % } if $res; % do { <div class="callout alert"> Store id <%= $id %> was deleted unsuccessful. </div> % } unless $res; <div class="text-center"> <a class="button" href="/store/list">List</a> <a class="button" href="/store/form/add">Add</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; <div class="row columns"> <form accept-charset="UTF-8" id="user-create-form" action="/store/add" method="post" data-abide novalidate> <h5>Add DB store</h5> <label>hostname <input type="text" name="hostname" placeholder="store hostname" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{3,64}"/> <span class="form-error">mandatory, 3 or more letter</span> </label> <label>username <input type="text" name="username" placeholder="login" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <label>password <input type="text" name="password" placeholder="password" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button">Accept</button> </p> </form> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $id = $req->param('id'); % my $store = pop @{$self->app->model->storeList($id)}; % my $hostname = $store->{'hostname'}; % my $username = $store->{'username'}; % my $password = $store->{'password'}; <form accept-charset="UTF-8" action="/store/update" method="post" data-abide novalidate> <h5>Update DB store <%= $store->{"hostname"} %></h5> <input type="hidden" name="id" value="<%= $id %>"/> <label>hostname <input type="text" name="hostname" placeholder="hostname" value="<%= $hostname %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory parameter, 5 or more letter</span> </label> <label>username <input type="text" name="username" placeholder="username" value="<%= $username %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <label>password <input type="password" name="password" placeholder="password" value="<%= $password %>" required pattern="[a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{5,42}"/> <span class="form-error">mandatory, 5 or more letter</span> </label> <p class="text-center"> <button type="submit" class="button">Accept</button> </p> </form> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(b64_encode b64_decode md5_sum dumper); % use Encode qw(decode encode); % use utf8; % use strict; % my $storeList = $self->app->model->storeList; % foreach my $store (@{$storeList}) { % my $id = $store->{'id'}; % my $hostname = $store->{'hostname'}; <div class="reveal" id="modal-<%= $id %>" data-reveal> <div class="text-center"> <h5>Store <%= $hostname %></h5> <div class="stacked-for-small button-group"> <a class="button" href="/store/form/update?id=<%= $id %>">Reconf</a> <a class="button" href="/store/datalist?id=<%= $id %>">DataList</a> <a class="button" href="/store/dataupdate?id=<%= $id %>">DataUpdate</a> <a class="button alert" href="#" data-open="modal-delete-<%= $id %>">Delete</a> </div> </div> <button class="close-button" data-close aria-label="Close modal" type="button"> <span aria-hidden="true">×</span> </button> </div> <div class="reveal" id="modal-delete-<%= $id %>" data-reveal> <div class="text-center"> <h5>Do you want to delete agent <%= $hostname %>?</h5> <a class="button hollow expanded" data-close aria-label="Close modal">Escape</a> <a class="button hollow expanded alert" href="/store/delete?id=<%= $id %>">Delete</a> </div> <button class="close-button" data-close aria-label="Close modal" type="button"> <span aria-hidden="true">×</span> </button> </div> % } <table id="table" class="display" > <thead> <tr> <td>#</td> <td>hostname</td> </tr> </thead> <tbody> % my $num = 1; % foreach my $store (@{$storeList}) { % my $id = $store->{'id'}; % my $hostname = $store->{'hostname'}; <tr> <td><%= $num %></td> <td><a href="#" data-open="modal-<%= $id %>"><%= $hostname %></a></td> </tr> % $num++; % } </tbody> </table> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; % my $id = $req->param('id'); % my $hostname = $req->param('hostname'); % my $username = $req->param('username'); % my $password = $req->param('password'); % my $res = $self->app->model->storeUpdate($id, $hostname, $username, $password); % do { <div class="callout success"> Store <%= $hostname %> was updated successful. </div> % } if $res; % do { <div class="callout alert"> Store <%= $hostname %> was updated unsuccessful. </div> % } unless $res; <div class="text-center"> <a class="button" href="/store/list">List</a> <a class="button" href="/store/form/add">Add</a> </div> %#EOF
%# %# $Id$ %# % layout 'default'; % title 'Dumper'; % use Mojo::Util qw(dumper); % use utf8; % use strict; %#EOF