net/relayd: Usability improvements (#2373)

net/relayd: Usability improvements https://github.com/opnsense/plugins/issues/2232

o support enable/disable flag on backend hosts, while here perform some minor cleanups as well (use our standard classes and some style fixes) for https://github.com/opnsense/plugins/issues/2232
o lock delete action to prevent "delete selected" from ignoring most entries. (eventually it might be better to stick to ApiMutableModelControllerBase actions to simplify logiuc, but since there's all some logic incorporated in these endpoints now, let's keep it as is now and only add a lock.)
o ignore backup without check in relayd.conf (prevent crash with "Template Error" when a backup without a template is provided)
o extend status controller with "wait" flag to give relayd some time for collecting status (retry mechanism)
o refactor status.volt use jQuery components to construct items and make sure each row contains all relevant data
o extend status controller output with attached configuration data and add listen_address and port(s) for virtualservers
o extend model with an easy to use "get by name" method (getObjectsByAttribute)
o change api response for host properties, since we can't uniquely tell which host item belongs to the output of `relayctl show summary`, we search the ones that are most likely to match
o extend status controller toggle action so it can enable/disable hosts on "add" and "remove" actions, $id's are (a list of) uuid's in that case
o extend status controller to return "unconfigured" when disabled in the configuration, so we can distinct between temporary disabled (running config) and offline
o status page: switch icons to fonts-awesome (standard theme) in status view
o status page: add filter option in status view
o status page: add bind address and port in virtual server field
o status page: add host name(s) if it differs from the address, so we can search on user configurable names as well
o status page: add transitions, down -> stopped, disabled -> enabled (+ service reconfigure)
o status page: always show host disable button
o status page: restructure translation texts
o status page: add toggle to hide table column, provides some additional overview when only single tables are used in a virtual server
o status page: support local presets using localStorage object, quickly traverse through different filters previously saved on the local client.


sponsored by : Modirum (https://www.modirum.com/)
This commit is contained in:
Ad Schellevis 2021-05-14 17:26:16 +02:00 committed by GitHub
parent 4428246a0f
commit 0334ceae56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 807 additions and 299 deletions

View File

@ -1,6 +1,6 @@
PLUGIN_NAME= relayd
PLUGIN_VERSION= 2.5
PLUGIN_REVISION= 2
PLUGIN_VERSION= 2.6
#PLUGIN_REVISION= 2
PLUGIN_DEPENDS= relayd
PLUGIN_COMMENT= Relayd Load Balancer
PLUGIN_MAINTAINER= frank.brendel@eurolog.com

View File

@ -2,6 +2,7 @@
/*
* Copyright (C) 2018 EURO-LOG AG
* Copyright (c) 2021 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -98,10 +99,9 @@ class ServiceController extends ApiMutableServiceControllerBase
$this->sessionClose();
$result['function'] = "reconfigure";
$result['status'] = 'failed';
$mdlRelayd = new Relayd();
$backend = new Backend();
$status = $this->statusAction();
if ($mdlRelayd->general->enabled->__toString() == 1) {
if (!empty((string)$this->getModel()->general->enabled)) {
$result = $this->configtestAction();
if ($result['template'] == 'OK' && preg_match('/configuration OK$/', $result['result']) == 1) {
if ($status['status'] != 'running') {
@ -118,8 +118,7 @@ class ServiceController extends ApiMutableServiceControllerBase
}
}
$this->lock(1);
$mdlRelayd = new Relayd();
if ($mdlRelayd->configClean()) {
if ($this->getModel()->configClean()) {
$result['status'] = 'ok';
}
return $result;

View File

@ -2,7 +2,7 @@
/**
* Copyright (C) 2018 EURO-LOG AG
*
* Copyright (c) 2021 Deciso B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -30,7 +30,7 @@
namespace OPNsense\Relayd\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Base\ApiMutableModelControllerBase;
use OPNsense\Core\Config;
use OPNsense\Relayd\Relayd;
use OPNsense\Base\UIModelGrid;
@ -39,25 +39,17 @@ use OPNsense\Base\UIModelGrid;
* Class SettingsController
* @package OPNsense\Relayd
*/
class SettingsController extends ApiControllerBase
class SettingsController extends ApiMutableModelControllerBase
{
protected static $internalModelName = 'relayd';
protected static $internalModelClass = '\OPNsense\Relayd\Relayd';
public $mdlRelayd = null;
/**
* list with valid model node types
*/
private $nodeTypes = array('general', 'host', 'tablecheck', 'table', 'protocol', 'virtualserver');
/**
* initialize object properties
*/
public function onConstruct()
{
$this->mdlRelayd = new Relayd();
}
/**
* check if changes to the relayd settings were made
@ -66,7 +58,7 @@ class SettingsController extends ApiControllerBase
public function dirtyAction()
{
$result = array('status' => 'ok');
$result['relayd']['dirty'] = $this->mdlRelayd->configChanged();
$result['relayd']['dirty'] = $this->getModel()->configChanged();
return $result;
}
@ -82,12 +74,12 @@ class SettingsController extends ApiControllerBase
if ($this->request->isGet() && $nodeType != null) {
$this->validateNodeType($nodeType);
if ($nodeType == 'general') {
$node = $this->mdlRelayd->getNodeByReference($nodeType);
$node = $this->getModel()->getNodeByReference($nodeType);
} else {
if ($uuid != null) {
$node = $this->mdlRelayd->getNodeByReference($nodeType . '.' . $uuid);
$node = $this->getModel()->getNodeByReference($nodeType . '.' . $uuid);
} else {
$node = $this->mdlRelayd->$nodeType->Add();
$node = $this->getModel()->$nodeType->Add();
}
}
if ($node != null) {
@ -111,12 +103,12 @@ class SettingsController extends ApiControllerBase
if ($this->request->isPost() && $this->request->hasPost('relayd') && $nodeType != null) {
$this->validateNodeType($nodeType);
if ($nodeType == 'general') {
$node = $this->mdlRelayd->getNodeByReference($nodeType);
$node = $this->getModel()->getNodeByReference($nodeType);
} else {
if ($uuid != null) {
$node = $this->mdlRelayd->getNodeByReference($nodeType . '.' . $uuid);
$node = $this->getModel()->getNodeByReference($nodeType . '.' . $uuid);
} else {
$node = $this->mdlRelayd->$nodeType->Add();
$node = $this->getModel()->$nodeType->Add();
}
}
if ($node != null) {
@ -207,16 +199,16 @@ class SettingsController extends ApiControllerBase
}
$node->setNodes($relaydInfo[$nodeType]);
$valMsgs = $this->mdlRelayd->performValidation();
$valMsgs = $this->getModel()->performValidation();
foreach ($valMsgs as $field => $msg) {
$fieldnm = str_replace($node->__reference, "relayd." . $nodeType, $msg->getField());
$result["validations"][$fieldnm] = $msg->getMessage();
}
if (empty($result["validations"])) {
unset($result["validations"]);
$this->mdlRelayd->serializeToConfig();
$this->getModel()->serializeToConfig();
$cfgRelayd = Config::getInstance()->save();
if ($this->mdlRelayd->configDirty()) {
if ($this->getModel()->configDirty()) {
$result['status'] = 'ok';
}
}
@ -234,13 +226,14 @@ class SettingsController extends ApiControllerBase
public function delAction($nodeType = null, $uuid = null)
{
$result = array("result" => "failed");
Config::getInstance()->lock();
if ($nodeType != null) {
$this->validateNodeType($nodeType);
if ($uuid != null) {
$node = $this->mdlRelayd->getNodeByReference($nodeType . '.' . $uuid);
$node = $this->getModel()->getNodeByReference($nodeType . '.' . $uuid);
if ($node != null) {
$nodeName = $this->mdlRelayd->getNodeByReference($nodeType . '.' . $uuid . '.name')->__toString();
if ($this->mdlRelayd->$nodeType->del($uuid) == true) {
$nodeName = $this->getModel()->getNodeByReference($nodeType . '.' . $uuid . '.name')->__toString();
if ($this->getModel()->$nodeType->del($uuid) == true) {
// delete relations
switch ($nodeType) {
case 'host':
@ -250,7 +243,7 @@ class SettingsController extends ApiControllerBase
$uuid,
'host',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
break;
case 'tablecheck':
@ -260,7 +253,7 @@ class SettingsController extends ApiControllerBase
$uuid,
'tablecheck',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
$this->deleteRelations(
'virtualserver',
@ -268,7 +261,7 @@ class SettingsController extends ApiControllerBase
$uuid,
'tablecheck',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
break;
case 'table':
@ -278,7 +271,7 @@ class SettingsController extends ApiControllerBase
$uuid,
'table',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
$this->deleteRelations(
'virtualserver',
@ -286,7 +279,7 @@ class SettingsController extends ApiControllerBase
$uuid,
'table',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
break;
case 'protocol':
@ -296,13 +289,13 @@ class SettingsController extends ApiControllerBase
$uuid,
'protocol',
$nodeName,
$this->mdlRelayd
$this->getModel()
);
break;
}
$this->mdlRelayd->serializeToConfig();
$this->getModel()->serializeToConfig();
Config::getInstance()->save();
if ($this->mdlRelayd->configDirty()) {
if ($this->getModel()->configDirty()) {
$result['status'] = 'ok';
}
}
@ -312,6 +305,21 @@ class SettingsController extends ApiControllerBase
return $result;
}
/**
* toggle status
* @param string $nodeType node type to address
* @param string $uuid id to toggled
* @param string|null $enabled set enabled by default
* @return array status
* @throws \Phalcon\Validation\Exception when field validations fail
* @throws \ReflectionException when not bound to model
*/
public function toggleAction($nodeType, $uuid, $enabled = null)
{
$this->getModel()->configDirty();
return $this->toggleBase($nodeType, $uuid, $enabled);
}
/**
* search relayd settings
* @param $nodeType
@ -322,11 +330,11 @@ class SettingsController extends ApiControllerBase
$this->sessionClose();
if ($this->request->isPost() && $nodeType != null) {
$this->validateNodeType($nodeType);
$grid = new UIModelGrid($this->mdlRelayd->$nodeType);
$grid = new UIModelGrid($this->getModel()->$nodeType);
$fields = array();
switch ($nodeType) {
case 'host':
$fields = array('name', 'address');
$fields = array('enabled', 'name', 'address');
break;
case 'tablecheck':
$fields = array('name', 'type');
@ -342,7 +350,7 @@ class SettingsController extends ApiControllerBase
break;
}
$result = $grid->fetchBindRequest($this->request, $fields);
$result['dirty'] = $this->mdlRelayd->configChanged();
$result['dirty'] = $this->getModel()->configChanged();
return $result;
}
}
@ -374,7 +382,7 @@ class SettingsController extends ApiControllerBase
$relNodeType = null,
$relNodeName = null
) {
$nodes = $this->mdlRelayd->$nodeType->getNodes();
$nodes = $this->getModel()->$nodeType->getNodes();
// get nodes with relations
foreach ($nodes as $nodeUuid => $node) {
// get relation uuids
@ -382,14 +390,14 @@ class SettingsController extends ApiControllerBase
// remove uuid from field
if ($fieldUuid == $relUuid) {
$refField = $nodeType . '.' . $nodeUuid . '.' . $nodeField;
$relNode = $this->mdlRelayd->getNodeByReference($refField);
$relNode = $this->getModel()->getNodeByReference($refField);
$nodeRels = str_replace($relUuid, '', $relNode->__toString());
$nodeRels = str_replace(',,', ',', $nodeRels);
$nodeRels = rtrim($nodeRels, ',');
$nodeRels = ltrim($nodeRels, ',');
$this->mdlRelayd->setNodeByReference($refField, $nodeRels);
$this->getModel()->setNodeByReference($refField, $nodeRels);
if ($relNode->isEmptyAndRequired()) {
$nodeName = $this->mdlRelayd->getNodeByReference("{$nodeType}.{$nodeUuid}.name")->__toString();
$nodeName = $this->getModel()->getNodeByReference("{$nodeType}.{$nodeUuid}.name")->__toString();
throw new \Exception("Cannot delete $relNodeType '$relNodeName' from $nodeType '$nodeName'");
}
}

View File

@ -2,6 +2,7 @@
/**
* Copyright (C) 2018 EURO-LOG AG
* Copyright (c) 2021 Deciso B.V.
*
* All rights reserved.
*
@ -32,6 +33,7 @@ namespace OPNsense\Relayd\Api;
use OPNsense\Base\ApiControllerBase;
use OPNsense\Core\Backend;
use OPNsense\Core\Config;
use OPNsense\Relayd\Relayd;
/**
@ -43,15 +45,32 @@ class StatusController extends ApiControllerBase
/**
* get relayd summary
*/
public function sumAction()
public function sumAction($wait=0)
{
$result = array("result" => "failed");
$backend = new Backend();
$relaydMdl = new Relayd();
// when $wait is set, try for max 10 seconds to receive a sensible status (wait for unknowns to resolve)
$max_tries = !empty($wait) ? 10 : 1;
$output = array();
$output = explode("\n", trim($backend->configdRun('relayd summary')));
for ($i = 0; $i < $max_tries; $i++) {
$output = explode("\n", trim($backend->configdRun('relayd summary')));
$unknowns = 0;
foreach ($output as $line) {
if (substr($line, -strlen("unknown")) == "unknown") {
$unknowns++;
}
}
if (!empty($output[0]) && $unknowns == 0) {
break;
}
sleep(1);
}
if (empty($output[0])) {
return $result;
}
$output[] = "0\t****\t"; // end of data marker
$result["result"] = 'ok';
$virtualServerId = 0;
$virtualServerType = '';
@ -59,40 +78,119 @@ class StatusController extends ApiControllerBase
$virtualserver = array();
$rows = array();
foreach ($output as $line) {
$words = explode("\t", $line);
$id = trim($words[0]);
$type = trim($words[1]);
if ($type == 'redirect' || $type == 'relay') {
$words = array_map('trim', explode("\t", $line));
$id = $words[0];
$type = $words[1];
if ($type == 'redirect' || $type == 'relay' || $type == '****') {
// new virtual server id/type means new record
if (
($id != $virtualServerId
&& $virtualServerId > 0)
|| ($type != $virtualServerType
&& strlen($virtualServerType) > 5)
($id != $virtualServerId && $virtualServerId > 0) ||
($type != $virtualServerType && strlen($virtualServerType) > 5) ||
($type == '****' && !empty($virtualserver))
) {
// append backend hosts not found in the list, since relayd only supports disabled tables
// you might loose track of hosts that are disabled
if (!empty($virtualserver['tables'])) {
foreach ($virtualserver['tables'] as &$table) {
if (!empty($table['uuid'])) {
$tblnode = $relaydMdl->getNodeByReference("table.".$table['uuid']);
foreach (explode(",", (string)$tblnode->hosts) as $host_uuid) {
$found = false;
if (!empty($table['hosts'])) {
foreach ($table['hosts'] as $tblhost) {
foreach ($tblhost['properties'] as $hprops) {
if ($hprops['uuid'] == $host_uuid) {
$found = true;
}
}
}
} else {
$table['hosts'] = [];
}
if (!$found) {
$hostnode = $relaydMdl->getNodeByReference("host.".$host_uuid);
$table['hosts'][$host_uuid] = [
"name" => (string)$hostnode->address,
"description" => (string)$hostnode->name,
"avlblty" => null,
"status" => empty((string)$hostnode->enabled) ? "disabled" : "-",
"properties" => [
[
"uuid" => $host_uuid,
"name" => (string)$hostnode->name,
"enabled" => (string)$hostnode->enabled
]
]
];
}
}
}
}
}
$rows[] = $virtualserver;
$virtualserver = array();
if ($type == '****') {
break; // end
}
$virtualserver = [];
}
$virtualServerId = $id;
$virtualServerType = $type;
$virtualserver['id'] = $id;
$virtualserver['type'] = $type;
$virtualserver['name'] = trim($words[2]);
$virtualserver['status'] = trim($words[4]);
}
if ($type == 'table') {
$virtualserver['name'] = $words[2];
$virtualserver['status'] = $words[4];
$objs = $relaydMdl->getObjectsByAttribute("virtualserver", "name", $virtualserver['name']);
if (count($objs) > 0) {
$obj = $objs[0];
$virtualserver['uuid'] = $obj->getAttribute('uuid');
$virtualserver['listen_address'] = (string)$obj->listen_address;
$virtualserver['listen_startport'] = (string)$obj->listen_startport;
$virtualserver['listen_endport'] = (string)$obj->listen_endport;
}
} elseif ($type == 'table') {
$tableId = $id;
$virtualserver['tables'][$tableId]['name'] = trim($words[2]);
$virtualserver['tables'][$tableId]['status'] = trim($words[4]);
}
if ($type == 'host') {
if (empty($virtualserver['tables'])) {
$virtualserver['tables'] = [];
}
$virtualserver['tables'][$tableId] = [];
$virtualserver['tables'][$tableId]['name'] = $words[2];
$virtualserver['tables'][$tableId]['status'] = $words[4];
$objs = $relaydMdl->getObjectsByAttribute("table", "name", explode(":", $words[2])[0]);
if (count($objs) > 0) {
$virtualserver['tables'][$tableId]['uuid'] = $objs[0]->getAttribute('uuid');
}
} elseif ($type == 'host') {
$hostId = trim($words[0]);
$virtualserver['tables'][$tableId]['hosts'][$hostId]['name'] = trim($words[2]);
$virtualserver['tables'][$tableId]['hosts'][$hostId]['avlblty'] = trim($words[3]);
$virtualserver['tables'][$tableId]['hosts'][$hostId]['status'] = trim($words[4]);
if (empty($virtualserver['tables'][$tableId]['hosts'])) {
$virtualserver['tables'][$tableId]['hosts'] = [];
}
$virtualserver['tables'][$tableId]['hosts'][$hostId] = ['properties' => []];
$virtualserver['tables'][$tableId]['hosts'][$hostId]['name'] = $words[2];
$virtualserver['tables'][$tableId]['hosts'][$hostId]['avlblty'] = $words[3];
$status = $words[4] == 'disabled' ? 'stopped' : $words[4];
$virtualserver['tables'][$tableId]['hosts'][$hostId]['status'] = $status;
// XXX: `relayctl show summary` name is actually the number, append name as description when found
$objs = $relaydMdl->getObjectsByAttribute("host", "address", $words[2]);
if (count($objs) > 0) {
$linked_hosts = [];
if (!empty($virtualserver['tables'][$tableId]['uuid'])) {
$tblnode = $relaydMdl->getNodeByReference("table.".$virtualserver['tables'][$tableId]['uuid']);
$linked_hosts = explode(",", (string)$tblnode->hosts);
}
// hosts aren't necessarily unique due to address matching
foreach ($objs as $obj) {
$this_uuid = $obj->getAttribute('uuid');
if (empty($linked_hosts) || in_array($this_uuid, $linked_hosts)) {
$virtualserver['tables'][$tableId]['hosts'][$hostId]['properties'][] = [
'uuid' => $this_uuid,
'name' => (string)$obj->name,
"enabled" => (string)$obj->enabled
];
}
}
}
}
}
$rows[] = $virtualserver;
$result["rows"] = $rows;
return $result;
}
@ -102,29 +200,32 @@ class StatusController extends ApiControllerBase
*/
public function toggleAction($nodeType = null, $id = null, $action = null)
{
$result = array("result" => "failed", "function" => "toggle");
if ($this->request->isPost()) {
$this->sessionClose();
}
$result = array("result" => "failed", "function" => "toggle");
if (
$nodeType != null &&
($nodeType == 'redirect' ||
$nodeType == 'table' ||
$nodeType == 'host')
) {
if (
$action != null &&
($action == 'enable' ||
$action == 'disable')
) {
$backend = new Backend();
if (in_array($nodeType, ['redirect', 'table', 'host']) && in_array($action, ['enable', 'disable'])){
if ($id != null && $id > 0) {
$backend = new Backend();
$result["output"] = $backend->configdRun("relayd toggle $nodeType $action $id");
$result["output"] = $backend->configdpRun("relayd toggle",[$nodeType, $action, $id]);
if (isset($result["output"])) {
$result["result"] = 'ok';
}
$result["output"] = trim($result["output"]);
}
} elseif ($nodeType == 'host' && in_array($action, ['remove', 'add'])) {
Config::getInstance()->lock();
$new_status = $action == "remove" ? "0" : "1";
$relaydMdl = new Relayd();
foreach (explode(",", $id) as $host_uuid) {
$obj = $relaydMdl->getNodeByReference("host.".$host_uuid);
if ($obj != null) {
$obj->enabled = $new_status;
}
}
$relaydMdl->serializeToConfig();
Config::getInstance()->save();
// invoke service controller
return (new ServiceController())->reconfigureAction();
}
}
return $result;

View File

@ -1,4 +1,10 @@
<form>
<field>
<id>relayd.host.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Set this option to enable this destination.</help>
</field>
<field>
<id>relayd.host.name</id>
<label>Name</label>

View File

@ -64,4 +64,21 @@ class Relayd extends BaseModel
{
return @unlink("/tmp/relayd.dirty");
}
/**
* @param string $type type of object (host, table, virtualserver)
* @param string $name name of the attribute
* @param string $value value to match
* @return ArrayField[] items found
*/
public function getObjectsByAttribute($type, $name, $value)
{
$results = [];
foreach ($this->$type->iterateItems() as $item) {
if ((string)$item->$name == $value) {
$results[] = $item;
}
}
return $results;
}
}

View File

@ -1,6 +1,6 @@
<model>
<mount>//OPNsense/relayd</mount>
<version>1.0.2</version>
<version>1.0.3</version>
<description>Relayd settings</description>
<items>
<general>
@ -35,10 +35,20 @@
</timeout>
</general>
<host type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<name type="TextField">
<Required>Y</Required>
<mask>/^([0-9a-zA-Z\._\- ]){1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and space.</ValidationMessage>
<Constraints>
<check001>
<ValidationMessage>Host names should be unique.</ValidationMessage>
<type>UniqueConstraint</type>
</check001>
</Constraints>
</name>
<address type="TextField">
<Required>Y</Required>
@ -67,6 +77,12 @@
<Required>Y</Required>
<mask>/^([0-9a-zA-Z\._\- ]){1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and space.</ValidationMessage>
<Constraints>
<check001>
<ValidationMessage>Table names should be unique.</ValidationMessage>
<type>UniqueConstraint</type>
</check001>
</Constraints>
</name>
<enabled type="BooleanField">
<default>0</default>
@ -134,6 +150,12 @@
<Required>Y</Required>
<mask>/^([0-9a-zA-Z\._\- ]){1,255}$/u</mask>
<ValidationMessage>Should be a string between 1 and 255 characters. Allowed characters are letters and numbers as well as underscore, minus, dot and space.</ValidationMessage>
<Constraints>
<check001>
<ValidationMessage>Virtual server names should be unique.</ValidationMessage>
<type>UniqueConstraint</type>
</check001>
</Constraints>
</name>
<enabled type="BooleanField">
<default>0</default>

View File

@ -1,6 +1,7 @@
{#
Copyright © 2018 by EURO-LOG AG
Copyright (c) 2021 Deciso B.V.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
@ -95,13 +96,17 @@ POSSIBILITY OF SUCH DAMAGE.
});
['host', 'tablecheck', 'table', 'protocol', 'virtualserver'].forEach(function(element) {
$("#grid-" + element).UIBootgrid({
let endpoints = {
'search': '/api/relayd/settings/search/' + element + '/',
'get': '/api/relayd/settings/get/' + element + '/',
'set': '/api/relayd/settings/set/' + element + '/',
'add': '/api/relayd/settings/set/' + element + '/',
'del': '/api/relayd/settings/del/' + element + '/'
});
};
if (['virtualserver', 'host', 'table'].includes(element)) {
endpoints['toggle'] = '/api/relayd/settings/toggle/' + element + '/';
}
$("#grid-" + element).UIBootgrid(endpoints);
});
// show/hide options depending on other options
@ -239,6 +244,7 @@ POSSIBILITY OF SUCH DAMAGE.
<table id="grid-host" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogEditHost">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="address" data-type="string">{{ lang._('Address') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
@ -285,7 +291,7 @@ POSSIBILITY OF SUCH DAMAGE.
<table id="grid-table" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogEditTable">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="boolean">{{ lang._('Enabled') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="commands" data-width="7em" data-formatter="commands" data-sortable="false">{{ lang._('Edit') }} | {{ lang._('Delete') }}</th>
@ -331,7 +337,7 @@ POSSIBILITY OF SUCH DAMAGE.
<table id="grid-virtualserver" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="DialogEditVirtualServer">
<thead>
<tr>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="boolean">{{ lang._('Enabled') }}</th>
<th data-column-id="enabled" data-width="6em" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="name" data-type="string">{{ lang._('Name') }}</th>
<th data-column-id="type" data-type="string">{{ lang._('Type') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>

View File

@ -1,6 +1,7 @@
{#
Copyright © 2018 by EURO-LOG AG
Copyright (c) 2021 Deciso B.V.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
@ -27,128 +28,462 @@ POSSIBILITY OF SUCH DAMAGE.
#}
<script type="text/javascript">
$( document ).ready(function() {
updateServiceControlUI('relayd');
// get status and build the table body
$('#btnRefresh').unbind('click').click(function() {
ajaxCall(url = "/api/relayd/status/sum", sendData={}, callback = function(result, status) {
if (status == "success" && result.result === 'ok') {
$('#tableStatus > tbody').empty();
$('#tableStatus > tbody').attr('style', 'display:none;');
/* create a table row for each host and combine
virtualserver/table fields afterwards via rowspan */
$.each(result.rows, function (vkey, virtualserver) {
var vrowspan = 0;
var trowspan = [];
var html = '<tr class="vrow"><td id="virtualServer' + vkey + '">';
html += getControlButtons(virtualserver.status, virtualserver.id, virtualserver.type);
html += virtualserver.name + ' (' + virtualserver.type + '): ' + virtualserver.status + '</td>';
var tfirst = true;
$.each(virtualserver.tables, function(tkey, table) {
trowspan[tkey] = 0;
if (tfirst == true) {
tfirst = false;
} else {
html += '<tr>';
}
html += '<td id="table' + tkey + '">';
html += getControlButtons(table.status, tkey, 'table');
html += table.name + ' ' + table.status + '</td>';
var hfirst = true;
$.each(table.hosts, function(hkey, host) {
if (hfirst == true) {
hfirst = false;
} else {
html += '<tr>';
}
vrowspan++;
trowspan[tkey]++;
html += '<td>';
html += getControlButtons(host.status, hkey, 'host');
html += host.name + ' ' + host.status + '</td></tr>';
});
// dummy host for disabled tables
if (hfirst == true) {
vrowspan++;
html += '<td></td>';
}
});
$( document ).ready(function() {
/**
* create status and start/stop buttons
**/
function getControlButtons(status, id, nodeType) {
let status_btn = $('<button class="label label-opnsense label-opnsense-xs"/>');
let action_btn = $('<button class="btn btn-xs btn-default" data-toggle="tooltip"/>').attr('data-nodeid', id).attr('data-nodetype', nodeType);
let action_btn_remove = action_btn.clone(true);
let response = [];
$('#tableStatus > tbody').append(html);
$('#virtualServer' + vkey).attr('rowspan', vrowspan);
$.each(trowspan, function(tid, trowspan) {
$('#table' + tid).attr('rowspan', trowspan);
});
$('#tableStatus > tbody > tr.vrow > td').css('border-top', '2px solid #ddd');
$('#tableStatus > tbody').fadeIn();
});
if (status.substring(0, 6) === 'active' || status === 'up') {
status_btn.addClass('label-success').append($('<i class="fa fa-play fa-fw"/>'));
action_btn.addClass('node_action').append($('<i class="fa fa-stop fa-fw"/>')).attr('data-nodeaction', 'disable');
action_btn.attr('title', $("#stop_" + nodeType + "_label").text());
response.push(status_btn);
if (nodeType === 'host') {
action_btn_remove.addClass('node_action').append($('<i class="fa fa-times fa-fw"/>')).attr('data-nodeaction', 'remove');
action_btn_remove.attr('title', $("#remove_host_label").text());
response.push(action_btn_remove);
}
response.push(action_btn);
} else if (['stopped', 'disabled'].includes(status)) {
status_btn.addClass('label-danger').append($('<i class="fa fa-stop fa-fw"/>'));
action_btn.addClass('node_action').append($('<i class="fa fa-play fa-fw"/>')).attr('data-nodeaction', 'enable');
action_btn.attr('title', $("#start_" + nodeType + "_label").text());
if (status === 'stopped' && nodeType === 'host') {
action_btn_remove.addClass('node_action').append($('<i class="fa fa-times fa-fw"/>')).attr('data-nodeaction', 'remove');
action_btn_remove.attr('title', $("#remove_host_label").text());
response.push(status_btn, action_btn_remove, action_btn);
} else {
response.push(status_btn, action_btn);
}
} else if (status === 'down' && nodeType == 'host') {
// remove host (permanent down)
status_btn.addClass('label-danger').append($('<i class="fa fa-stop fa-fw"/>'));
action_btn.addClass('node_action').append($('<i class="fa fa-times fa-fw"/>')).attr('data-nodeaction', 'remove');
action_btn.attr('title', $("#remove_host_label").text());
response.push(status_btn, action_btn);
} else {
status_btn.addClass('label-danger').append($('<i class="fa fa-stop fa-fw"/>'));
action_btn.append($('<i class="fa fa-play fa-fw"/>')).attr('disabled', 'disabled');
response.push(status_btn, action_btn);
}
// no action for relays; see relayctl(8)
if (nodeType === 'relay') {
action_btn.removeClass('node_action').attr('disabled', 'disabled');
}
return response;
};
/**
* apply provided filters on selected data, reformat virtual server and table groups with borders
*/
function apply_filters() {
let prev_tr = null;
let filter_vs = $("#filter_virtualserver").val();
let filter_table = $("#filter_table").val();
let filter_host = $("#filter_host").val();
if ($("#hide_table_column").prop('checked')) {
$(".relayd_table").hide();
$(".relayd_table_th").hide();
} else {
$(".relayd_table").show();
$(".relayd_table_th").show();
}
$('#tableStatus > tbody > tr').each(function(){
let vs_td = $(this).find("td.relayd_virtualserver");
let tbl_td = $(this).find("td.relayd_table");
let host_td = $(this).find("td.relayd_host");
let is_visible_row = true;
// filter;
if (filter_vs !== "" && vs_td.data('display_name') !== undefined && vs_td.data('display_name').toUpperCase().indexOf(filter_vs.toUpperCase()) == -1) {
is_visible_row=false;
} else if (filter_table !== "" && tbl_td.data('display_name') !== undefined && tbl_td.data('display_name').toUpperCase().indexOf(filter_table.toUpperCase()) == -1) {
is_visible_row=false;
} else if (filter_host !== "" && host_td.data('display_name') !== undefined && host_td.data('display_name').toUpperCase().indexOf(filter_host.toUpperCase()) == -1) {
is_visible_row=false;
}
if (is_visible_row) {
$(this).show();
if (prev_tr !== null && prev_tr.find("td.relayd_virtualserver").data('id') === vs_td.data('id')){
vs_td.find("div.object_container").hide();
vs_td.css('border-top-style', 'hidden');
} else {
vs_td.find("div.object_container").show();
vs_td.css('border-top-style', 'solid');
}
if (prev_tr !== null && prev_tr.find("td.relayd_table").data('id') === tbl_td.data('id')){
tbl_td.find("div.object_container").hide();
tbl_td.css('border-top-style', 'hidden');
} else {
tbl_td.find("div.object_container").show();
tbl_td.css('border-top-style', 'solid');
}
prev_tr = $(this);
} else {
$(this).hide();
}
});
}
/**
* bind form events
*/
updateServiceControlUI('relayd');
// get status and build the table body
$('#btnRefresh').click(function(event) {
event.preventDefault();
if ($("#btnRefreshProgress").hasClass("fa-spinner")) {
return;
}
// do not wait for relayctl output on consecutive calls
let api_wait = $('#tableStatus > tbody > tr').length > 0 ? 1 : 0;
$("#btnRefreshProgress").addClass("fa-spinner fa-pulse");
ajaxCall("/api/relayd/status/sum/"+api_wait, {}, function(result, status) {
$('#tableStatus > tbody').empty();
if (status == "success" && result.result === 'ok') {
/* create a table row for each host and combine
virtualserver/table fields afterwards (hide repeating items and align borders accordingly) */
$.each(result.rows, function (vkey, virtualserver) {
let $vs_td = $('<td data-id="'+vkey+'" class="relayd_virtualserver"/>');
let listen_str = "";
if (virtualserver.listen_address !== undefined) {
listen_str = " [" + virtualserver.listen_address + ":" + virtualserver.listen_startport +"] "
}
$vs_td.append(
$('<div class="object_container"/>').append(
getControlButtons(virtualserver.status, virtualserver.id, virtualserver.type),
virtualserver.name,
listen_str,
' (' + virtualserver.type + '): ',
virtualserver.status
).data('payload', virtualserver).data('type', 'virtualserver')
);
$vs_td.data('display_name', virtualserver.name + listen_str);
if (virtualserver.tables) {
$.each(virtualserver.tables, function(tkey, table) {
let $tbl_td = $('<td data-id="'+tkey+'" class="relayd_table"/>');
$tbl_td.data('display_name', table.name);
$tbl_td.append(
$('<div class="object_container"/>').append(
getControlButtons(table.status, tkey, 'table'),
table.name + ' ' + table.status
).data('payload', table).data('type', 'table')
);
if (table.hosts) {
$.each(table.hosts, function(hkey, host) {
let $host_td = $('<td data-id="'+hkey+'" class="relayd_host"/>');
let host_names = [];
let display_name = host.name ;
if (host.properties != undefined) {
for (i=0; i < host.properties.length; i++) {
if (host.properties[i].name !== host.name) {
host_names.push(host.properties[i].name);
}
}
if (host_names.length > 0) {
display_name = display_name + ' [' + host_names.join(',') + '] ';
}
}
$host_td.append(
$('<div class="object_container"/>').append(
getControlButtons(host.status, hkey, 'host'),
display_name,
" ",
host.status
).data('payload', host).data('type', 'host')
);
$host_td.data('display_name', display_name);
$('#tableStatus > tbody').append(
$("<tr/>").append(
$vs_td.clone(true),
$tbl_td.clone(true),
$host_td
)
);
});
} else {
$('#tableStatus > tbody').append(
$("<tr/>").append(
$vs_td.clone(true),
$tbl_td.clone(true),
$("<td/>")
)
);
}
});
} else {
$('#tableStatus > tbody').append(
$("<tr/>").append(
$vs_td.clone(true),
$("<td/>"),
$("<td/>")
)
);
}
});
$('[data-toggle="tooltip"]').tooltip();
apply_filters();
// bind node actions
$(".node_action").click(function(){
let container = $(this).closest("div.object_container");
let item_payload = container.data('payload');
let action = $(this).data('nodeaction');
let nodeid = $(this).data('nodeid');
let nodetype = $(this).data('nodetype');
let host_uuids = [];
let host_enabled = false;
if (item_payload.properties != undefined) {
for (i=0; i < item_payload.properties.length; i++) {
host_uuids.push(item_payload.properties[i].uuid);
if (item_payload.properties[i].enabled === "1") {
host_enabled = true;
}
}
}
if (action === "remove" && nodetype == 'host') {
if (item_payload !== undefined) {
stdDialogConfirm(
'{{ lang._('Relayd') }}',
$("#remove_host_message").text().trim(),
'{{ lang._('Yes') }}',
'{{ lang._('No') }}',
function () {
if (host_uuids.length > 0) {
ajaxCall("/api/relayd/status/toggle/host/" + host_uuids.join(',') + "/remove", {}, function(result, status) {
$("#btnRefresh").click();
});
}
}
);
}
} else if (action === "enable" && nodetype == 'host' && !host_enabled) {
stdDialogConfirm(
'{{ lang._('Relayd') }}',
$("#add_host_message").text().trim(),
'{{ lang._('Yes') }}',
'{{ lang._('No') }}',
function () {
if (item_payload.properties != undefined) {
if (host_uuids.length > 0) {
ajaxCall("/api/relayd/status/toggle/host/" + host_uuids.join(',') + "/add", {}, function(result, status) {
$("#btnRefresh").click();
});
}
}
}
);
} else {
// default action, parameters for relayctl
ajaxCall("/api/relayd/status/toggle/" + nodetype + "/" + nodeid + "/" + action, {}, function(result, status) {
$("#btnRefresh").click();
});
}
});
} else {
$("#tableStatus").html("<tr><td><br/>{{ lang._('The status could not be fetched. Is Relayd running?') }}</td></tr>");
}
$('#btnRefresh').blur();
$("#btnRefreshProgress").removeClass("fa-spinner fa-pulse");
});
});
$(".filter_item").keyup(apply_filters);
// initial load
/**
* stored selections (presets) events
*/
function update_presets(selected) {
let settings = JSON.parse(window.localStorage.getItem("api.relayd.presets"));
if (settings === null) {
settings = {};
}
$("#stored_filters").empty();
$("#stored_filters").append($("<option/>").val("").text("{{ lang._('Add new')}}"));
$("#stored_filters").append($("<option/>").val("__clear__").text("{{ lang._('Clear')}}"));
Object.keys(settings).sort().forEach(function(name) {
$("#stored_filters").append(
$("<option/>").val(name).text(name)
);
});
if (selected !== undefined) {
$("#stored_filters").val(selected);
}
$("#stored_filters").selectpicker("refresh");
}
$("#stored_filters").change(function(){
if ($(this).val() === "__clear__") {
$("#hide_table_column").prop("checked", false);
$(".filter_item").val("");
} else if ($(this).val() !== "" !== $(this).val() !== "__clear__") {
let settings = JSON.parse(window.localStorage.getItem("api.relayd.presets"));
if (settings !== null && settings[$(this).val()] !== undefined) {
let selections = settings[$(this).val()];
Object.keys(selections).forEach(function(prop) {
let target = $("#"+prop);
if (target.prop("type") === "checkbox") {
target.prop('checked', selections[prop]);
} else {
target.val(selections[prop]);
}
});
}
}
apply_filters();
});
$("#hide_table_column").change(apply_filters);
$("#template_add").click(function(event){
event.preventDefault();
let selected_name = "";
if ($(this).val() !== "__clear__") {
selected_name = $("#stored_filters").val();
}
BootstrapDialog.show({
type:BootstrapDialog.TYPE_DANGER,
title: "<?= gettext("Relayd");?>",
message: [
$("<span/>").text("<?=gettext('Save as :');?>"),
$("<input type='text' class='form-control'/>").val(selected_name),
],
buttons: [{
label: "<?= gettext("Abort");?>",
action: function(dialogRef) {
dialogRef.close();
}}, {
label: "<?= gettext("Save");?>",
action: function(dialogRef) {
let template_name = dialogRef.getModalBody().find('input').val();
let settings = JSON.parse(window.localStorage.getItem("api.relayd.presets"));
if (settings === null) {
settings = {};
}
settings[template_name] = {
"filter_virtualserver": $("#filter_virtualserver").val(),
"filter_table": $("#filter_table").val(),
"filter_host": $("#filter_host").val(),
"hide_table_column": $("#hide_table_column").is(':checked')
};
window.localStorage.setItem("api.relayd.presets", JSON.stringify(settings));
update_presets(template_name);
dialogRef.close();
}
}]
});
});
$("#template_delete").click(function(){
let settings = JSON.parse(window.localStorage.getItem("api.relayd.presets"));
let prop = $("#stored_filters").val();
if (settings !== null && settings[prop] !== undefined) {
delete settings[prop];
window.localStorage.setItem("api.relayd.presets", JSON.stringify(settings));
update_presets();
}
});
/**
* initial load
*/
$("#btnRefresh").click();
if (window.localStorage) {
update_presets();
$("#presets").collapse('show');
} else {
// without localStorage, presets aren't supported
$("#stored_presets_panel").delete();
}
});
// create status and start/stop buttons
function getControlButtons(status, id, nodeType) {
var statusClass = "btn btn-xs glyphicon ";
var controlClass = "btn btn-xs btn-default glyphicon ";
var action;
if (status.substring(0, 6) === 'active' || status === 'up') {
statusClass += "btn-success glyphicon-play";
controlClass += "glyphicon-stop";
controlTitle = "Stop this " + nodeType;
action = 'onclick="toggleNode(\'' + nodeType + '\', ' + id + ', \'disable\')""';
} else if (status === 'disabled') {
statusClass += "btn-danger glyphicon-stop";
controlClass += "glyphicon-play";
controlTitle = "Start this " + nodeType;
action = 'onclick="toggleNode(\'' + nodeType + '\', ' + id + ', \'enable\')"';
} else {
statusClass += "btn-danger glyphicon-stop";
controlClass += "glyphicon-play";
action = 'disabled="disabled"';
}
// no action for relays; see relayctl(8)
if (nodeType === 'relay') {
action = 'disabled="disabled"';
}
var html = '<span class="' + statusClass + '" style="cursor: default;"> </span>&nbsp;';
html += '<span ' + action + ' class="' + controlClass + '" title="' + controlTitle + '"> </span>&nbsp;';
return html;
};
// enable/disable redirects, tables or hosts
function toggleNode(nodeType, id, action) {
ajaxCall(url = "/api/relayd/status/toggle/" + nodeType + "/" + id + "/" + action, callback = function(result, status) {
$("#btnRefresh").click();
});
};
</script>
<div class="content-box">
<table id="tableStatus" class="table table-condensed">
<thead><tr><th>{{ lang._('Virtual Server') }}</th><th>{{ lang._('Table') }}</th><th>{{ lang._('Host') }}</th></tr></thead>
<tbody style="display:none;"></tbody>
</table>
<div class="col-sm-12">
<div class="row">
<table class="table">
<tr>
<td>
<div class="pull-right">
<button class="btn btn-primary" id="btnRefresh" type="button"><b>{{ lang._('Refresh') }}</b> <span id="btnRefreshProgress" class="fa fa-refresh"> </span></button>
</div>
</td>
</tr>
</table>
<style type="text/css">
.panel-heading-sm{
height: 28px;
padding: 4px 10px;
}
</style>
<div style="display:none" id="tooltips">
<!--
dynamic labels to ease translations
-->
<label id="stop_host_label">{{ lang._('Stop this host') }}</label>
<label id="stop_table_label">{{ lang._('Stop this table') }}</label>
<label id="stop_relay_label">{{ lang._('Stop this relay') }}</label>
<label id="stop_redirect_label">{{ lang._('Stop this redirect') }}</label>
<label id="start_host_label">{{ lang._('Start this host') }}</label>
<label id="start_table_label">{{ lang._('Start this table') }}</label>
<label id="start_relay_label">{{ lang._('Start this relay') }}</label>
<label id="start_redirect_label">{{ lang._('Start this redirect') }}</label>
<label id="remove_host_label">{{ lang._('Disable this host') }}</label>
<label id="remove_host_message">{{ lang._('
Are you sure you do want to disable this host?
When being used in other virtual servers it will be disabled in there as well.') }}
</label>
<label id="add_host_message">{{ lang._('
Are you sure you do want to enable this host?
When being used in other virtual servers it will be enabled in there as well.') }}
</label>
</div>
<div class="panel panel-primary" id="stored_presets_panel">
<div class="panel-heading panel-heading-sm">
<i class="fa fa-chevron-down" style="cursor: pointer;" data-toggle="collapse" data-target="#presets"></i>
<b>{{ lang._('Presets') }}</b>
</div>
<div class="panel-body collapse" id="presets">
<form class="form-inline">
<div class="form-group">
<select class="selectpicker" id="stored_filters" data-toggle="tooltip" title="{{ lang._('Saved selections')}}">
</select>
<button id="template_add" type="button" class="btn btn-default" data-toggle="tooltip" title="{{ lang._('Add template')}}">
<span class="fa fa-save"></span>
</button>
<button id="template_delete" type="button" class="btn btn-default" data-toggle="tooltip" title="{{ lang._('Deleted selected template')}}">
<span class="fa fa-trash"></span>
</button>
<div class="checkbox">
<input type="checkbox" id="hide_table_column">
<label for="hide_table_column"><strong>{{ lang._('Hide table column')}}</strong></label>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<table id="tableStatus" class="table table-condensed">
<thead>
<tr>
<th class="relayd_virtualserver_th">{{ lang._('Virtual Server') }}</th>
<th class="relayd_table_th">{{ lang._('Table') }}</th>
<th class="relayd_host_th">{{ lang._('Host') }}</th>
</tr>
<tr>
<th class="relayd_virtualserver_th"><input type="text" id="filter_virtualserver" class="input-sm filter_item" autocomplete="off"></th>
<th class="relayd_table_th"><input type="text" id="filter_table" class="input-sm filter_item" autocomplete="off"></th>
<th class="relayd_host_th"><input type="text" id="filter_host" class="input-sm filter_item" autocomplete="off"></th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<td colspan="3">
<div class="pull-right">
<button class="btn btn-primary" id="btnRefresh" type="button"><b>{{ lang._('Refresh') }}</b>
<span id="btnRefreshProgress" class="fa fa-refresh"> </span>
</button>
</div>
</td>
</tr>
</tfoot>
</table>
</div>
</div>

View File

@ -1,6 +1,7 @@
# DO NOT EDIT THIS FILE -- OPNsense auto-generated file
{% from 'OPNsense/Macros/interface.macro' import physical_interface %}
{% set all_active_tables = [] %}
{% if helpers.exists('OPNsense.relayd.general') %}
{% if helpers.exists('OPNsense.relayd.general.interval') %}
interval {{ OPNsense.relayd.general.interval }}
@ -18,21 +19,30 @@ timeout {{ OPNsense.relayd.general.timeout }}
{% if helpers.exists('OPNsense.relayd.table') %}
{% for table in helpers.toList('OPNsense.relayd.table') %}
{% set name = table.name %}
{% set disable = '' %}
{% set hosts = '' %}
{% set hosts = [] %}
{% if table.enabled|default('1') == '0' %}
{% set disable = ' disable' %}
{% endif %}
table <{{ name }}>{{ disable }} {
{% for host in table.hosts.split(",") %}
{% set host = helpers.getUUID(host) %}
{% set ipTTL = " ip ttl " ~ host.ipTTL if host.ipTTL is defined %}
{% set priority = " priority " ~ host.priority if host.priority is defined %}
{% set retry = " retry " ~ host.retry if host.retry is defined %}
{% for hostid in table.hosts.split(",") %}
{% set host = helpers.getUUID(hostid) %}
{% if host.enabled|default("1") == "1" %}
{% do hosts.append(host) %}
{% endif %}
{% endfor %}
{% if hosts|length > 0 %}
table <{{ table.name }}>{{ disable }} {
{% do all_active_tables.append(table['@uuid']) %}
{% for host in hosts %}
{% set ipTTL = " ip ttl " ~ host.ipTTL if host.ipTTL is defined %}
{% set priority = " priority " ~ host.priority if host.priority is defined %}
{% set retry = " retry " ~ host.retry if host.retry is defined %}
{% if host.enabled|default("1") == "1" %}
{{ host.address }}{{ ipTTL }}{{ priority }}{{ retry }}
{% endfor %}
{% endif %}
{% endfor %}
}
{% endif %}
{% endfor %}
{% endif %}
@ -45,106 +55,110 @@ table <{{ name }}>{{ disable }} {
{% endif %}
{% if helpers.exists('OPNsense.relayd.virtualserver') %}
{% for virtualserver in helpers.toList('OPNsense.relayd.virtualserver') %}
{% for virtualserver in helpers.toList('OPNsense.relayd.virtualserver') %}
{% if virtualserver.transport_table in all_active_tables or virtualserver.backuptransport_table in all_active_tables %}
{{ virtualserver.type }} "{{virtualserver.name}}" {
{% if virtualserver.enabled|default('1') == '0' %}
{% if virtualserver.enabled|default('1') == '0' %}
disable
{% endif %}
{% set listen = "listen on " ~ virtualserver.listen_address %}
{% if virtualserver.listen_startport is defined %}
{% set listen = listen ~ " port " ~ virtualserver.listen_startport %}
{% if virtualserver.listen_endport is defined and virtualserver.type == 'redirect'%}
{% set listen = listen ~ ":" ~ virtualserver.listen_endport %}
{% endif %}
{% endif %}
{% if virtualserver.listen_interface is defined and virtualserver.type == 'redirect' %}
{% set listen = listen ~ " interface " ~ physical_interface(virtualserver.listen_interface) %}
{% endif %}
{% endif %}
{% set listen = "listen on " ~ virtualserver.listen_address %}
{% if virtualserver.listen_startport is defined %}
{% set listen = listen ~ " port " ~ virtualserver.listen_startport %}
{% if virtualserver.listen_endport is defined and virtualserver.type == 'redirect'%}
{% set listen = listen ~ ":" ~ virtualserver.listen_endport %}
{% endif %}
{% endif %}
{% if virtualserver.listen_interface is defined and virtualserver.type == 'redirect' %}
{% set listen = listen ~ " interface " ~ physical_interface(virtualserver.listen_interface) %}
{% endif %}
{{ listen }}
{% set transport_type = 'forward' %}
{% if virtualserver.type == 'redirect' %}
{% set transport_type = virtualserver.transport_type %}
{% if virtualserver.transport_type == 'route' %}
{% set routing_interface = " interface " ~ virtualserver.routing_interface %}
{% endif %}
{% endif %}
{% set table = helpers.getUUID(virtualserver.transport_table) %}
{% set tablecheck = helpers.getUUID(virtualserver.transport_tablecheck) %}
{% set _tablecheck = '' %}
{% if tablecheck.type == 'http' and tablecheck.path is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type %}
{% if tablecheck.ssl|default('0') == '1' %}
{% set _tablecheck = _tablecheck ~ 's' %}
{% endif %}
{% set _tablecheck = _tablecheck ~ ' "' ~ tablecheck.path ~ '"' %}
{% if tablecheck.host is defined %}
{% set _tablecheck = _tablecheck ~ ' host ' ~ tablecheck.host %}
{% endif %}
{% if tablecheck.code is defined %}
{% set _tablecheck = _tablecheck ~ ' code ' ~ tablecheck.code %}
{% elif tablecheck.digest is defined %}
{% set _tablecheck = _tablecheck ~ ' digest "' ~ tablecheck.digest ~ '"' %}
{% else %}
{% set _tablecheck = '' %}
{% endif %}
{% elif tablecheck.type == 'script' and tablecheck.path is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type ~ ' "' ~ tablecheck.path ~ '"' %}
{% elif tablecheck.type == 'send' and tablecheck.expect is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type ~ ' "' ~ tablecheck.data ~ '" expect "' ~ tablecheck.expect ~ '"' %}
{% if tablecheck.ssl|default('0') == '1' %}
{% set _tablecheck = _tablecheck ~ ' ssl' %}
{% endif %}
{% else %}
{% set _tablecheck = 'check ' ~ tablecheck.type %}
{% endif %}
{% set port = " port " ~ virtualserver.transport_port if virtualserver.transport_port is defined %}
{% set timeout = " timeout " ~ virtualserver.transport_timeout if virtualserver.transport_timeout is defined %}
{% set interval = " interval " ~ virtualserver.transport_interval if virtualserver.transport_interval is defined %}
{% set transport_type = 'forward' %}
{% if virtualserver.type == 'redirect' %}
{% set transport_type = virtualserver.transport_type %}
{% if virtualserver.transport_type == 'route' %}
{% set routing_interface = " interface " ~ virtualserver.routing_interface %}
{% endif %}
{% endif %}
{% if virtualserver.transport_table in all_active_tables %}
{% set table = helpers.getUUID(virtualserver.transport_table) %}
{% set tablecheck = helpers.getUUID(virtualserver.transport_tablecheck) %}
{% set _tablecheck = '' %}
{% if tablecheck.type == 'http' and tablecheck.path is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type %}
{% if tablecheck.ssl|default('0') == '1' %}
{% set _tablecheck = _tablecheck ~ 's' %}
{% endif %}
{% set _tablecheck = _tablecheck ~ ' "' ~ tablecheck.path ~ '"' %}
{% if tablecheck.host is defined %}
{% set _tablecheck = _tablecheck ~ ' host ' ~ tablecheck.host %}
{% endif %}
{% if tablecheck.code is defined %}
{% set _tablecheck = _tablecheck ~ ' code ' ~ tablecheck.code %}
{% elif tablecheck.digest is defined %}
{% set _tablecheck = _tablecheck ~ ' digest "' ~ tablecheck.digest ~ '"' %}
{% else %}
{% set _tablecheck = '' %}
{% endif %}
{% elif tablecheck.type == 'script' and tablecheck.path is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type ~ ' "' ~ tablecheck.path ~ '"' %}
{% elif tablecheck.type == 'send' and tablecheck.expect is defined %}
{% set _tablecheck = 'check ' ~ tablecheck.type ~ ' "' ~ tablecheck.data ~ '" expect "' ~ tablecheck.expect ~ '"' %}
{% if tablecheck.ssl|default('0') == '1' %}
{% set _tablecheck = _tablecheck ~ ' ssl' %}
{% endif %}
{% else %}
{% set _tablecheck = 'check ' ~ tablecheck.type %}
{% endif %}
{% set port = " port " ~ virtualserver.transport_port if virtualserver.transport_port is defined %}
{% set timeout = " timeout " ~ virtualserver.transport_timeout if virtualserver.transport_timeout is defined %}
{% set interval = " interval " ~ virtualserver.transport_interval if virtualserver.transport_interval is defined %}
{{ transport_type }} to <{{ table.name }}>{{ port }} mode {{ virtualserver.transport_tablemode }}{{ timeout }} {{ interval }} {{ _tablecheck }} {{ routing_interface }}
{% if virtualserver.backuptransport_table is defined and virtualserver.transport_type == 'forward' %}
{% set backuptable = helpers.getUUID(virtualserver.backuptransport_table) %}
{% set backuptablecheck = helpers.getUUID(virtualserver.backuptransport_tablecheck) if virtualserver.backuptransport_tablecheck is defined %}
{% set _backuptablecheck = '' %}
{% if backuptablecheck.type == 'http' and backuptablecheck.path is defined%}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type %}
{% if backuptablecheck.ssl|default('0') == '1' %}
{% set _backuptablecheck = _backuptablecheck ~ 's' %}
{% endif %}
{% set _backuptablecheck = _backuptablecheck ~ ' "' ~ backuptablecheck.path ~ '"' %}
{% if backuptablecheck.host is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' host ' ~ backuptablecheck.host %}
{% endif %}
{% if backuptablecheck.code is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' code ' ~ backuptablecheck.code %}
{% elif backuptablecheck.digest is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' digest "' ~ backuptablecheck.digest ~ '"' %}
{% else %}
{% set _backuptablecheck = '' %}
{% endif %}
{% elif backuptablecheck.type == 'script' and backuptablecheck.path is defined %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type ~ ' "' ~ backuptablecheck.path ~ '"' %}
{% elif backuptablecheck.type == 'send' and backuptablecheck.expect is defined %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type ~ ' "' ~ backuptablecheck.data ~ '" expect "' ~ backuptablecheck.expect ~ '"' %}
{% if backuptablecheck.ssl|default('0') == '1' %}
{% set _backuptablecheck = _backuptablecheck ~ ' ssl' %}
{% endif %}
{% else %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type %}
{% endif %}
{% set backuptimeout = " timeout " ~ virtualserver.backuptransport_timeout if virtualserver.backuptransport_timeout is defined %}
{% set backupinterval = " interval " ~ virtualserver.backuptransport_interval if virtualserver.backuptransport_interval is defined %}
{% endif %}
{% set backuptablecheck = helpers.getUUID(virtualserver.backuptransport_tablecheck) if virtualserver.backuptransport_tablecheck is defined else None%}
{% if backuptablecheck and virtualserver.backuptransport_table is defined and virtualserver.backuptransport_table in all_active_tables and virtualserver.transport_type == 'forward' %}
{% set backuptable = helpers.getUUID(virtualserver.backuptransport_table) %}
{% set _backuptablecheck = '' %}
{% if backuptablecheck.type == 'http' and backuptablecheck.path is defined%}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type %}
{% if backuptablecheck.ssl|default('0') == '1' %}
{% set _backuptablecheck = _backuptablecheck ~ 's' %}
{% endif %}
{% set _backuptablecheck = _backuptablecheck ~ ' "' ~ backuptablecheck.path ~ '"' %}
{% if backuptablecheck.host is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' host ' ~ backuptablecheck.host %}
{% endif %}
{% if backuptablecheck.code is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' code ' ~ backuptablecheck.code %}
{% elif backuptablecheck.digest is defined %}
{% set _backuptablecheck = _backuptablecheck ~ ' digest "' ~ backuptablecheck.digest ~ '"' %}
{% else %}
{% set _backuptablecheck = '' %}
{% endif %}
{% elif backuptablecheck.type == 'script' and backuptablecheck.path is defined %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type ~ ' "' ~ backuptablecheck.path ~ '"' %}
{% elif backuptablecheck.type == 'send' and backuptablecheck.expect is defined %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type ~ ' "' ~ backuptablecheck.data ~ '" expect "' ~ backuptablecheck.expect ~ '"' %}
{% if backuptablecheck.ssl|default('0') == '1' %}
{% set _backuptablecheck = _backuptablecheck ~ ' ssl' %}
{% endif %}
{% else %}
{% set _backuptablecheck = 'check ' ~ backuptablecheck.type %}
{% endif %}
{% set backuptimeout = " timeout " ~ virtualserver.backuptransport_timeout if virtualserver.backuptransport_timeout is defined %}
{% set backupinterval = " interval " ~ virtualserver.backuptransport_interval if virtualserver.backuptransport_interval is defined %}
{{ transport_type }} to <{{ backuptable.name }}>{{ port }} mode {{ virtualserver.transport_tablemode }}{{ backuptimeout }} {{ backupinterval }} {{ _backuptablecheck }}
{% endif %}
{% if virtualserver.sessiontimeout is defined %}
{% endif %}
{% if virtualserver.sessiontimeout is defined %}
session timeout {{ virtualserver.sessiontimeout }}
{% endif %}
{% if virtualserver.stickyaddress|default('0') == '1' and virtualserver.type == 'redirect' %}
{% endif %}
{% if virtualserver.stickyaddress|default('0') == '1' and virtualserver.type == 'redirect' %}
sticky-address
{% endif %}
{% if virtualserver.protocol is defined and virtualserver.type == 'relay' %}
{% set protocol = helpers.getUUID(virtualserver.protocol) %}
{% endif %}
{% if virtualserver.protocol is defined and virtualserver.type == 'relay' %}
{% set protocol = helpers.getUUID(virtualserver.protocol) %}
protocol "{{ protocol.name }}"
{% endif %}
{% endif %}
}
{% endfor %}
{% endif %}
{% endfor %}
{% endif %}