diff options
-rw-r--r-- | modules-available/dnbd3/config.json | 2 | ||||
-rw-r--r-- | modules-available/dnbd3/hooks/cron.inc.php | 3 | ||||
-rw-r--r-- | modules-available/dnbd3/hooks/runmode/config.json | 6 | ||||
-rw-r--r-- | modules-available/dnbd3/inc/dnbd3rpc.inc.php | 4 | ||||
-rw-r--r-- | modules-available/dnbd3/inc/dnbd3util.inc.php | 96 | ||||
-rw-r--r-- | modules-available/dnbd3/page.inc.php | 153 | ||||
-rw-r--r-- | modules-available/dnbd3/templates/page-client-loclist.html | 27 | ||||
-rw-r--r-- | modules-available/dnbd3/templates/page-clientlist.html | 28 | ||||
-rw-r--r-- | modules-available/dnbd3/templates/page-header-servername.html | 1 | ||||
-rw-r--r-- | modules-available/dnbd3/templates/page-server-locations.html | 96 | ||||
-rw-r--r-- | modules-available/dnbd3/templates/page-serverlist.html | 161 |
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> </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&show=clients&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&show=locations&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&show=clients&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&module=dnbd3&modeid=proxy&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">×</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 |