Files
LibreQoS/src/rust/lqos_node_manager/static/config.html
2024-04-30 09:32:46 -05:00

1060 lines
55 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="/vendor/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="/vendor/solid.min.css">
<link rel="stylesheet" href="/lqos.css">
<link rel="icon" href="/favicon.png">
<title>LibreQoS - Local Node Manager</title>
<script src="/lqos.js"></script>
<script src="/vendor/plotly-2.16.1.min.js"></script>
<script src="/vendor/jquery.min.js"></script><script src="/vendor/msgpack.min.js"></script>
<script defer src="/vendor/bootstrap.bundle.min.js"></script>
</head>
<body class="bg-secondary">
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/"><img src="/vendor/tinylogo.svg" alt="LibreQoS SVG Logo" width="25" height="25" />&nbsp;LibreQoS</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/tree?parent=0"><i class="fa fa-tree"></i> Tree</a>
</li>
<li class="nav-item">
<a class="nav-link" aria-current="page" href="/shaped"><i class="fa fa-users"></i> Shaped Devices <span id="shapedCount" class="badge badge-pill badge-success green-badge">?</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/unknown"><i class="fa fa-address-card"></i> Unknown IPs <span id="unshapedCount" class="badge badge-warning orange-badge">?</span></a>
</li>
</ul>
</div>
<ul class="navbar-nav ms-auto">
<li class="nav-item" id="currentLogin"></li>
<li class="nav-item ms-auto">
<a class="nav-link active" href="/config"><i class="fa fa-gear"></i> Configuration</a>
</li>
<li>
<a class="nav-link btn btn-small black-txt" href="#" id="btnReload"><i class="fa fa-refresh"></i> Reload LibreQoS</a>
</li>
</ul>
</div>
</nav>
<div id="container" class="pad4">
<div class="row">
<div class="col-sm-1"></div>
<div class="col-sm-12">
<div class="card bg-light">
<div class="card-body">
<h5 class="card-title"><i class="fa fa-users"></i> Configuration</h5>
<div class="col-sm-8 mx-auto" class="pad4 mbot4" id="controls">
<a href="#" class="btn btn-primary" id="btnSaveIspConfig"><i class="fa fa-save"></i> Save Configuration</a>
</div>
<div class="d-flex align-items-start">
<div class="nav flex-column nav-pills me-3" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<button class="nav-link active" id="v-pills-display-tab" data-bs-toggle="pill" data-bs-target="#v-pills-display" type="button" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-tv"></i> Display</button>
<button class="nav-link" id="v-pills-home-tab" data-bs-toggle="pill" data-bs-target="#v-pills-home" type="button" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-server"></i> General</button>
<button class="nav-link" id="v-pills-anon-tab" data-bs-toggle="pill" data-bs-target="#v-pills-anon" type="button" role="tab" aria-controls="v-pills-profile" aria-selected="false"><i class="fa fa-user-secret"></i> Anonymous Usage Stats</button>
<button class="nav-link" id="v-pills-tuning-tab" data-bs-toggle="pill" data-bs-target="#v-pills-tuning" type="button" role="tab" aria-controls="v-pills-server" aria-selected="false"><i class="fa fa-warning"></i> Tuning</button>
<button class="nav-link" id="v-pills-bridge-tab" data-bs-toggle="pill" data-bs-target="#v-pills-bridge" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-chain"></i> Bridge/On-a-Stick</button>
<button class="nav-link" id="v-pills-queues-tab" data-bs-toggle="pill" data-bs-target="#v-pills-queues" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-car"></i> Queues</button>
<button class="nav-link" id="v-pills-lts-tab" data-bs-toggle="pill" data-bs-target="#v-pills-lts" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-line-chart"></i> Long-Term Stats</button>
<button class="nav-link" id="v-pills-iprange-tab" data-bs-toggle="pill" data-bs-target="#v-pills-iprange" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-address-card"></i> IP Ranges</button>
<button class="nav-link" id="v-pills-flows-tab" data-bs-toggle="pill" data-bs-target="#v-pills-flows" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-arrow-circle-down"></i> Flow Tracking</button>
<button class="nav-link" id="v-pills-icommon-tab" data-bs-toggle="pill" data-bs-target="#v-pills-icommon" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> Integration - Common</button>
<button class="nav-link" id="v-pills-spylnx-tab" data-bs-toggle="pill" data-bs-target="#v-pills-spylnx" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> Spylnx</button>
<button class="nav-link" id="v-pills-uisp-tab" data-bs-toggle="pill" data-bs-target="#v-pills-uisp" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> UISP</button>
<button class="nav-link" id="v-pills-powercode-tab" data-bs-toggle="pill" data-bs-target="#v-pills-powercode" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> Powercode</button>
<button class="nav-link" id="v-pills-sonar-tab" data-bs-toggle="pill" data-bs-target="#v-pills-sonar" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> Sonar</button>
<button class="nav-link" id="v-pills-influxdb-tab" data-bs-toggle="pill" data-bs-target="#v-pills-influxdb" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-link"></i> InfluxDB</button>
<button class="nav-link" id="v-pills-netjson-tab" data-bs-toggle="pill" data-bs-target="#v-pills-netjson" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-map"></i> Network Layout</button>
<button class="nav-link" id="v-pills-shapeddevs-tab" data-bs-toggle="pill" data-bs-target="#v-pills-shapeddevs" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-table"></i> Shaped Devices</button>
<button class="nav-link" id="v-pills-users-tab" data-bs-toggle="pill" data-bs-target="#v-pills-users" type="button" role="tab" aria-controls="v-pills-settings" aria-selected="false"><i class="fa fa-users"></i> LibreQoS Users</button>
</div>
<div class="tab-content" id="v-pills-tabContent">
<!-- Tabbed config elements -->
<div class="tab-pane fade show active" id="v-pills-display" role="tabpanel" aria-labelledby="v-pills-display-tab">
<h2><i class="fa fa-wifi"></i> Display Settings</h2>
<table class="table">
<tr>
<td>
<p class="alert alert-info" role="alert">
<i class="fa fa-info"></i> The settings on this tab are per-computer and are stored locally in your browser's "local storage".
</p>
</td>
</tr>
<tr>
<td>
<input class="form-check-input" type="checkbox" id="redact">
<label class="form-check-label" for="redact">
Redact Customer Information (screenshot mode)
</label>
</td>
</tr>
<tr>
<td>
<select id="colorMode">
<option id="0">Regular Colors</option>
<option id="1">Metaverse Colors</option>
</select>
<label class="form-select-label" for="colorMode">
RTT Color Mode
</label>
</td>
</tr>
<tr>
<td><a class="btn btn-primary" id="applyDisplay">Apply Changes</a></td>
</tr>
</table>
</div>
<!-- General tab -->
<div class="tab-pane fade" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
<h2><i class="fa fa-server"></i> General Settings</h2>
<table class="table">
<tr>
<td><label for="bindVersion">Version: </label></td>
<td><input type="text" id="bindVersion" style="background: #ddd" /></td>
</tr>
<tr>
<td><label for="bindPath">Path to LibreQoS: </label></td>
<td><input type="text" id="bindPath" style="width: 500px" /></td>
</tr>
<tr>
<td><label for="bindNodeId">Node ID: </label></td>
<td><input type="text" id="bindNodeId" style="width: 500px" /></td>
</tr>
<tr>
<td><label for="bindNodeName">Node Name: </label></td>
<td><input type="text" id="bindNodeName" style="width: 500px" /></td>
</tr>
<tr>
<td><label for="bindPacketCaptureTime">Packet Capture Time: </label></td>
<td><input type="number" min="1" max="300" id="bindPacketCaptureTime" /></td>
</tr>
<tr>
<td><label for="bindQueueCheckPeriodMs">Queue Check Period (ms): </label></td>
<td><input type="number" min="10" max="5000" id="bindQueueCheckPeriodMs" /></td>
</tr>
</table>
</div>
<!-- Anonymous Usage tab -->
<div class="tab-pane fade" id="v-pills-anon" role="tabpanel" aria-labelledby="v-pills-anon-tab">
<h2><i class="fa fa-user-secret"></i>Anonymous Usage Statistics Settings</h2>
<table class="table">
<tr>
<td><label for="bindSendAnonymous">Send Anonymous Stats: </label></td>
<td><input type="checkbox" id="bindSendAnonymous" /></td>
</tr>
<tr>
<td><label for="bindAnonymousServer">Anonymous Stats Server: </label></td>
<td><input type="text" id="bindAnonymousServer" style="width: 500px" /></td>
</tr>
</table>
</div>
<!-- Tuning tab -->
<div class="tab-pane fade" id="v-pills-tuning" role="tabpanel" aria-labelledby="v-pills-tuning-tab">
<h2><i class="fa fa-warning"></i> Tuning Settings</h2>
<table class="table">
<tr>
<td><label for="bindStopIrqBalance">Stop IRQ Balancer: </label></td>
<td><input type="checkbox" id="bindStopIrqBalance" /></td>
</tr>
<tr>
<td><label for="bindNetdevBudgetUs">Netdev Budget (usecs): </label></td>
<td><input type="number" id="bindNetdevBudgetUs" /></td>
</tr>
<tr>
<td><label for="bindNetdevBudgetPackets">Netdev Budget (packets): </label></td>
<td><input type="number" id="bindNetdevBudgetPackets" /></td>
</tr>
<tr>
<td><label for="bindRxUs">Rx Period (usecs): </label></td>
<td><input type="number" id="bindRxUs" /></td>
</tr>
<tr>
<td><label for="bindTxUs">Tx Period (usecs): </label></td>
<td><input type="number" id="bindTxUs" /></td>
</tr>
<tr>
<td><label for="bindDisableRxVlan">Disable RxVlan Offload: </label></td>
<td><input type="checkbox" id="bindDisableRxVlan" /></td>
</tr>
<tr>
<td><label for="bindDisableTxVlan">Disable TxVlan Offload: </label></td>
<td><input type="checkbox" id="bindDisableTxVlan" /></td>
</tr>
<tr>
<td><label for="bindDisableOffload">Offloads to Disable: </label></td>
<td><input type="text" id="bindDisableOffload" style="width: 500px" /></td>
</tr>
</table>
</div>
<!-- Bridge/On-a-Stick tab -->
<div class="tab-pane fade" id="v-pills-bridge" role="tabpanel" aria-labelledby="v-pills-bridge-tab">
<h2><i class="fa fa-chain"></i> Bridge Settings</h2>
<h3>Connection Mode: </h3>
<input type="radio" name="bridgeMode" value="bridge" id="radioBridge" onclick="setBridgeMode();">
<label for="radioBridge">Bridge Mode</label>
<br />
<input type="radio" name="bridgeMode" value="onatick" id="radioOnAStick" onclick="setStickMode();">
<label for="radioOnAStick">Single-Interface Mode</label>
<br />
<hr />
<div id="bridgeMode">
<h3>Bridge Mode</h3>
<table class="table">
<tr>
<td><label for="bindUseXdpBridge">Use XDP Enhanced Bridge: </label></td>
<td><input type="checkbox" id="bindUseXdpBridge" /></td>
</tr>
<tr>
<td><label for="bindBridgeToInternet">Network Interface Facing the Internet: </label></td>
<td><select id="bindBridgeToInternet"></select></td>
</tr>
<tr>
<td><label for="bindBridgeToNetwork">Network Interface Facing Your Network: </label></td>
<td><select id="bindBridgeToNetwork"></select></td>
</tr>
</table>
</div>
<div id="OnAStickMode">
<h3>Single-Interface ("On a Stick") Mode</h3>
<table class="table">
<tr>
<td><label for="bindSingleInterfaceNic">Network Interface: </label></td>
<td><select id="bindSingleInterfaceNic"></select></td>
</tr>
<tr>
<td><label for="bindSingleInterfaceInternetVlan">Internet VLAN: </label></td>
<td><input type="number" min="1" max="65535" id="bindSingleInterfaceInternetVlan" /></td>
</tr>
<tr>
<td><label for="bindSingleInterfaceNetworklan">Network VLAN: </label></td>
<td><input type="number" min="1" max="65535" id="bindSingleInterfaceNetworklan" /></td>
</tr>
</table>
</div>
</div>
<!-- Queues Tab -->
<div class="tab-pane fade" id="v-pills-queues" role="tabpanel" aria-labelledby="v-pills-queues-tab">
<h2><i class="fa car"></i> Queue Settings</h2>
<table class="table">
<tr>
<td><label for="bindSqm">SQM Mode: </label></td>
<td>
<select id="bindSqm">
<option value="cake diffserv4">Cake with Diffserv4</option>
<option value="cake diffserv4 ack-filter">Cake with Diffserv4 + Duplicate ACK Filtering</option>
</select>
</td>
</tr>
<tr>
<td><label for="bindMonitorMode">Monitor Only Mode (No Shaping): </label></td>
<td><input type="checkbox" id="bindMonitorMode" /></td>
</tr>
<tr>
<td><label for="bindUplinkMbps">Uplink Bandwidth (Mbps): </label></td>
<td><input type="number" id="bindUplinkMbps" /></td>
</tr>
<tr>
<td><label for="bindDownlinkMbps">Downlink Bandwidth (Mbps): </label></td>
<td><input type="number" id="bindDownlinkMbps" /></td>
</tr>
<tr>
<td><label for="bindGeneratedDownlinkMbps">Generated PN Download Bandwidth (Mbps): </label></td>
<td><input type="number" id="bindGeneratedDownlinkMbps" /></td>
</tr>
<tr>
<td><label for="bindGeneratedUplinkMbps">Generated PN Upload Bandwidth (Mbps): </label></td>
<td><input type="number" id="bindGeneratedUplinkMbps" /></td>
</tr>
<tr>
<td><label for="bindDryRun">Dry Run (Don't Make Queues): </label></td>
<td><input type="checkbox" id="bindDryRun" /></td>
</tr>
<tr>
<td><label for="bindSudo">Use Sudo: </label></td>
<td><input type="checkbox" id="bindSudo" /></td>
</tr>
<tr>
<td><label for="bindBinpack">Use Binpacking to equalize CPU usage: </label></td>
<td><input type="checkbox" id="bindBinpack" /></td>
</tr>
<tr>
<td><label for="bindQueuesAvailableOverride">Queues Available Override: </label></td>
<td><input type="number" id="bindQueuesAvailableOverride" /></td>
</tr>
</table>
</div>
<!-- Long-Term Stats Tab -->
<div class="tab-pane fade" id="v-pills-lts" role="tabpanel" aria-labelledby="v-pills-lts-tab">
<h2><i class="fa fa-line-chart"></i> Long-Term Stats Settings</h2>
<table class="table">
<tr>
<td><label for="bindEnableLTS">Send Statistics to Long-Term Stats: </label></td>
<td><input type="checkbox" id="bindEnableLTS" /></td>
</tr>
<tr>
<td><label for="bindLtsCollation">Collation Period (seconds): </label></td>
<td><input type="number" id="bindLtsCollation" /></td>
</tr>
<tr>
<td><label for="bindLtsLicense">License Key: </label></td>
<td><input type="text" id="bindLtsLicense" style="width: 500px" /></td>
</tr>
<tr>
<td><label for="bindLtsUispInterval">UISP Reporting Interval (Seconds): </label></td>
<td><input type="number" id="bindLtsUispInterval" /></td>
</tr>
</table>
</div>
<!-- IP Ranges Stats Tab -->
<div class="tab-pane fade" id="v-pills-iprange" role="tabpanel" aria-labelledby="v-pills-iprange-tab">
<h2><i class="fa fa-address-card"></i> IP Ranges Settings</h2>
<table>
<tr>
<td><label for="bindIgnoreRanges">IP Subnets to Ignore</label></td>
<td>
<em>Enter one IP range per Line</em><br />
<textarea id="bindIgnoreRanges" rows="10" cols="40"></textarea>
</td>
</tr>
<tr>
<td><label for="bindAllowRanges">IP Ranges to Allow</label></td>
<td>
<em>Enter one IP range per Line</em><br />
<textarea id="bindAllowRanges" rows="10" cols="40"></textarea>
</td>
</tr>
</table>
</div>
<!-- Flows Tab -->
<div class="tab-pane fade" id="v-pills-flows" role="tabpanel" aria-labelledby="v-pills-flow-tab">
<h2><i class="fa fa-arrow-circle-down"></i> Flows Settings</h2>
<table class="table">
<tr>
<td><label for="bindFlowsTimeout">Flow Timeout (Seconds): </label></td>
<td><input type="number" id="bindFlowsTimeout" /></td>
</tr>
<tr>
<td><label for="bindEnableNetflow">Enable Netflow Collection: </label></td>
<td><input type="checkbox" id="bindEnableNetflow" /></td>
</tr>
<tr>
<td><label for="bindFlowsPort">Netflow Port Number: </label></td>
<td><input type="number" id="bindFlowsPort" /></td>
</tr>
<tr>
<td><label for="bindFlowsTarget">Send Netflow to IP: </label></td>
<td><input type="text" id="bindFlowsTarget" /></td>
</tr>
<tr>
<td><label for="bindFlowsVersion">Netflow Version: </label></td>
<td>
<select id="bindFlowsVersion">
<option value="5">Netflow v5 - fast, but IPv4 only</option>
<option value="9">Netflow v6/IPFIX</option>
</select>
</td>
</tr>
</table>
</div>
<!-- Integration Common Tab -->
<div class="tab-pane fade" id="v-pills-icommon" role="tabpanel" aria-labelledby="v-pills-icommon-tab">
<h2><i class="fa fa-link"></i> Common Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindCircuitNameAsAddress">Use Circuit Name as Address: </label></td>
<td><input type="checkbox" id="bindCircuitNameAsAddress" /></td>
</tr>
<tr>
<td><label for="bindOverwriteNetJson">Always Overwrite Network.json: </label></td>
<td><input type="checkbox" id="bindOverwriteNetJson" /></td>
</tr>
<tr>
<td><label for="bindQueueRefreshInterval">Queue Refresh Interval (Minutes): </label></td>
<td><input type="number" id="bindQueueRefreshInterval" /></td>
</tr>
</table>
</div>
<!-- Spylnx Tab -->
<div class="tab-pane fade" id="v-pills-spylnx" role="tabpanel" aria-labelledby="v-pills-spylnx-tab">
<h2><i class="fa fa-link"></i> Spylnx Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindSplynxEnable">Enable Spylnx Integration: </label></td>
<td><input type="checkbox" id="bindSplynxEnable" /></td>
</tr>
<tr>
<td><label for="bindSplynxApiKey">API Key: </label></td>
<td><input type="text" id="bindSplynxApiKey" /></td>
</tr>
<tr>
<td><label for="bindSplynxApiSecret">API Secret: </label></td>
<td><input type="text" id="bindSplynxApiSecret" /></td>
</tr>
<tr>
<td><label for="bindSplynxApiUrl">API URL: </label></td>
<td><input type="text" id="bindSplynxApiUrl" /></td>
</tr>
</table>
</div>
<!-- UISP Tab -->
<div class="tab-pane fade" id="v-pills-uisp" role="tabpanel" aria-labelledby="v-pills-uisp-tab">
<h2><i class="fa fa-link"></i> UISP Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindUispEnable">Enable UISP Integration: </label></td>
<td><input type="checkbox" id="bindUispEnable" /></td>
</tr>
<tr>
<td><label for="bindUispToken">UISP Token: </label></td>
<td><input type="text" id="bindUispToken" /></td>
</tr>
<tr>
<td><label for="bindUispUrl">UISP URL: </label></td>
<td><input type="text" id="bindUispUrl" /></td>
</tr>
<tr>
<td><label for="bindUispSite">Root Site Name (for 'full' integrations): </label></td>
<td><input type="text" id="bindUispSite" /></td>
</tr>
<tr>
<td><label for="bindUispStrategy">Generation Strategy: </label></td>
<td>
<select id="bindUispStrategy">
<option value="flat">Flat: no topology</option>
<option value="full">Full: construct a full hierarchy</option>
</select>
</td>
</tr>
<tr>
<td><label for="bindUispSuspended">Suspended Customer Strategy: </label></td>
<td>
<select id="bindUispSuspended">
<option value="none">Do Nothing</option>
<option value="slow">Apply a Slow Speed</option>
</select>
</td>
</tr>
<tr>
<td><label for="bindUispAirmaxCapacity">Airmax Capacity Multiplier: </label></td>
<td><input type="text" id="bindUispAirmaxCapacity" /></td>
</tr>
<tr>
<td><label for="bindUispLtuCapacity">LTU Capacity Multiplier: </label></td>
<td><input type="text" id="bindUispLtuCapacity" /></td>
</tr>
<tr>
<td><label for="bindUispIpv6Mikrotik">Query Mikrotiks for IPv6 Information: </label></td>
<td><input type="checkbox" id="bindUispIpv6Mikrotik" /></td>
</tr>
<tr>
<td><label for="bindUispOverheadFactor">Bandwidth Overhead Factor: </label></td>
<td><input type="text" id="bindUispOverheadFactor" /></td>
</tr>
<tr>
<td><label for="bindUispCommitMultiplier">Commit Bandwidth Multiplier: </label></td>
<td><input type="text" id="bindUispCommitMultiplier" /></td>
</tr>
<tr>
<td><label for="bindUispExcludeSites">Exclude Sites: </label></td>
<td><input type="text" id="bindUispExcludeSites" /></td>
</tr>
<tr>
<td><label for="bindUispExceptionCpes">Exception CPEs: </label></td>
<td><input type="text" id="bindUispExceptionCpes" /></td>
</tr>
<tr>
<td><label for="bindUispUsePtmpAsParent">Use PTMP As Parent: </label></td>
<td><input type="checkbox" id="bindUispUsePtmpAsParent" /></td>
</tr>
</table>
</div>
<!-- Powercode Tab -->
<div class="tab-pane fade" id="v-pills-powercode" role="tabpanel" aria-labelledby="v-pills-powercode-tab">
<h2><i class="fa fa-link"></i> Powercode Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindPowercodeEnable">Enable Powercode Integration: </label></td>
<td><input type="checkbox" id="bindPowercodeEnable" /></td>
</tr>
<tr>
<td><label for="bindPowercodeKey">Powercode API Key: </label></td>
<td><input type="text" id="bindPowercodeKey" /></td>
</tr>
<tr>
<td><label for="bindPowercodeUrl">Powercode API URL: </label></td>
<td><input type="text" id="bindPowercodeUrl" /></td>
</tr>
</table>
</div>
<!-- Sonar Tab -->
<div class="tab-pane fade" id="v-pills-sonar" role="tabpanel" aria-labelledby="v-pills-sonar-tab">
<h2><i class="fa fa-link"></i> Sonar Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindSonarEnable">Enable Sonar Integration: </label></td>
<td><input type="checkbox" id="bindSonarEnable" /></td>
</tr>
<tr>
<td><label for="bindSonarApiKey">Sonar API Key: </label></td>
<td><input type="text" id="bindSonarApiKey" /></td>
</tr>
<tr>
<td><label for="bindSonarApiUrl">Sonar API URL: </label></td>
<td><input type="text" id="bindSonarApiUrl" /></td>
</tr>
<tr>
<td><label for="bindSonarSnmp">Sonar SNMP Community: </label></td>
<td><input type="text" id="bindSonarSnmp" /></td>
</tr>
<tr>
<td><label for="bindSonarAirmax">Sonar Airmax Model IDs: </label></td>
<td><input type="text" id="bindSonarAirmax" /></td>
</tr>
<tr>
<td><label for="bindSonarLtu">Sonar LTU Model IDs: </label></td>
<td><input type="text" id="bindSonarLtu" /></td>
</tr>
<tr>
<td><label for="bindSonarActive">Sonar Active Status IDs: </label></td>
<td><input type="text" id="bindSonarActive" /></td>
</tr>
</table>
</div>
<!-- InfluxDB Tab -->
<div class="tab-pane fade" id="v-pills-influxdb" role="tabpanel" aria-labelledby="v-pills-influxdb-tab">
<h2><i class="fa fa-link"></i> InfluxDB Integration Settings</h2>
<table class="table">
<tr>
<td><label for="bindInfluxEnable">Enable InfluxDB Integration: </label></td>
<td><input type="checkbox" id="bindInfluxEnable" /></td>
</tr>
<tr>
<td><label for="bindInfluxUrl">InfluxDB URL: </label></td>
<td><input type="text" id="bindInfluxUrl" /></td>
</tr>
<tr>
<td><label for="bindInfluxOrg">InfluxDB Organization: </label></td>
<td><input type="text" id="bindInfluxOrg" /></td>
</tr>
<tr>
<td><label for="bindInfluxBucket">InfluxDB Bucket: </label></td>
<td><input type="text" id="bindInfluxBucket" /></td>
</tr>
<tr>
<td><label for="bindInfluxToken">InfluxDB Token: </label></td>
<td><input type="text" id="bindInfluxToken" /></td>
</tr>
</table>
</div>
<!-- Network Layout/JSON Tab -->
<div class="tab-pane fade" id="v-pills-netjson" role="tabpanel" aria-labelledby="v-pills-netjson-tab">
<h2><i class="fa fa-map"></i> Network.Json - Network Layout</h2>
<div id="netjson"></div>
</div>
<!-- ShapedDevices.csv Tab -->
<div class="tab-pane fade" id="v-pills-shapeddevs" role="tabpanel" aria-labelledby="v-pills-shapeddevs-tab">
<h2><i class="fa table"></i> Shaped Devices</h2>
<p>All Shaped Devices. Note that if you have an integration enabled, this will be overwritten periodically.</p>
<div id="shapedDeviceTable"></div>
</div>
<div class="tab-pane fade" id="v-pills-users" role="tabpanel" aria-labelledby="v-pills-users-tab">
<h2><i class="fa fa-users"></i> LibreQos Web Interface Users</h2>
<div id="userManager"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-2"></div>
</div>
</div>
<footer>&copy; 2022-2023, LibreQoE LLC</footer>
<script>
let nics = null;
let lqosd_config = null;
let shaped_devices = null;
let network_json = null;
const bindings = [
// General
{ field: "bindVersion", path: ".version", data: "string", editable: false },
{ field: "bindPath", path: ".lqos_directory", data: "string", editable: true },
{ field: "bindNodeId", path: ".node_id", data: "string", editable: true },
{ field: "bindNodeName", path: ".node_name", data: "string", editable: true },
{ field: "bindPacketCaptureTime", path: ".packet_capture_time", data: "integer", editable: true },
{ field: "bindQueueCheckPeriodMs", path: ".queue_check_period_ms", data: "integer", editable: true },
// Anonymous Usage
{ field: "bindSendAnonymous", path: ".usage_stats.send_anonymous", data: "bool", editable: true },
{ field: "bindAnonymousServer", path: ".usage_stats.anonymous_server", data: "string", editable: true },
// Tuning
{ field: "bindStopIrqBalance", path: ".tuning.stop_irq_balance", data: "bool", editable: true },
{ field: "bindNetdevBudgetUs", path: ".tuning.netdev_budget_usecs", data: "integer", editable: true },
{ field: "bindNetdevBudgetPackets", path: ".tuning.netdev_budget_packets", data: "integer", editable: true },
{ field: "bindRxUs", path: ".tuning.rx_usecs", data: "integer", editable: true },
{ field: "bindTxUs", path: ".tuning.tx_usecs", data: "integer", editable: true },
{ field: "bindDisableRxVlan", path: ".tuning.disable_rxvlan", data: "bool", editable: true },
{ field: "bindDisableTxVlan", path: ".tuning.disable_txvlan", data: "bool", editable: true },
{ field: "bindDisableOffload", path: ".tuning.disable_offload", data: "array_of_strings", editable: true },
// Bridge/Stick
{ conditional: true, condition: "if_exists", path: ".bridge", showDiv: "bridgeMode", hideDiv: "OnAStickMode" },
{ field: "bindUseXdpBridge", path: ".bridge.use_xdp_bridge", data: "bool", editable: true },
{ field: "bindBridgeToInternet", path: ".bridge.to_internet", data: "interface", editable: true },
{ field: "bindBridgeToNetwork", path: ".bridge.to_network", data: "interface", editable: true },
{ end_conditional: true },
{ conditional: true, condition: "if_exists", path: ".single_interface", showDiv: "OnAStickMode", hideDiv: "bridgeMode" },
{ field: "bindSingleInterfaceNic", path: ".single_interface.interface", data: "interface", editable: true },
{ field: "bindSingleInterfaceInternetVlan", path: ".single_interface.internet_vlan", data: "integer", editable: true },
{ field: "bindSingleInterfaceNetworklan", path: ".single_interface.network_vlan", data: "integer", editable: true },
{ end_conditional: true },
// Queues
{ field: "bindSqm", path: ".queues.default_sqm", data: "select-premade", editable: true },
{ field: "bindMonitorMode", path: ".queues.monitor_only", data: "bool", editable: true },
{ field: "bindUplinkMbps", path: ".queues.uplink_bandwidth_mbps", data: "integer", editable: true },
{ field: "bindDownlinkMbps", path: ".queues.downlink_bandwidth_mbps", data: "integer", editable: true },
{ field: "bindGeneratedDownlinkMbps", path: ".queues.generated_pn_download_mbps", data: "integer", editable: true },
{ field: "bindGeneratedUplinkMbps", path: ".queues.generated_pn_upload_mbps", data: "integer", editable: true },
{ field: "bindDryRun", path: ".queues.dry_run", data: "bool", editable: true },
{ field: "bindSudo", path: ".queues.sudo", data: "bool", editable: true },
{ field: "bindBinpack", path: ".queues.use_binpacking", data: "bool", editable: true },
{ field: "bindQueuesAvailableOverride", path: ".queues.override_available_queues", data: "integer", editable: true },
// LTS
{ field: "bindEnableLTS", path: ".long_term_stats.gather_stats", data: "bool", editable: true },
{ field: "bindLtsCollation", path: ".long_term_stats.collation_period_seconds", data: "integer", editable: true },
{ field: "bindLtsLicense", path: ".long_term_stats.license_key", data: "string", editable: true },
{ field: "bindLtsUispInterval", path: ".long_term_stats.uisp_reporting_interval_seconds", data: "integer", editable: true },
// IP Ranges
{ field: "bindIgnoreRanges", path: ".ip_ranges.ignore_subnets", data: "ip_array", editable: true },
{ field: "bindAllowRanges", path: ".ip_ranges.allow_subnets", data: "ip_array", editable: true },
// Flow tracking
{ field: "bindFlowsTimeout", path: ".flows.flow_timeout_seconds", data: "integer", editable: true },
{ field: "bindEnableNetflow", path: ".flows.netflow_enabled", data: "bool", editable: true },
{ field: "bindFlowsPort", path: ".flows.netflow_port", data: "integer", editable: true },
{ field: "bindFlowsTarget", path: ".flows.netflow_ip", data: "string", editable: true },
{ field: "bindFlowsVersion", path: ".flows.netflow_ip", data: "select-premade", editable: true },
// Integration Common
{ field: "bindCircuitNameAsAddress", path: ".integration_common.circuit_name_as_address", data: "bool", editable: true },
{ field: "bindOverwriteNetJson", path: ".integration_common.always_overwrite_network_json", data: "bool", editable: true },
{ field: "bindQueueRefreshInterval", path: ".integration_common.queue_refresh_interval_mins", data: "integer", editable: true },
// Splynx
{ field: "bindSplynxEnable", path: ".spylnx_integration.enable_spylnx", data: "bool", editable: true },
{ field: "bindSplynxApiKey", path: ".spylnx_integration.api_key", data: "string", editable: true },
{ field: "bindSplynxApiSecret", path: ".spylnx_integration.api_secret", data: "string", editable: true },
{ field: "bindSplynxApiUrl", path: ".spylnx_integration.url", data: "string", editable: true },
// UISP
{ field: "bindUispEnable", path: ".uisp_integration.enable_uisp", data: "bool", editable: true },
{ field: "bindUispToken", path: ".uisp_integration.token", data: "string", editable: true },
{ field: "bindUispUrl", path: ".uisp_integration.url", data: "string", editable: true },
{ field: "bindUispSite", path: ".uisp_integration.site", data: "string", editable: true },
{ field: "bindUispStrategy", path: ".uisp_integration.strategy", data: "select-premade", editable: true },
{ field: "bindUispSuspended", path: ".uisp_integration.suspended_strategy", data: "select-premade", editable: true },
{ field: "bindUispAirmaxCapacity", path: ".uisp_integration.airmax_capacity", data: "float", editable: true },
{ field: "bindUispLtuCapacity", path: ".uisp_integration.ltu_capacity", data: "float", editable: true },
{ field: "bindUispIpv6Mikrotik", path: ".uisp_integration.ipv6_with_mikrotik", data: "bool", editable: true },
{ field: "bindUispOverheadFactor", path: ".uisp_integration.bandwidth_overhead_factor", data: "float", editable: true },
{ field: "bindUispCommitMultiplier", path: ".uisp_integration.commit_bandwidth_multiplier", data: "float", editable: true },
{ field: "bindUispExcludeSites", path: ".uisp_integration.exclude_sites", data: "array_of_strings", editable: true },
{ field: "bindUispExceptionCpes", path: ".uisp_integration.exception_cpes", data: "array_of_strings", editable: true },
{ field: "bindUispUsePtmpAsParent", path: ".uisp_integration.use_ptmp_as_parent", data: "bool", editable: true },
// Powercode
{ field: "bindPowercodeEnable", path: ".powercode_integration.enable_powercode", data: "bool", editable: true },
{ field: "bindPowercodeKey", path: ".powercode_integration.powercode_api_key", data: "string", editable: true },
{ field: "bindPowercodeUrl", path: ".powercode_integration.powercode_api_url", data: "string", editable: true },
// Sonar
{ field: "bindSonarEnable", path: ".sonar_integration.enable_sonar", data: "bool", editable: true },
{ field: "bindSonarApiKey", path: ".sonar_integration.sonar_api_key", data: "string", editable: true },
{ field: "bindSonarApiUrl", path: ".sonar_integration.sonar_api_url", data: "string", editable: true },
{ field: "bindSonarSnmp", path: ".sonar_integration.snmp_community", data: "string", editable: true },
{ field: "bindSonarAirmax", path: ".sonar_integration.airmax_model_ids", data: "array_of_strings", editable: true },
{ field: "bindSonarLtu", path: ".sonar_integration.ltu_model_ids", data: "array_of_strings", editable: true },
{ field: "bindSonarActive", path: ".sonar_integration.active_status_ids", data: "array_of_strings", editable: true },
// Influx
{ field: "bindInfluxEnable", path: ".influxdb.enable_influxdb", data: "bool", editable: true },
{ field: "bindInfluxUrl", path: ".influxdb.url", data: "string", editable: true },
{ field: "bindInfluxOrg", path: ".influxdb.org", data: "string", editable: true },
{ field: "bindInfluxBucket", path: ".influxdb.bucket", data: "string", editable: true },
{ field: "bindInfluxToken", path: ".influxdb.token", data: "string", editable: true },
];
function doBindings() {
let active = true;
for (var i=0; i<bindings.length; ++i) {
let entry = bindings[i];
if (entry.conditional != null) {
console.log("Conditional encountered");
if (entry.condition === "if_exists") {
let val = "lqosd_config" + entry.path;
if (eval(val) != null) {
console.log("Conditional fired");
active = true;
if (entry.showDiv != null) {
$("#" + entry.showDiv).show();
}
if (entry.hideDiv != null) {
$("#" + entry.hideDiv).hide();
}
} else {
console.log("Conditional did not fire")
active = false;
}
}
} else if (entry.end_conditional != null) {
console.log("Conditional ended");
active = true;
} else {
if (!active) {
console.log("Skipping " + entry.path);
continue;
}
let value_location = "lqosd_config" + entry.path;
let value = eval(value_location);
let controlId = "#" + entry.field;
if (entry.data === "bool") {
$(controlId).prop('checked', value);
} else if (entry.data === "selector-premade") {
$(controlId + " select").val(value);
} else if (entry.data === "array_of_strings") {
let expanded = "";
let length = eval("lqosd_config" + entry.path + ".length");
for (let j = 0; j < length; j++) {
let v = eval("lqosd_config" + entry.path + "[" + j + "]");
expanded += v + " ";
}
expanded = expanded.trimEnd();
$(controlId).val(expanded);
} else if (entry.data === "ip_array") {
let expanded = "";
let length = eval("lqosd_config" + entry.path + ".length");
for (let j = 0; j < length; j++) {
let v = eval("lqosd_config" + entry.path + "[" + j + "]");
expanded += v + "\n";
}
expanded = expanded.trimEnd();
$(controlId).val(expanded);
} else if (entry.data === "interface") {
console.log("NIC: " + entry.field);
fillNicList(entry.field, value);
} else {
$(controlId).val(value);
}
$(controlId).prop('readonly', !entry.editable);
}
}
}
function setBridgeMode() {
$("#bridgeMode").show();
$("#OnAStickMode").hide();
lqosd_config.single_interface = null;
lqosd_config.bridge = {
use_xdp_bridge: true,
to_internet: "",
to_network: "",
};
$("#bindUseXdpBridge").prop('checked', true);
fillNicList("bindBridgeToInternet", nics[0][0]);
fillNicList("bindBridgeToNetwork", nics[0][0]);
}
function setStickMode() {
$("#bridgeMode").hide();
$("#OnAStickMode").show();
lqosd_config.bridge = null;
lqosd_config.single_interface = {
interface: "",
internet_vlan: 2,
network_vlan: 3,
};
fillNicList("bindSingleInterfaceNic", nics[0][0]);
$("#bindSingleInterfaceInternetVlan").val('2');
$("#bindSingleInterfaceNetworklan").val('3');
}
function validateConfig() {
for (var i=0; i<bindings.length; ++i) {
let entry = bindings[i];
let controlId = entry.field;
if (entry.path != null) {
let path = "lqosd_config" + entry.path;
let currentValue = $("#" + controlId).val();
let storedVAlue = eval(path);
console.log(currentValue, storedVAlue);
}
}
}
function start() {
display();
colorReloadButton();
updateHostCounts();
$.get("/api/admin_check", (is_admin) => {
if (!is_admin) {
$("#controls").html("<p class='alert alert-danger' role='alert'>You have to be an administrative user to change configuration.");
$("#userManager").html("<p class='alert alert-danger' role='alert'>Only administrators can see/change user information.");
} else {
// Handle Saving ispConfig.py
$("#btnSaveIspConfig").on('click', (data) => {
validateConfig();
});
}
$.get("/api/config", (data) => {
lqosd_config = data;
console.log("Bindings Done");
$.get("/api/list_nics", (data) => {
nics = data;
console.log(lqosd_config);
doBindings();
// User management
if (is_admin) {
userManager();
}
$.get("/api/network_json", (njs) => {
network_json = njs;
$.get("/api/all_shaped_devices", (data) => {
shaped_devices = data;
shapedDevices(data);
RenderNetworkJson();
});
});
});
});
});
}
function iterateNetJson(level, depth) {
let html = "<div style='margin-left: " + depth * 30 + "px;'>";
for (const [key, value] of Object.entries(level)) {
html += "<div>";
html += "<strong>" + key + "</strong><br />";
html += "Download: <input type='number' value='" + value.downloadBandwidthMbps + "'></input><br />";
html += "Upload: <input type='number' value='" + value.uploadBandwidthMbps + "'></input><br />";
let num_children = 0;
for (let i=0; i<shaped_devices.length; i++) {
if (shaped_devices[i].parent_node === key) {
num_children++;
}
}
html += "<em>Associated Devices: " + num_children + "</em><br />";
//console.log(`${key}: ${value}`);
//console.log("Children", value.children);
if (value.children != null) {
html += iterateNetJson(value.children, depth + 1);
}
html += "</div>";
}
html += "</div>";
return html;
}
function RenderNetworkJson() {
let html = "";
html += iterateNetJson(network_json, 0);
$("#netjson").html(html);
}
function makeSheetBox(value, small=false) {
let html = "";
if (!small) {
html = "<td style='padding: 0px'><input type=\"text\" value=\"" + value + "\"></input></td>"
} else {
html = "<td style='padding: 0px'><input type=\"text\" value=\"" + value + "\" style='font-size: 8pt;'></input></td>"
}
return html;
}
function makeSheetNumberBox(value) {
let html = "<td style='padding: 0px'><input type=\"number\" value=\"" + value + "\" style='width: 100px; font-size: 8pt;'></input></td>"
return html;
}
function separatedIpArray(value) {
let html = "<td style='padding: 0px'>";
let val = "";
for (i = 0; i<value.length; i++) {
val += value[i][0];
val += "/";
val += value[i][1];
val += ", ";
}
if (val.length > 0) {
val = val.substring(0, val.length-2);
}
html += "<input type='text' style='font-size: 8pt; width: 100px;' value='" + val + "'></input>";
html += "</td>";
return html;
}
function nodeDropDown(selectedNode) {
let html = "<td style='padding: 0px'>";
html += "<select style='font-size: 8pt; width: 150px;'>";
function iterate(data, level) {
let html = "";
for (const [key, value] of Object.entries(data)) {
html += "<option value='" + key + "'";
if (key === selectedNode) html += " selected";
html += ">";
for (let i=0; i<level; i++) html += "-";
html += key;
html += "</option>";
if (value.children != null)
html += iterate(value.children, level+1);
}
return html;
}
html += iterate(network_json, 0);
html += "</select>";
html += "</td>";
return html;
}
function shapedDevices() {
let html = "<table style='height: 500px; overflow: scroll; border-collapse: collapse; width: 100%; padding: 0px'>";
html += "<thead style='position: sticky; top: 0; height: 50px; background: #ddd'>";
html += "<tr style='font-size: 9pt;'><th>Circuit ID</th><th>Circuit Name</th><th>Device ID</th><th>Device Name</th><th>Parent Node</th><th>MAC</th><th>IPv4</th><th>IPv6</th><th>Download Min</th><th>Upload Min</th><th>Download Max</th><th>Upload Max</th><th>Comment</th></tr>";
html += "</thead>>";
for (var i=0; i<shaped_devices.length; i++) {
let row = shaped_devices[i];
html += "<tr>";
html += makeSheetBox(row.circuit_id, true);
html += makeSheetBox(row.circuit_name, true);
html += makeSheetBox(row.device_id, true);
html += makeSheetBox(row.device_name, true);
html += nodeDropDown(row.parent_node, true);
html += makeSheetBox(row.mac, true);
html += separatedIpArray(row.ipv4);
html += separatedIpArray(row.ipv6);
html += makeSheetNumberBox(row.download_min_mbps);
html += makeSheetNumberBox(row.upload_min_mbps);
html += makeSheetNumberBox(row.download_max_mbps);
html += makeSheetNumberBox(row.upload_max_mbps);
html += makeSheetBox(row.comment, true);
html += "</tr>";
}
html += "</tbody></table>";
$("#shapedDeviceTable").html(html);
}
function userManager() {
let html = "<p>For now, please use <em>bin/lqusers</em> to manage users.</p>";
$("#userManager").html(html);
}
function fillNicList(id, selected) {
let select = $("#" + id);
let html = "";
for (i=0; i<nics.length; i++) {
html += "<option value=\"";
html += nics[i][0] + "\"";
if (nics[i][0] === selected) {
html += " selected";
}
html += ">" + nics[i][0] + " - " + nics[i][1] + " - " + nics[i][2] + "</option>";
}
select.html(html);
}
function buildNICList(id, selected, disabled=false) {
let html = "<select id='" + id + "'";
if (disabled) html += " disabled='true' ";
html += ">";
for (i=0; i<nics.length; i++) {
html += "<option value=\"";
html += nics[i][0] + "\"";
if (nics[i][0] == selected) {
html += " selected";
}
html += ">" + nics[i][0] + " - " + nics[i][1] + " - " + nics[i][2] + "</option>";
}
html += "</select>";
return html;
}
function display() {
let colorPreference = window.localStorage.getItem("colorPreference");
if (colorPreference == null) {
window.localStorage.setItem("colorPreference", 0);
colorPreference = 0;
}
$("#colorMode option[id='" + colorPreference + "']").attr("selected", true);
let redact = window.localStorage.getItem("redact");
if (redact == null) {
window.localStorage.setItem("redact", false);
redact = false;
}
if (redact == "false") redact = false;
$("#redact").prop('checked', redact);
$("#applyDisplay").on('click', () => {
let colorPreference = $("#colorMode").find('option:selected').attr('id');
window.localStorage.setItem("colorPreference", colorPreference);
let redact = $("#redact").prop('checked');
window.localStorage.setItem("redact", redact);
});
}
$(document).ready(start);
</script>
</body>
</html>