Merge pull request #1616 from mtongnz/network-display-improvements

Network info display improvements
This commit is contained in:
tom mortensen
2024-08-08 23:16:15 -07:00
committed by GitHub
4 changed files with 92 additions and 28 deletions

View File

@@ -31,8 +31,8 @@ $cpus = cpu_list();
<link type="text/css" rel="stylesheet" href="<?autov("/plugins/dynamix.docker.manager/styles/style-$theme.css")?>">
<table id="docker_containers" class="tablesorter shift">
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Port Mappings)_ <small>(_(App to Host)_)</small></th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
<tbody id="docker_list"><tr><td colspan='8'></td></tr></tbody>
<thead><tr><th><a id="resetsort" class="nohand" onclick="resetSorting()" title="_(Reset sorting)_"><i class="fa fa-th-list"></i></a>_(Application)_</th><th>_(Version)_</th><th>_(Network)_</th><th>_(Container IP)_</th><th>_(Container Port)_</th><th>_(LAN IP:Port)_</th><th>_(Volume Mappings)_ <small>(_(App to Host)_)</small></th><th class="load advanced">_(CPU & Memory load)_</th><th class="nine">_(Autostart)_</th><th class="five">_(Uptime)_</th></tr></thead>
<tbody id="docker_list"><tr><td colspan='9'></td></tr></tbody>
</table>
<input type="button" onclick="addContainer()" value="_(Add Container)_" style="display:none">
<input type="button" onclick="startAll()" value="_(Start All)_" style="display:none">
@@ -183,3 +183,4 @@ window.onunload = function(){
dockerload.stop();
}
</script>

View File

@@ -49,11 +49,11 @@ $port = file_exists('/sys/class/net/br0') ? 'BR0' : (file_exists('/sys/class/net
// Docker configuration file - guaranteed to exist
$docker_cfgfile = '/boot/config/docker.cfg';
if (file_exists($docker_cfgfile)) {
exec("grep -Pom2 '_SUBNET_|_{$port}(_[0-9]+)?=' $docker_cfgfile",$cfg);
if (isset($cfg[0]) && $cfg[0]=='_SUBNET_' && empty($cfg[1])) {
# interface has changed, update configuration
exec("sed -ri 's/_(BR0|BOND0|ETH0)(_[0-9]+)?=/_{$port}\\2=/' $docker_cfgfile");
}
exec("grep -Pom2 '_SUBNET_|_{$port}(_[0-9]+)?=' $docker_cfgfile",$cfg);
if (isset($cfg[0]) && $cfg[0]=='_SUBNET_' && empty($cfg[1])) {
# interface has changed, update configuration
exec("sed -ri 's/_(BR0|BOND0|ETH0)(_[0-9]+)?=/_{$port}\\2=/' $docker_cfgfile");
}
}
$defaults = (array)@parse_ini_file("$docroot/plugins/dynamix.docker.manager/default.cfg");
@@ -255,7 +255,7 @@ class DockerTemplates {
$doc = new DOMDocument();
$doc->load($file['path']);
if ($name) {
if ($doc->getElementsByTagName('Name')->item(0)->nodeValue !== $name) continue;
if (@$doc->getElementsByTagName('Name')->item(0)->nodeValue !== $name) continue;
}
$TemplateRepository = DockerUtil::ensureImageTag($doc->getElementsByTagName('Repository')->item(0)->nodeValue??'');
if ($TemplateRepository && $TemplateRepository==$Repository) {
@@ -293,7 +293,7 @@ class DockerTemplates {
}
public function getAllInfo($reload=false,$com=true,$communityApplications=false) {
global $dockerManPaths, $host;
global $driver, $dockerManPaths, $host;
$DockerClient = new DockerClient();
$DockerUpdate = new DockerUpdate();
//$DockerUpdate->verbose = $this->verbose;
@@ -307,9 +307,8 @@ class DockerTemplates {
$tmp['paused'] = $ct['Paused'];
$tmp['autostart'] = in_array($name,$autoStart);
$tmp['cpuset'] = $ct['CPUset'];
$tmp['url'] = $tmp['url'] ?? '';
$tmp['url'] = $ct['Url'] ?? $tmp['url'] ?? '';
// read docker label for WebUI & Icon
if (isset($ct['Url']) && !$tmp['url']) $tmp['url'] = $ct['Url'];
if (isset($ct['Icon'])) $tmp['icon'] = $ct['Icon'];
if (isset($ct['Shell'])) $tmp['shell'] = $ct['Shell'];
if (!$communityApplications) {
@@ -322,8 +321,18 @@ class DockerTemplates {
// non-templated webui, user specified
$tmp['url'] = $webui;
} else {
$ip = ($ct['NetworkMode']=='host'||_var($port,'NAT')) ? $host : _var($port,'IP');
if ($ct['NetworkMode']=='host') {
$ip = $host;
} elseif (isset($driver[$ct['NetworkMode']]) && ($driver[$ct['NetworkMode']] == 'ipvlan' || $driver[$ct['NetworkMode']] == 'macvlan')) {
$ip = reset($ct['Networks'])['IPAddress'];
} elseif (!is_null(_var($port,'PublicPort'))) {
$ip = $host;
} else {
$ip = _var($port,'IP');
}
$tmp['url'] = $ip ? (strpos($tmp['url'],$ip)!==false ? $tmp['url'] : $this->getControlURL($ct, $ip, $tmp['url'])) : $tmp['url'];
if (strpos($ct['NetworkMode'], 'container:') === 0)
$tmp['url'] = '';
}
if ( ($tmp['shell'] ?? false) == false )
$tmp['shell'] = $this->getTemplateValue($image, 'Shell');
@@ -338,8 +347,12 @@ class DockerTemplates {
$tmp['updated'] = var_export($DockerUpdate->getUpdateStatus($image),true);
}
if (!$com) $tmp['updated'] = 'undef';
if (empty($tmp['template']) || $reload) $tmp['template'] = $this->getUserTemplate($name);
if ($reload) $DockerUpdate->updateUserTemplate($name);
if ($ct['Manager'] !== 'dockerman')
$tmp['template'] = null;
else if (empty($tmp['template']) || $reload) {
$tmp['template'] = $this->getUserTemplate($name);
if ($reload) $DockerUpdate->updateUserTemplate($name);
}
//$this->debug("\n$name");
//foreach ($tmp as $c => $d) $this->debug(sprintf(' %-10s: %s', $c, $d));
}
@@ -897,7 +910,7 @@ class DockerClient {
}
public function getDockerContainers() {
global $driver;
global $driver, $host;
// Return cached values
if (is_array($this::$containersCache)) return $this::$containersCache;
$this::$containersCache = [];
@@ -922,23 +935,43 @@ class DockerClient {
$c['Icon'] = $info['Config']['Labels']['net.unraid.docker.icon'] ?? false;
$c['Url'] = $info['Config']['Labels']['net.unraid.docker.webui'] ?? false;
$c['Shell'] = $info['Config']['Labels']['net.unraid.docker.shell'] ?? false;
$c['Manager'] = $info['Config']['Labels']['net.unraid.docker.managed'] ?? false;
$c['Ports'] = [];
$c['Networks'] = [];
if ($id) $c['NetworkMode'] = $net.str_replace('/',':',DockerUtil::ctMap($id)?:'/???');
if (isset($driver[$c['NetworkMode']])) {
if ($driver[$c['NetworkMode']]=='bridge') {
$ports = &$info['HostConfig']['PortBindings'];
$nat = true;
} elseif ($driver[$c['NetworkMode']]=='host') {
$c['Ports']['host'] = ['host' => ''];
} elseif ($driver[$c['NetworkMode']]=='ipvlan' || $driver[$c['NetworkMode']]=='macvlan') {
$c['Ports']['vlan'] = ['vlan' => ''];
} else {
$ports = &$info['Config']['ExposedPorts'];
$nat = false;
}
$ip = $ct['NetworkSettings']['Networks'][$c['NetworkMode']]['IPAddress'];
} else if (!$id) {
$c['NetworkMode'] = DockerUtil::ctMap($c['NetworkMode']);
$ports = &$info['Config']['ExposedPorts'];
foreach($ct['NetworkSettings']['Networks'] as $netName => $netVals) {
$i = $c['NetworkMode']=='host' ? $host : $netVals['IPAddress'];
$c['Networks'][$netName] = [ 'IPAddress' => $i ];
}
}
$ip = $c['NetworkMode']=='host' ? $host : $ct['NetworkSettings']['Networks'][$c['NetworkMode']]['IPAddress'] ?? null;
$c['Networks'][$c['NetworkMode']] = [ 'IPAddress' => $ip ];
$ports = (isset($ports) && is_array($ports)) ? $ports : [];
foreach ($ports as $port => $value) {
if (!isset($info['HostConfig']['PortBindings'][$port])) {
continue;
}
[$PrivatePort, $Type] = array_pad(explode('/', $port),2,'');
$c['Ports'][] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $nat ? $value[0]['HostPort'] : $PrivatePort, 'NAT' => $nat, 'Type' => $Type];
$PublicPort = $info['HostConfig']['PortBindings']["$port"][0]['HostPort'] ?: null;
$nat = ($driver[$c['NetworkMode']]=='bridge');
if (array_key_exists($PrivatePort, $c['Ports']) && $Type != $c['Ports'][$PrivatePort]['Type'])
$Type = $c['Ports'][$PrivatePort]['Type'] . '/' . $Type;
$c['Ports'][$PrivatePort] = ['IP' => $ip, 'PrivatePort' => $PrivatePort, 'PublicPort' => $PublicPort, 'NAT' => $nat, 'Type' => $Type, 'Driver' => $driver[$c['NetworkMode']]];
}
ksort($c['Ports']);
$this::$containersCache[] = $c;
}
array_multisort(array_column($this::$containersCache,'Name'), SORT_NATURAL|SORT_FLAG_CASE, $this::$containersCache);

View File

@@ -87,11 +87,32 @@ foreach ($containers as $ct) {
$icon = $info['icon'] ?: '/plugins/dynamix.docker.manager/images/question.png';
$image = substr($icon,-4)=='.png' ? "<img src='$icon?".filemtime("$docroot{$info['icon']}")."' class='img' onerror=this.src='/plugins/dynamix.docker.manager/images/question.png';>" : (substr($icon,0,5)=='icon-' ? "<i class='$icon img'></i>" : "<i class='fa fa-$icon img'></i>");
$wait = var_split($autostart[array_search($name,$names)]??'',1);
$ports = [];
$networks = [];
$network_ips = [];
foreach($ct['Networks'] as $netName => $netVals) {
$networks[] = $netName;
$network_ips[] = $netVals['IPAddress'];
}
$ports_internal = [];
$ports_external = [];
foreach ($ct['Ports'] as $port) {
$intern = $running ? ($ct['NetworkMode']=='host' ? $host : _var($port,'IP')) : $null;
$extern = $running ? (_var($port,'NAT') ? $host : $intern) : $null;
$ports[] = sprintf('%s:%s/%s<i class="fa fa-arrows-h" style="margin:0 6px"></i>%s:%s', $intern, _var($port,'PrivatePort'), strtoupper(_var($port,'Type')), $extern, _var($port,'PublicPort'));
if (strpos($ct['NetworkMode'], 'container:') === 0)
break;
if (_var($port,'PublicPort') && _var($port,'Driver') == 'bridge')
$ports_external[] = sprintf('%s:%s', $host, strtoupper(_var($port,'PublicPort')));
if (isset($ct['Networks']['host'])) {
$ports_external[] = sprintf('%s', $netVals['IPAddress']);
$ports_internal[] = sprintf('%s', 'all');
break;
}
if (isset($ct['Ports']['vlan'])) {
$ports_external[] = sprintf('%s', $netVals['IPAddress']);
$ports_internal[] = sprintf('%s', 'all');
break;
}
if ((!isset($ct['Networks']['host'])) || (!isset($ct['Networks']['vlan']))) {
$ports_internal[] = sprintf('%s:%s', _var($port,'PrivatePort'), strtoupper(_var($port,'Type')));
}
}
$paths = [];
$ct['Volumes'] = is_array($ct['Volumes']) ? $ct['Volumes'] : [];
@@ -141,8 +162,10 @@ foreach ($containers as $ct) {
break;
}
echo "<div class='advanced'><i class='fa fa-info-circle fa-fw'></i> ".compress(_($version),12,0)."</div></td>";
echo "<td>{$ct['NetworkMode']}</td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports)."</span></td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'> ".implode('<br>',$networks)."</span></td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'> ".implode('<br>',$network_ips)."</span></td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports_internal)."</span></td>";
echo "<td style='white-space:nowrap'><span class='docker_readmore'>".implode('<br>',$ports_external)."</span></td>";
echo "<td style='word-break:break-all'><span class='docker_readmore'>".implode('<br>',$paths)."</span></td>";
echo "<td class='advanced'><span class='cpu-$id'>0%</span><div class='usage-disk mm'><span id='cpu-$id' style='width:0'></span><span></span></div>";
echo "<br><span class='mem-$id'>0 / 0</span></td>";
@@ -162,3 +185,4 @@ foreach ($images as $image) {
}
echo "\0".implode($docker)."\0".(pgrep('rc.docker')!==false ? 1:0);
?>

View File

@@ -515,9 +515,15 @@ function getAllocations() {
$nat = $ip = false;
$list['Name'] = $ct['Name'];
foreach ($ct['Ports'] as $tmp) {
$nat = $tmp['NAT'];
$ip = $tmp['IP'];
$port[] = $tmp['PublicPort'];
if (isset($tmp['NAT'])) {
$nat = $tmp['NAT'];
}
if (isset($tmp['IP'])) {
$ip = $tmp['IP'];
}
if (isset($tmp['PublicPort'])) {
$port[] = $tmp['PublicPort'];
}
}
sort($port);
$ip = $ct['NetworkMode']=='host'||$nat ? $host : ($ip ?: DockerUtil::myIP($ct['Name']) ?: '0.0.0.0');