Skip to content

Commit

Permalink
Basic info and containers working
Browse files Browse the repository at this point in the history
  • Loading branch information
dave-lang committed Jul 11, 2023
1 parent e4a465d commit b62594d
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 40 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
# Webmin module for basic docker tasks

Created to manage a local Docker install for a home server. Allows listing of containers; viewing basic container perf stats and docker state; and starting/stopping/restarting containers.

It uses the Docker CLI and abuses the `--format {{}}` arg for output parsing.

## Development

Docker configuration has been setup to allow easier development.

This environment has webmin and docker already installed, along with a very basic Ubuntu 18 docker config ready to start in the container (Docker in Docker).

1. `cd tools`
2. `docker-compose up`
2. `docker-compose up -d` to run docker compose as daemon
3. Open http://localhost:10000 to access the webmin console

Use `docker exec -it webmin_master /bin/bash` to get a SSH console.

Docker will not be running by default, use `dockerd` to run it, Ctrl+C to stop.

To setup the test DinD container
56 changes: 48 additions & 8 deletions docker/docker-lib.pl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ sub get_status {
}

my $json = decode_json($status);
if ($json->{ ServerErrors }) {
return $json->{ ServerErrors };
if ($json->{ServerErrors}) {
return $json->{ServerErrors}[0];
}

return 0, $json;
Expand Down Expand Up @@ -55,25 +55,46 @@ sub get_containers
sub get_stats
{
my ($containers, $fail);
my $code = execute_command('docker stats --all --no-stream --format "{{.ID}},{{.CPUPerc}},{{.MemPerc}}"', undef, \$containers, \$fail, 0, 1);
my $code = execute_command('docker stats --all --no-stream --format "{{.ID}},{{.CPUPerc}},{{.MemPerc}},{{.MemUsage}}"', undef, \$containers, \$fail, 0, 1);

if ($code != 0) {
return $fail;
}

#{"BlockIO":"0B / 0B","CPUPerc":"0.00%","Container":"344327f11366","ID":"344327f11366","MemPerc":"0.00%","MemUsage":"0B / 0B","Name":"objective_dhawan","NetIO":"0B / 0B","PIDs":"0"}
# return 0, $containers;
my %results = ( );
my @containers = split(/\n/, $containers);

foreach my $u (@containers) {
my ($id, $cpu, $mem, $memUsage) = split(/,/, $u);
$results{$id} = {
'cpu' => $cpu,
'mem' => $mem,
'memUsage' => $memUsage
};
}

return 0, %results;
}

sub inspect_container
{
my ($containers, $fail);
my $code = execute_command('docker inspect --all --no-stream --format "{{.ID}},{{.CPUPerc}},{{.MemPerc}},{{.MemUsage}}"', undef, \$containers, \$fail, 0, 1);

if ($code != 0) {
return $fail;
}

my %results = ( );
my @containers = split(/\n/, $containers);

foreach my $u (@containers) {
my ($id, $cpu, $mem) = split(/,/, $u);
my ($id, $cpu, $mem, $memUsage) = split(/,/, $u);
$results{$id} = {
'cpu' => $cpu,
'mem' => $mem
'mem' => $mem,
'memUsage' => $memUsage
};
# $results{$id} = $id;
}

return 0, %results;
Expand All @@ -98,4 +119,23 @@ sub restart_container
my($container) = @_;
my ($output, $fail);
execute_command('docker restart ' . $container, undef, \$output, \$fail, 0, 1);
}

sub circular_grid
{
my($statsRaw, $depth) = @_;
$depth ||= 1;

my @stats;
foreach my $field ( keys %{$statsRaw}) {
if (ref $statsRaw->{$field} eq ref {}) { # If hash down the rabbit hole we go
push (@stats, sprintf("<b>%s</b>: %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("<b>%s</b>: %s<br />", $field, join(",", @{$statsRaw->{$field}})));
} else { # If hash down the rabbit hole we go
push (@stats, sprintf("<b>%s</b>: %s<br />", $field, $statsRaw->{$field} ||= "N/A"));
}
}

return ui_grid_table(\@stats, $depth == 1 ? 2 : 1);
}
47 changes: 21 additions & 26 deletions docker/index.cgi
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,48 @@ use Data::Dumper;

require 'docker-lib.pl';

my %text;

ui_print_header(undef, $text{'index_title'}, "", undef, 1, 1);

my($status_fail, @status) = get_status();
@tabs = ( [ 'info', $text{'tab_info'} ],
[ 'containers', $text{'tab_containers'} ] );

print ui_tabs_start(\@tabs, 'info', 'containers', 1);

# INFO TAB
print ui_tabs_start_tab('mode', 'info');
my($status_fail, $status) = get_status();
if ($status_fail) {
print ui_alert_box($fail, 'danger');
print ui_alert_box($status_fail, 'danger');
} else {
print Dumper($status);
print circular_grid($status); # Ugly recursive output
}
print ui_tabs_end_tab('mode', 'info');


# CONTAINERS TAB
print ui_tabs_start_tab('mode', 'containers');
my($fail, @containers) = get_containers();
my($stat_fail, %stats) = get_stats();
# print(get_status());

print Dumper(\%stats);

print ui_subheading($text{'index_containers_title'});
if ($fail) {
print ui_alert_box($fail, 'danger');
} else {
print ui_columns_start(['name', 'label', 'running for', ' ', ' ', ' ' ]);
print ui_columns_start([$text{'label_name'}, $text{'label_label'}, $text{'label_runningfor'}, $text{'label_cpu'}, $text{'label_mem'}, ' ' ]);
foreach my $u (@containers) {
my $container_stats = \%stats{$u->{'id'}};
print ui_columns_row([
html_escape($u->{'name'}),
html_escape($u->{'image'}),
html_escape($u->{'status'}),
html_escape($container_stats->{'cpu'}),
html_escape($container_stats->{'mem'}),
"<a href='command.cgi?c=start&container=" . urlize($u->{'name'}) . "'>Start</a>",
"<a href='command.cgi?c=stop&container=" . urlize($u->{'name'}) . "'>Stop</a>",
"<a href='command.cgi?c=restart&container=" . urlize($u->{'name'}) . "'>Restart</a>",
html_escape($stats{$u->{'id'}}{'cpu'}),
html_escape($stats{$u->{'id'}}{'memUsage'}) . " (" . html_escape($stats{$u->{'id'}}{'mem'}) . ")",
sprintf("<a href='command.cgi?c=start&container=%s'>%s</a>", urlize($u->{'name'}), $text{'command_start'}),
sprintf("<a href='command.cgi?c=stop&container=%s'>%s</a>", urlize($u->{'name'}), $text{'command_stop'}),
sprintf("<a href='command.cgi?c=restart&container=%s'>%s</a>", urlize($u->{'name'}), $text{'command_restart'}),
]);
}
print ui_columns_end();
}
print ui_tabs_end_tab('mode', 'containers');

print ui_hr();

print ui_subheading($text{'index_stats_title'});

if ($fail) {
print ui_alert_box($stat_fail, 'danger');
} else {
print $stats, "T";
}
print ui_tabs_end();

ui_print_footer("/", $text{'index'});
17 changes: 14 additions & 3 deletions docker/lang/en
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
index_title=Docker container management
index_containers_title=Containers
index_stats_title=Stats

command_err=Command could not be run
tab_info=Docker Info
tab_containers=Containers

command_err=Command could not be run

label_name=Name
label_label=Label
label_runningfor=Running for
label_cpu=CPU
label_mem=MEM

command_start=Start
command_stop=Stop
command_restart=Restart
2 changes: 0 additions & 2 deletions tools/dind/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
FROM ubuntu:18.04

LABEL maintainer="Bibin Wilson <bibinwilsonn@gmail.com>"

RUN apt-get update && \
apt-get -qy full-upgrade && \
apt-get install -qy curl && \
Expand Down
8 changes: 8 additions & 0 deletions tools/dind/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3.5'
name: 'webmin'
services:
webmin:
container_name: internal_test
privileged: true
build:
context: ./

0 comments on commit b62594d

Please sign in to comment.