diff --git a/docker/command.cgi b/docker/command.cgi index d752bc0..b40f354 100644 --- a/docker/command.cgi +++ b/docker/command.cgi @@ -1,25 +1,30 @@ #!/usr/bin/perl +use strict; +use warnings; require './docker-lib.pl'; -ui_print_header(undef, &text('index_title'), "", undef, 1, 1); +#ui_print_header(undef, &text('index_title'), "", undef, 1, 1); &ReadParse(); -&error_setup($text{'command_err'}); +&error_setup(text('command_err')); -$command = $in{'c'}; +our (%in); +my $command = $in{'c'}; + +my $err; if ($command == 'start') { - start_container($in{'container'}); + $err = container_command($in{'container'}, 'start'); } if ($command == 'stop') { - stop_container($in{'container'}); + $err = container_command($in{'container'}, 'stop'); } if ($command == 'restart') { - restart_container($in{'container'}) + $err = container_command($in{'container'}, 'restart') } -#&error($err) if ($err); +&error($err) if ($err); &webmin_log(ucfirst($command), 'docker container', $in{'container'}); &redirect(""); \ No newline at end of file diff --git a/docker/container.cgi b/docker/container.cgi new file mode 100644 index 0000000..4500ac0 --- /dev/null +++ b/docker/container.cgi @@ -0,0 +1,53 @@ +#!/usr/bin/perl +use strict; +use warnings; +use Data::Dumper; + +require './docker-lib.pl'; +&ReadParse(); + +our (%in); + +my $container = $in{'container'}; +my $tab = $in{'tab'}; + +ui_print_header(undef, text('view_container_title', $container), "", undef, undef, $in{'nonavlinks'}); + + +my @tabs = ( [ 'log', &text('tab_log') ], + [ 'inspect', &text('tab_inspect') ] ); + +print ui_tabs_start(\@tabs, 'log', $tab, 1); + +# LOGS TAB +print ui_tabs_start_tab('mode', 'log'); +my ($failed, $result) = container_logs($container); +if ($failed) { + print ui_alert_box($failed, 'danger'); +} else { + print &ui_form_start("container.cgi"); + print &ui_hidden("container", $container),"\n"; + print &ui_hidden("tab", "log"),"\n"; + print &ui_submit(text('label_refresh')); + print &ui_form_end(),"
\n"; + + print "
" . $result . "
"; +} +print ui_tabs_end_tab('mode', 'log'); + +# INSPECT TAB +print ui_tabs_start_tab('mode', 'inspect'); +my ($failedB, $resultB) = inspect_container($container); +if ($failedB) { + print ui_alert_box($failedB, 'danger'); +} else { + print "
" . $resultB . "
"; +} +print ui_tabs_end_tab('mode', 'inspect'); + +print ui_tabs_end(); + +&ui_print_footer("", text('index_return')); + +# if using authentic theme, enable codemirror for readability +print ""; diff --git a/docker/docker-lib.pl b/docker/docker-lib.pl index 9302a81..88155a0 100644 --- a/docker/docker-lib.pl +++ b/docker/docker-lib.pl @@ -11,34 +11,49 @@ init_config(); -sub get_status { - my $fail; - my $status = "{}"; - my $code = execute_command('docker info --format "{{json .}}"', undef, \$status, \$fail, 0, 1); +sub docker_command { + my($command, $format, $safe) = @_; + $format ||= ""; + $safe ||= 1; + + if ($format) { + $format = ' --format "' . $format . '"' + } + + my ($result, $fail); + my $code = execute_command('docker ' . $command . $format, undef, \$result, \$fail, 0, $safe); if ($code != 0) { - return $fail; + return $code, $fail; } - my $json = decode_json($status); - if ($json->{ServerErrors}) { - return $json->{ServerErrors}[0]; + return 0, $result +} + + +sub get_status { + my ($code, $result) = docker_command('info'); + if ($code != 0) { + return $result; } - return 0, $json; + # my $json = decode_json($result); + # if ($json->{ServerErrors}) { + # return $json->{ServerErrors}[0]; + # } + + return 0, $result; } sub get_containers { - my ($containers, $fail); - my $code = execute_command('docker container ls --all --format {{.ID}},{{.Names}},{{.Image}},{{.Status}}', undef, \$containers, \$fail, 0, 1); - + my ($code, $result) = docker_command('container ls --all ', '{{.ID}},{{.Names}},{{.Image}},{{.Status}}'); if ($code != 0) { - return $fail; + return $result; } my @results; - my @containers = split(/\n/, $containers); + my @containers = split(/\n/, $result); foreach my $u (@containers) { my ($id, $name, $image, $status) = split(/,/, $u); push (@results, { @@ -54,15 +69,13 @@ sub get_containers sub get_stats { - my ($containers, $fail); - my $code = execute_command('docker stats --all --no-stream --format "{{.ID}},{{.CPUPerc}},{{.MemPerc}},{{.MemUsage}}"', undef, \$containers, \$fail, 0, 1); - + my ($code, $result) = docker_command('stats --all --no-stream', '{{.ID}},{{.CPUPerc}},{{.MemPerc}},{{.MemUsage}}'); if ($code != 0) { - return $fail; + return $result; } my %results = ( ); - my @containers = split(/\n/, $containers); + my @containers = split(/\n/, $result); foreach my $u (@containers) { my ($id, $cpu, $mem, $memUsage) = split(/,/, $u); @@ -79,56 +92,40 @@ sub get_stats sub inspect_container { my($container) = @_; - my ($result, $fail); - # my $code = execute_command('docker inspect ' . $container . ' --format "{{json .}}"', undef, \$result, \$fail, 0, 1); - my $code = execute_command('docker inspect ' . $container, undef, \$result, \$fail, 0, 1); - + my ($code, $result) = docker_command('inspect ' . $container . ' --type=container --size'); if ($code != 0) { - return $fail; + return $result; } - - # my $json = decode_json($result); - # if ($json->{ServerErrors}) { - # return $json->{ServerErrors}[0]; - # } - - # return 0, $json, $result; + return 0, $result; } sub container_logs { my($container) = @_; - my ($result, $fail); - my $code = execute_command('docker logs ' . $container, undef, \$result, \$fail, 0, 1); - + my ($code, $result) = docker_command('logs ' . $container); if ($code != 0) { - return $fail; + return $result; } return 0, $result; } -sub start_container +sub container_command { - my($container) = @_; - my ($output, $fail); - execute_command('docker start ' . $container, undef, \$output, \$fail, 0, 1); -} + my($container, $command) = @_; -sub stop_container -{ - my($container) = @_; - my ($output, $fail); - execute_command('docker stop ' . $container, undef, \$output, \$fail, 0, 1); -} + if (!($command !~~ ['start', 'stop', 'restart'])) { + return "Command not allowed"; + } + + my ($code, $result) = docker_command($command . ' ' . $container); + if ($code != 0) { + return $result; + } -sub restart_container -{ - my($container) = @_; - my ($output, $fail); - execute_command('docker restart ' . $container, undef, \$output, \$fail, 0, 1); + return $result; } sub circular_grid @@ -136,16 +133,25 @@ sub circular_grid my($statsRaw, $depth) = @_; $depth ||= 1; + print Dumper($statsRaw); + my $result = ui_table_start($depth > 1 ? "" : "Info"); + my @stats; foreach my $field ( keys %{$statsRaw}) { if (ref $statsRaw->{$field} eq ref {}) { # If hash down the rabbit hole we go - push (@stats, sprintf("%s: %s", $field, circular_grid($statsRaw->{$field}, $depth + 1))); + $result = $result . ui_table_hr() . ui_table_span($field); + $result = $result . ui_table_span(circular_grid($statsRaw->{$field}, $depth + 1)); + #push (@stats, sprintf("%s: %s", $field, circular_grid($statsRaw->{$field}, $depth + 1))); } elsif (ref $statsRaw->{$field} eq 'ARRAY') { # Make the brave assumption we can flatten and join the array - push (@stats, sprintf("%s: %s
", $field, join(",", @{$statsRaw->{$field}}))); + $result = $result . ui_table_row($field . "(ARR)", join(",", @{$statsRaw->{$field}})); #, [cols], [&td-tags]) + # push (@stats, sprintf("%s: %s
", $field, join(",", @{$statsRaw->{$field}}))); } else { # If hash down the rabbit hole we go - push (@stats, sprintf("%s: %s
", $field, $statsRaw->{$field} ||= "N/A")); + $result = $result . ui_table_row($field, $statsRaw->{$field} eq "" ? "[N/A]" : $statsRaw->{$field}); #, [cols], [&td-tags]) + #push (@stats, sprintf("%s: %s
", $field, $statsRaw->{$field} ||= "N/A")); } } + $result = $result . ui_table_end(); - return ui_grid_table(\@stats, $depth == 1 ? 2 : 1); + # return ui_grid_table(\@stats, 1); + return $result; } \ No newline at end of file diff --git a/docker/index.cgi b/docker/index.cgi index 4a5c551..f6df253 100644 --- a/docker/index.cgi +++ b/docker/index.cgi @@ -5,7 +5,7 @@ use Data::Dumper; require 'docker-lib.pl'; -ui_print_header(undef, &text('index_title'), "", undef, 1, 1); +ui_print_header(undef, &text('index_title'), "", undef, undef, 1); if (!&has_command('docker')) { &ui_print_endpage(&text('not_installed')); #, "$config{'syslog_conf'}", "../config.cgi?$module_name")); @@ -22,7 +22,8 @@ my($status_fail, $status) = get_status(); if ($status_fail) { print ui_alert_box($status_fail, 'danger'); } else { - print circular_grid($status); # Ugly recursive output + #print circular_grid($status); # Ugly recursive output + print "
" . $status . "
"; } print ui_tabs_end_tab('mode', 'info'); @@ -34,6 +35,10 @@ my($stat_fail, %stats) = get_stats(); if ($fail) { print ui_alert_box($fail, 'danger'); } else { + print &ui_form_start(""); + print &ui_submit(text('label_refresh')); + print &ui_form_end(),"
\n"; + print ui_columns_start([&text('label_name'), &text('label_label'), &text('label_runningfor'), &text('label_cpu'), &text('label_mem'), ' ' ]); foreach my $u (@containers) { print ui_columns_row([ @@ -45,8 +50,8 @@ if ($fail) { sprintf("%s", urlize($u->{'name'}), &text('label_start')), sprintf("%s", urlize($u->{'name'}), &text('label_stop')), sprintf("%s", urlize($u->{'name'}), &text('label_restart')), - &ui_link('logs.cgi?container=' . urlize($u->{'id'}), 'View log'), - &ui_link('inspect.cgi?container=' . urlize($u->{'id'}) . '&view=1', 'Inspect container') + &ui_link('container.cgi?tab=log&container=' . urlize($u->{'id'}), 'View log'), + &ui_link('container.cgi?tab=inspect&container=' . urlize($u->{'id'}), 'Inspect') ]); } print ui_columns_end(); @@ -59,3 +64,5 @@ print ui_tabs_end(); ui_print_footer("/", &text(('index'))); # if using authentic theme, enable codemirror for readability +print ""; + diff --git a/docker/inspect.cgi b/docker/inspect.cgi deleted file mode 100644 index 2277818..0000000 --- a/docker/inspect.cgi +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use Data::Dumper; - -require './docker-lib.pl'; -&ReadParse(); - -our (%in); - -my $container = $in{'container'}; - -ui_print_header("".&html_escape($container)."", text('index_title'), "", undef, undef, $in{'nonavlinks'}); - -my ($failed, $result) = inspect_container($container); -if ($failed) { - &ui_print_endpage(ui_alert_box($failed, 'danger'), "Back", "/"); -} - -print &ui_form_start("inspect.cgi"); -print &ui_hidden("container", $container),"\n"; -print &ui_submit(text('label_refresh')); -print &ui_form_end(),"
\n"; - -print "
" . $result . "
"; - -&ui_print_footer("", text('index_return')); - -# if using authentic theme, enable codemirror for readability -print ""; diff --git a/docker/lang/en b/docker/lang/en index 33e586b..f63504a 100644 --- a/docker/lang/en +++ b/docker/lang/en @@ -1,9 +1,14 @@ -index_title=Docker container management not_installed=Docker was not found on this system + +index_title=Docker container management index_return=container list +view_container_title=Container $1 + tab_info=Docker Info tab_containers=Containers +tab_log=Log +tab_inspect=Inspect command_err=Command could not be run diff --git a/docker/logs.cgi b/docker/logs.cgi deleted file mode 100644 index ef0d934..0000000 --- a/docker/logs.cgi +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use Data::Dumper; - -require './docker-lib.pl'; -&ReadParse(); - -our (%in); - -my $container = $in{'container'}; - -ui_print_header("".&html_escape($container)."", text('index_title'), "", undef, undef, $in{'nonavlinks'}); - -my ($failed, $result) = container_logs($container); -if ($failed) { - &ui_print_endpage(ui_alert_box($failed, 'danger'), "Back", "/"); -} - -print &ui_form_start("inspect.cgi"); -print &ui_hidden("container", $container),"\n"; -print &ui_submit(text('label_refresh')); -print &ui_form_end(),"
\n"; - -print "
" . Dumper($result) . "
"; - -&ui_print_footer("", text('index_return')); - -# if using authentic theme, enable codemirror for readability -print ""; diff --git a/docker/module.info b/docker/module.info index 0c26543..be04e3f 100644 --- a/docker/module.info +++ b/docker/module.info @@ -1,3 +1,4 @@ desc=Docker container management os_support=*-linux -category=servers \ No newline at end of file +category=servers +longdesc=View and perform basic actions on docker containers on the host \ No newline at end of file