summaryrefslogtreecommitdiffstats
path: root/modules-available/dnbd3
diff options
context:
space:
mode:
authorSimon Rettberg2017-10-10 17:40:51 +0200
committerSimon Rettberg2017-10-10 17:40:51 +0200
commit0041e188f7ef4c936eb08f26e08f229e7ce1b550 (patch)
tree52f2cb90e2b44c1325bbc8a65b47b3c088750929 /modules-available/dnbd3
parent[dnbd3] New module for managing dnbd3 servers - WIP (diff)
downloadslx-admin-0041e188f7ef4c936eb08f26e08f229e7ce1b550.tar.gz
slx-admin-0041e188f7ef4c936eb08f26e08f229e7ce1b550.tar.xz
slx-admin-0041e188f7ef4c936eb08f26e08f229e7ce1b550.zip
[dnbd3] More progress, manage location restrictions for proxies
Diffstat (limited to 'modules-available/dnbd3')
-rw-r--r--modules-available/dnbd3/config.json2
-rw-r--r--modules-available/dnbd3/hooks/cron.inc.php3
-rw-r--r--modules-available/dnbd3/hooks/runmode/config.json6
-rw-r--r--modules-available/dnbd3/inc/dnbd3rpc.inc.php4
-rw-r--r--modules-available/dnbd3/inc/dnbd3util.inc.php96
-rw-r--r--modules-available/dnbd3/page.inc.php153
-rw-r--r--modules-available/dnbd3/templates/page-client-loclist.html27
-rw-r--r--modules-available/dnbd3/templates/page-clientlist.html28
-rw-r--r--modules-available/dnbd3/templates/page-header-servername.html1
-rw-r--r--modules-available/dnbd3/templates/page-server-locations.html96
-rw-r--r--modules-available/dnbd3/templates/page-serverlist.html161
11 files changed, 497 insertions, 80 deletions
diff --git a/modules-available/dnbd3/config.json b/modules-available/dnbd3/config.json
index f06dda5c..f84a4170 100644
--- a/modules-available/dnbd3/config.json
+++ b/modules-available/dnbd3/config.json
@@ -1,4 +1,4 @@
{
"category":"main.settings-server",
- "dependencies":["runmode"]
+ "dependencies":["locations","runmode"]
}
diff --git a/modules-available/dnbd3/hooks/cron.inc.php b/modules-available/dnbd3/hooks/cron.inc.php
new file mode 100644
index 00000000..3da4cae4
--- /dev/null
+++ b/modules-available/dnbd3/hooks/cron.inc.php
@@ -0,0 +1,3 @@
+<?php
+
+Dnbd3Util::updateServerStatus();
diff --git a/modules-available/dnbd3/hooks/runmode/config.json b/modules-available/dnbd3/hooks/runmode/config.json
new file mode 100644
index 00000000..095cb42f
--- /dev/null
+++ b/modules-available/dnbd3/hooks/runmode/config.json
@@ -0,0 +1,6 @@
+{
+ "isClient": false,
+ "configHook": "Dnbd3Util::runmodeConfigHook",
+ "noSysconfig": true,
+ "systemdDefaultTarget": "dnbd3-proxy"
+} \ No newline at end of file
diff --git a/modules-available/dnbd3/inc/dnbd3rpc.inc.php b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
index 41dcefd4..6134489a 100644
--- a/modules-available/dnbd3/inc/dnbd3rpc.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3rpc.inc.php
@@ -14,6 +14,10 @@ class Dnbd3Rpc {
*/
public static function query($stats, $clients, $images, $server, $port = 5003)
{
+ // Special case - local server
+ if ($server === '<self>') {
+ $server = '127.0.0.1';
+ }
$url = 'http://' . $server . ':' . $port . '/query?';
if ($stats) {
$url .= 'q=stats&';
diff --git a/modules-available/dnbd3/inc/dnbd3util.inc.php b/modules-available/dnbd3/inc/dnbd3util.inc.php
index b04583b8..48e887b3 100644
--- a/modules-available/dnbd3/inc/dnbd3util.inc.php
+++ b/modules-available/dnbd3/inc/dnbd3util.inc.php
@@ -5,6 +5,7 @@ class Dnbd3Util {
public static function updateServerStatus()
{
$dynClients = RunMode::getForMode('dnbd3', 'proxy', false, true);
+ $satServerIp = Property::getServerIp();
$servers = array();
$res = Database::simpleQuery('SELECT s.serverid, s.machineuuid, s.fixedip, s.lastup, s.lastdown, m.clientip
FROM dnbd3_server s
@@ -17,7 +18,7 @@ class Dnbd3Util {
} else {
continue; // Huh?
}
- if (!is_null($row['machineuuid'])) {
+ if (!is_null($row['machineuuid']) || $row['clientip'] === $satServerIp) {
unset($dynClients[$row['machineuuid']]);
}
$server = array(
@@ -32,6 +33,10 @@ class Dnbd3Util {
array('machineuuid' => $client['machineuuid']));
// Missing from $servers now but we'll handle them in the next run, so don't bother
}
+ // Same for this server - we use the special fixedip '<self>' for it and need to prevent we don't have the
+ // IP address of the server itself in the list.
+ Database::exec('DELETE FROM dnbd3_server WHERE fixedip = :serverip', array('serverip' => $satServerIp));
+ Database::exec("INSERT IGNORE INTO dnbd3_server (fixedip) VALUES ('<self>')");
// Now query them all
$NOW = time();
foreach ($servers as $server) {
@@ -59,4 +64,93 @@ class Dnbd3Util {
}
}
+ /**
+ * A client is booting that has runmode dnbd3 proxy - set config vars accordingly.
+ *
+ * @param string $machineUuid
+ * @param string $mode always 'proxy'
+ * @param string $modeData
+ */
+ public static function runmodeConfigHook($machineUuid, $mode, $modeData)
+ {
+ // Get all directly assigned locations
+ $res = Database::simpleQuery('SELECT locationid FROM dnbd3_server
+ INNER JOIN dnbd3_server_x_location USING (serverid)
+ WHERE machineuuid = :uuid',
+ array('uuid' => $machineUuid));
+ $assignedLocs = $res->fetchAll(PDO::FETCH_ASSOC);
+ if (!empty($assignedLocs)) {
+ // Get all sub-locations too
+ $recursiveLocs = $assignedLocs;
+ $locations = Location::getLocationsAssoc();
+ foreach ($assignedLocs as $l) {
+ if (isset($locations[$l])) {
+ $recursiveLocs = array_merge($recursiveLocs, $locations[$l]['children']);
+ }
+ }
+ $res = Database::simpleQuery('SELECT startaddr, endaddr FROM subnet WHERE locationid IN (:locs)',
+ array('locs' => $recursiveLocs));
+ // Got subnets, build whitelist
+ $opt = '';
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $opt .= ' ' . self::range2Cidr($row['startaddr'], $row['endaddr']);
+ }
+ if (!empty($opt)) {
+ ConfigHolder::add('SLX_DNBD3_WHITELIST', $opt, 1000);
+ }
+ }
+ // Send list of other proxy servers
+ $res = Database::simpleQuery('SELECT s.fixedip, m.clientip, sxl.locationid FROM dnbd3_server s
+ LEFT JOIN machine m USING (machineuuid)
+ LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
+ WHERE s.machineuuid <> :uuid OR s.machineuuid IS NULL', array('uuid' => $machineUuid));
+ $public = array();
+ $private = array();
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ $ip = $row['clientip'] ? $row['clientip'] : $row['fixedip'];
+ if ($ip === '<self>') {
+ continue;
+ }
+ if (is_null($row['locationid'])) {
+ if (!array_key_exists($ip, $private)) {
+ $public[$ip] = $ip;
+ }
+ } else {
+ $private[$ip] = $ip;
+ }
+ }
+ if (!empty($public)) {
+ ConfigHolder::add('SLX_DNBD3_PUBLIC', implode(' ', $public));
+ }
+ if (!empty($private)) {
+ ConfigHolder::add('SLX_DNBD3_PRIVATE', implode(' ', $private));
+ }
+ ConfigHolder::add('SLX_ADDONS', '', 1000);
+ }
+
+ /**
+ * Get smallest subnet in CIDR notation that covers the given range.
+ * The subnet denoted by the CIDR notation might actually be larger
+ * than the range described by $start and $end.
+ *
+ * @param int $start start address
+ * @param int $end end address
+ * @return string CIDR notation
+ */
+ private static function range2Cidr($start, $end)
+ {
+ $bin = decbin($start ^ $end);
+ if ($bin === '0')
+ return $start;
+ $mask = 32 - strlen($bin);
+ return $start . '/' . $mask;
+ }
+
+}
+
+class Dnbd3ProxyConfig
+{
+
+ public $a;
+
} \ No newline at end of file
diff --git a/modules-available/dnbd3/page.inc.php b/modules-available/dnbd3/page.inc.php
index 309680a6..213afa03 100644
--- a/modules-available/dnbd3/page.inc.php
+++ b/modules-available/dnbd3/page.inc.php
@@ -11,17 +11,47 @@ class Page_Dnbd3 extends Page
Message::addError('main.no-permission');
Util::redirect('?do=Main');
}
- if (Request::post('re')) {
+ $action = Request::post('action', false, 'string');
+ if ($action === 'refresh') {
Dnbd3Util::updateServerStatus();
+ } elseif ($action === 'delserver') {
+ $this->deleteServer();
+ } elseif ($action === 'savelocations') {
+ $this->saveServerLocations();
+ }
+ if (Request::isPost()) {
Util::redirect('?do=dnbd3');
}
}
+ private function saveServerLocations()
+ {
+ $server = $this->getServerById();
+ $locids = Request::post('location', [], 'array');
+ if (empty($locids)) {
+ Database::exec('DELETE FROM dnbd3_server_x_location WHERE serverid = :serverid',
+ array('serverid' => $server['serverid']));
+ } else {
+ Database::exec('DELETE FROM dnbd3_server_x_location WHERE serverid = :serverid AND locationid NOT IN (:lids)',
+ array('serverid' => $server['serverid'], 'lids' => $locids));
+ foreach ($locids as $lid) {
+ Database::exec('INSERT IGNORE INTO dnbd3_server_x_location (serverid, locationid) VALUES (:serverid, :lid)',
+ array('serverid' => $server['serverid'], 'lid' => $lid));
+ }
+ }
+ }
+
+ /*
+ * RENDER
+ */
+
protected function doRender()
{
$show = Request::get('show', false, 'string');
if ($show === 'clients') {
$this->showClientList();
+ } elseif ($show === 'locations') {
+ $this->showServerLocationEdit();
} elseif ($show === false) {
$this->showServerList();
} else {
@@ -32,7 +62,11 @@ class Page_Dnbd3 extends Page
private function showServerList()
{
$dynClients = RunMode::getForMode(Page::getModule(), 'proxy', true, true);
- $res = Database::simpleQuery('SELECT serverid, machineuuid, fixedip, lastseen, uptime, totalup, totaldown, clientcount FROM dnbd3_server');
+ $res = Database::simpleQuery('SELECT s.serverid, s.machineuuid, s.fixedip, s.lastseen,
+ s.uptime, s.totalup, s.totaldown, s.clientcount, Count(sxl.locationid) AS locations
+ FROM dnbd3_server s
+ LEFT JOIN dnbd3_server_x_location sxl USING (serverid)
+ GROUP BY s.serverid');
$servers = array();
$sort = array();
$NOW = time();
@@ -48,12 +82,17 @@ class Page_Dnbd3 extends Page
$server['uptime_s'] = $server['uptime'] ? floor($server['uptime'] / 86400) . 'd ' . gmdate('H:i', $server['uptime']) : '-';
$server['totalup_s'] = Util::readableFileSize($server['totalup']);
$server['totaldown_s'] = Util::readableFileSize($server['totaldown']);
+ $server['self'] = ($server['fixedip'] === '<self>');
$servers[] = $server;
- $sort[] = $server['fixedip'] . '.' . $server['machineuuid'];
+ if ($server['self']) {
+ $sort[] = '---';
+ } else {
+ $sort[] = $server['fixedip'] . '.' . $server['machineuuid'];
+ }
}
foreach ($dynClients as $server) {
$servers[] = $server;
- $sort[] = 'A' . $server['machineuuid'];
+ $sort[] = '-' . $server['machineuuid'];
}
array_multisort($sort, SORT_ASC, $servers);
Render::addTemplate('page-serverlist', array('list' => $servers));
@@ -61,11 +100,94 @@ class Page_Dnbd3 extends Page
private function showClientList()
{
- $serverId = Request::get('server', false, 'int');
+ $server = $this->getServerById();
+ Render::addTemplate('page-header-servername', $server);
+ $data = Dnbd3Rpc::query(false, true, false, $server['ip']);
+ if ($data === false || !isset($data['clients'])) {
+ Message::addError('server-unreachable');
+ return;
+ }
+ $ips = array();
+ $sort = array();
+ foreach ($data['clients'] as &$c) {
+ $c['bytesSent_s'] = Util::readableFileSize($c['bytesSent']);
+ $sort[] = $c['bytesSent'];
+ $ips[] = preg_replace('/:\d+$/', '', $c['address']);
+ }
+ array_multisort($sort, SORT_DESC, $data['clients']);
+ Render::openTag('div', ['class' => 'row']);
+ // Count locations
+ $res = Database::simpleQuery('SELECT locationid, Count(*) AS cnt FROM machine WHERE clientip IN (:ips) GROUP BY locationid', compact('ips'));
+ $locCount = Location::getLocationsAssoc();
+ $locCount[0] = array(
+ 'locationname' => '/',
+ 'depth' => 0,
+ 'recCount' => 0,
+ );
+ foreach ($locCount as &$loc) {
+ $loc['recCount'] = 0;
+ }
+ $showLocs = false;
+ while ($row = $res->fetch(PDO::FETCH_ASSOC)) {
+ settype($row['locationid'], 'int');
+ $loc =& $locCount[$row['locationid']];
+ $loc['clientCount'] = $row['cnt'];
+ $loc['recCount'] += $row['cnt'];
+ if ($row['locationid'] !== 0) {
+ $showLocs = true;
+ }
+ $loc['keep'] = true;
+ if (isset($loc['parents'])) {
+ foreach ($loc['parents'] as $p) {
+ $locCount[$p]['keep'] = true;
+ $locCount[$p]['recCount'] += $row['cnt'];
+ }
+ }
+ }
+ if ($showLocs) {
+ $locCount = array_filter($locCount, function ($v) { return isset($v['keep']); });
+ Render::addTemplate('page-client-loclist', array('list' => array_values($locCount)));
+ }
+ Render::addTemplate('page-clientlist', $data);
+ Render::closeTag('div');
+ }
+
+ private function showServerLocationEdit()
+ {
+ $server = $this->getServerById();
+ // Get selected ones
+ $res = Database::simpleQuery('SELECT locationid FROM dnbd3_server_x_location WHERE serverid = :serverid',
+ array('serverid' => $server['serverid']));
+ $selectedLocations = array();
+ while ($loc = $res->fetchColumn(0)) {
+ $selectedLocations[$loc] = true;
+ }
+ // Build location list
+ $server['locations'] = array_values(Location::getSubnetsByLocation());
+ $filtered = array();
+ foreach ($server['locations'] as &$loc) {
+ $filtered['l'.$loc['locationid']] = array(
+ 'children' => $loc['children'],
+ 'subnets' => $loc['subnets']
+ );
+ if (isset($selectedLocations[$loc['locationid']])) {
+ $loc['checked_s'] = 'checked';
+ }
+ }
+ unset($loc);
+ $server['jsonLocations'] = json_encode($filtered);
+ Render::addTemplate('page-server-locations', $server);
+ }
+
+ private function getServerById($serverId = false)
+ {
+ if ($serverId === false) {
+ $serverId = Request::any('server', false, 'int');
+ }
if ($serverId === false) {
// TODO: Missing param
}
- $server = Database::queryFirst('SELECT s.machineuuid, s.fixedip, m.clientip, m.hostname
+ $server = Database::queryFirst('SELECT s.serverid, s.machineuuid, s.fixedip, m.clientip, m.hostname
FROM dnbd3_server s
LEFT JOIN machine m USING (machineuuid)
WHERE s.serverid = :serverId', compact('serverId'));
@@ -73,24 +195,13 @@ class Page_Dnbd3 extends Page
// TODO: Not found
}
if (!is_null($server['clientip'])) {
- $ip = $server['clientip'];
+ $server['ip'] = $server['clientip'];
} elseif (!is_null($server['fixedip'])) {
- $ip = $server['fixedip'];
+ $server['ip'] = $server['fixedip'];
} else {
- $ip = '127.0.0.1';
- }
- $data = Dnbd3Rpc::query(false, true, false, $ip);
- if ($data === false || !isset($data['clients'])) {
- Message::addError('server-unreachable');
- } else {
- $sort = array();
- foreach ($data['clients'] as &$c) {
- $c['bytesSent_s'] = Util::readableFileSize($c['bytesSent']);
- $sort[] = $c['bytesSent'];
- }
- array_multisort($sort, SORT_DESC, $data['clients']);
- Render::addTemplate('page-clientlist', $data);
+ $server['ip'] = '127.0.0.1';
}
+ return $server;
}
}
diff --git a/modules-available/dnbd3/templates/page-client-loclist.html b/modules-available/dnbd3/templates/page-client-loclist.html
new file mode 100644
index 00000000..67c90683
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-client-loclist.html
@@ -0,0 +1,27 @@
+<div class="col-md-6">
+ <h2>{{lang_clientsByLocation}}</h2>
+
+ <table class="table table-condensed">
+ <tr>
+ <th>{{lang_location}}</th>
+ <th class="text-right">{{lang_count}}</th>
+ <th class="text-right">{{lang_recursiveCount}}</th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td>
+ {{#depth}}
+ <div style="display:inline-block;width:{{depth}}em"></div>
+ {{/depth}}
+ {{locationname}}
+ </td>
+ <td class="text-right">
+ {{clientCount}}
+ </td>
+ <td class="text-right">
+ {{recCount}}
+ </td>
+ </tr>
+ {{/list}}
+ </table>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-clientlist.html b/modules-available/dnbd3/templates/page-clientlist.html
index 3fd4442c..9e7cec4c 100644
--- a/modules-available/dnbd3/templates/page-clientlist.html
+++ b/modules-available/dnbd3/templates/page-clientlist.html
@@ -1,12 +1,20 @@
-<table class="table">
- {{#clients}}
+<div class="col-md-6">
+ <h2>{{lang_clientList}}</h2>
+
+ <table class="table table-condensed">
<tr>
- <td>
- {{address}}
- </td>
- <td class="text-right">
- {{bytesSent_s}}
- </td>
+ <th>{{lang_client}}</th>
+ <th class="text-right">{{lang_bytesSent}}</th>
</tr>
- {{/clients}}
-</table> \ No newline at end of file
+ {{#clients}}
+ <tr>
+ <td>
+ {{address}}
+ </td>
+ <td data-sort="int" data-sort-value="{{bytesSent}}" class="text-right">
+ {{bytesSent_s}}
+ </td>
+ </tr>
+ {{/clients}}
+ </table>
+</div> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-header-servername.html b/modules-available/dnbd3/templates/page-header-servername.html
new file mode 100644
index 00000000..6f3f1b7f
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-header-servername.html
@@ -0,0 +1 @@
+<h1>{{ip}}</h1> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-server-locations.html b/modules-available/dnbd3/templates/page-server-locations.html
new file mode 100644
index 00000000..8e76a68d
--- /dev/null
+++ b/modules-available/dnbd3/templates/page-server-locations.html
@@ -0,0 +1,96 @@
+<h1>{{lang_manageAccessTo}} {{ip}}</h1>
+
+<p><i>{{lang_proxyLocationText}}</i></p>
+
+<form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="server" value="{{serverid}}">
+
+ <div class="buttonbar text-right">
+ <button type="submit" class="btn btn-success" name="action" value="savelocations">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+
+ <div class="row">
+ <div class="col-md-6">
+ <h3>{{lang_allowedSubnets}}</h3>
+ <div id="subnet-list">
+
+ </div>
+ </div>
+ <div class="col-md-6">
+ <h3>{{lang_locations}}</h3>
+ {{#locations}}
+ <div class="checkbox">
+ {{#depth}}
+ <div style="display:inline-block;width:{{depth}}em"></div>
+ {{/depth}}
+ <input id="cb-{{locationid}}" class="loc-check" type="checkbox" name="location[]" value="{{locationid}}" {{checked_s}}>
+ <label for="cb-{{locationid}}">{{locationname}}</label>
+ </div>
+ {{/locations}}
+ </div>
+ </div>
+
+ <div class="buttonbar text-right">
+ <button type="submit" class="btn btn-success" name="action" value="savelocations">
+ <span class="glyphicon glyphicon-floppy-disk"></span>
+ {{lang_save}}
+ </button>
+ </div>
+</form>
+
+<script type="application/javascript"><!--
+document.addEventListener('DOMContentLoaded', function() {
+
+ const locData = {{{jsonLocations}}};
+ const $snList = $('#subnet-list');
+
+ function updateLocations() {
+ var allLocs = [];
+ $('.loc-check:checked').each(function () {
+ var lid = parseInt(this.value);
+ var locs = getAllLocs(lid);
+ for (var i = 0; i < locs.length; ++i) {
+ if (allLocs.indexOf(locs[i]) === -1) allLocs.push(locs[i]);
+ }
+ });
+ var subnets = [];
+ $snList.empty();
+ for (var i = 0; i < allLocs.length; ++i) {
+ var loc = locData['l'+allLocs[i]];
+ if (!loc || !loc.subnets) continue;
+ for (var j = 0; j < loc.subnets.length; ++j) {
+ var line = long2ip(loc.subnets[j].startaddr) + ' - ' + long2ip(loc.subnets[j].endaddr);
+ if (subnets.indexOf(line) === -1) {
+ subnets.push(line);
+ $snList.append($('<div>').text(line));
+ }
+ }
+ }
+ if (subnets.length === 0) {
+ $snList.text('{{lang_global}}');
+ }
+ }
+
+ function getAllLocs(lid) {
+ var e = locData['l'+lid];
+ if (!e || !e.children) return [];
+ var ret = e.children;
+ ret.push(lid);
+ return ret;
+ }
+
+ function long2ip(ip) {
+ // discuss at: http://locutus.io/php/long2ip/
+ // original by: Waldo Malqui Silva (http://waldo.malqui.info)
+ if (!isFinite(ip)) return false;
+ return [ip >>> 24, ip >>> 16 & 0xFF, ip >>> 8 & 0xFF, ip & 0xFF].join('.');
+ }
+
+ $('.loc-check').change(updateLocations);
+ updateLocations();
+});
+//--></script> \ No newline at end of file
diff --git a/modules-available/dnbd3/templates/page-serverlist.html b/modules-available/dnbd3/templates/page-serverlist.html
index b7fe9038..e50c6bcb 100644
--- a/modules-available/dnbd3/templates/page-serverlist.html
+++ b/modules-available/dnbd3/templates/page-serverlist.html
@@ -1,51 +1,118 @@
+<h1>{{lang_dnbd3Management}}</h1>
+<p><i>{{lang_dnbd3IntroText}}</i></p>
+
<form method="post">
<input type="hidden" name="token" value="{{token}}">
- <button name="re" value="bla">Refresh</button>
+ <h2>
+ {{lang_serverList}}
+ <button type="submit" class="btn btn-default" name="action" value="refresh"><span class="glyphicon glyphicon-refresh"></span></button>
+ </h2>
+</form>
+<form method="post" action="?do=dnbd3">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="hidden" name="action" value="delserver">
+ <table class="table">
+ <tr>
+ <th>{{lang_proxyServerTHead}}</th>
+ <th class="text-right">{{lang_clientCount}}</th>
+ <th class="text-right">{{lang_lastSeen}}</th>
+ <th class="text-right">{{lang_uptime}}</th>
+ <th class="text-right">{{lang_txTotal}}</th>
+ <th class="text-right">{{lang_rxTotal}}</th>
+ <th class="text-right">{{lang_locations}}</th>
+ <th>&nbsp;</th>
+ </tr>
+ {{#list}}
+ <tr>
+ <td class="{{#self}}slx-bold{{/self}}">
+ {{#uptime}}
+ <span class="glyphicon glyphicon-ok text-success"></span>
+ {{/uptime}}
+ {{^uptime}}
+ <span class="glyphicon glyphicon-off"></span>
+ {{/uptime}}
+ {{fixedip}}
+ {{#machineuuid}}
+ <a href="?do=Statistics&uuid={{machineuuid}}">{{clientip}}</a>
+ <div class="small">{{hostname}}</div>
+ {{/machineuuid}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" class="text-right">
+ {{#uptime}}
+ <a href="?do=dnbd3&amp;show=clients&amp;server={{serverid}}">{{clientcount}}</a>
+ {{/uptime}}
+ {{^uptime}}
+ -
+ {{/uptime}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{lastseen}}" class="text-right text-nowrap">
+ {{lastseen_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{uptime}}" class="text-right text-nowrap">
+ {{uptime_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{totalup}}" class="text-right text-nowrap">
+ {{totalup_s}}
+ </td>
+ <td data-sort="int" data-sort-default="desc" data-sort-value="{{totaldown}}" class="text-right text-nowrap">
+ {{totaldown_s}}
+ </td>
+ <td class="text-right text-nowrap">
+ {{^self}}
+ {{^locations}}
+ <i>{{lang_global}}</i>
+ {{/locations}}
+ {{#locations}}
+ {{locations}}
+ {{/locations}}
+ <a href="?do=dnbd3&amp;show=locations&amp;server={{serverid}}" class="btn btn-default btn-xs">
+ <span class="glyphicon glyphicon-map-marker"></span>
+ </a>
+ {{/self}}
+ </td>
+ <td>
+ {{^self}}
+ <button class="btn btn-danger btn-xs" name="serverid" value="{{serverid}}"><span class="glyphicon glyphicon-trash"></span></button>
+ {{/self}}
+ </td>
+ </tr>
+ {{/list}}
+ </table>
</form>
-<table class="table">
- <tr>
- <th>{{lang_proxyServerTHead}}</th>
- <th class="text-right">{{lang_lastSeen}}</th>
- <th class="text-right">{{lang_uptime}}</th>
- <th class="text-right">{{lang_txTotal}}</th>
- <th class="text-right">{{lang_rxTotal}}</th>
- <th class="text-right">{{lang_clientCount}}</th>
- </tr>
- {{#list}}
- <tr>
- <td>
- {{#uptime}}
- <span class="glyphicon glyphicon-ok text-success"></span>
- {{/uptime}}
- {{^uptime}}
- <span class="glyphicon glyphicon-off"></span>
- {{/uptime}}
- {{fixedip}}
- {{#machineuuid}}
- <a href="?do=Statistics&uuid={{machineuuid}}">{{hostname}} ({{clientip}})</a>
- {{/machineuuid}}
- </td>
- <td data-sort="int" data-sort-default="desc" data-sort-value="{{lastseen}}" class="text-right">
- {{lastseen_s}}
- </td>
- <td data-sort="int" data-sort-default="desc" data-sort-value="{{uptime}}" class="text-right">
- {{uptime_s}}
- </td>
- <td data-sort="int" data-sort-default="desc" data-sort-value="{{totalup}}" class="text-right">
- {{totalup_s}}
- </td>
- <td data-sort="int" data-sort-default="desc" data-sort-value="{{totaldown}}" class="text-right">
- {{totaldown_s}}
- </td>
- <td data-sort="int" data-sort-default="desc" class="text-right">
- {{#uptime}}
- <a href="?do=dnbd3&amp;show=clients&amp;server={{serverid}}">{{clientcount}}</a>
- {{/uptime}}
- {{^uptime}}
- -
- {{/uptime}}
- </td>
- </tr>
- {{/list}}
-</table> \ No newline at end of file
+<div class="buttonbar pull-right">
+ <button type="button" class="btn btn-success" data-toggle="modal" data-target="#add-modal">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_addExternalServer}}
+ </button>
+ <a class="btn btn-success" href="?do=runmode&amp;module=dnbd3&amp;modeid=proxy&amp;redirect=?do=dnbd3">
+ <span class="glyphicon glyphicon-plus"></span>
+ {{lang_addManagedProxy}}
+ </a>
+</div>
+
+<div id="add-modal" class="modal fade" role="dialog">
+ <div class="modal-dialog">
+
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal">&times;</button>
+ <h4 class="modal-title"><b>{{lang_addServer}}</b></h4>
+ </div>
+ <div class="modal-body">
+ <p>{{lang_enterIpOfServer}}</p>
+ <form method="post">
+ <input type="hidden" name="token" value="{{token}}">
+ <input type="text" class="form-control" name="newip">
+ <br>
+ <button type="submit" class="btn btn-default btn-success pull-right" name="action" value="addserver">{{lang_save}}</button>
+ </form>
+ <div class="clearfix"></div>
+ </div>
+ <div class="modal-footer">
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="clearfix"></div> \ No newline at end of file