Eerste commit

This commit is contained in:
Blackwhitebear8 2025-06-09 20:56:28 +02:00
parent 534389b6f4
commit 9928321826
22 changed files with 1594 additions and 0 deletions

187
templates/arp.html Normal file
View file

@ -0,0 +1,187 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | ARP table</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="arp">
<div class="input-group mb-3">
<input type="text" id="arpSearch" class="form-control" placeholder="Search in ARP table..." onkeyup="filterTable('arpSearch', 'arpTable')">
<button id="refreshButton" class="btn btn-outline-primary" type="button" onclick="refreshArpTable()">
<span id="refreshIcon"></span>
<span id="refreshSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
Refresh data
</button>
</div>
<table class="striped table table-bordered" id="arpTable">
<thead>
<tr>
<th class="sortable" onclick="sortTable('arpTable', 0, this)">Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('arpTable', 1, this)">Interface <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('arpTable', 2, this)">Link Layer Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('arpTable', 3, this)">State <span class="sort-arrow"></span></th>
</tr>
</thead>
<tbody id="arpTableBody">
<tr id="arp-loading-row">
<td colspan="4" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
</tbody>
</table>
<p><a href="/arp/json">JSON version</a></p>
</section>
</main>
<footer>
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script> document.getElementById("year").textContent = new Date().getFullYear(); </script>
</footer>
<script src="../static/js/materialize.min.js"></script>
<script>
function filterTable(searchInputId, tableId) {
const input = document.getElementById(searchInputId);
const filter = input.value.toUpperCase();
const table = document.getElementById(tableId);
const tr = table.getElementsByTagName("tr");
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "none";
const td = tr[i].getElementsByTagName("td");
for (let j = 0; j < td.length; j++) {
if (td[j]) {
const txtValue = td[j].textContent || td[j].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
break;
}
}
}
}
}
function sortTable(tableId, columnIndex, th) {
const table = document.getElementById(tableId);
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
const headers = Array.from(th.parentNode.children);
headers.forEach(header => {
if (header !== th) {
header.classList.remove('asc', 'desc');
header.setAttribute('data-sort-state', 'none');
}
});
let currentState = th.getAttribute('data-sort-state') || 'none';
let newState = currentState === 'none' ? 'asc' : currentState === 'asc' ? 'desc' : 'none';
th.classList.remove('asc', 'desc');
if (newState !== 'none') th.classList.add(newState);
th.setAttribute('data-sort-state', newState);
if (newState === 'none') {
rows.sort((a, b) => parseInt(a.getAttribute('data-index')) - parseInt(b.getAttribute('data-index')));
} else {
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent.trim();
const bText = b.cells[columnIndex].textContent.trim();
const aNum = parseFloat(aText.replace(/[^0-9.-]/g, ''));
const bNum = parseFloat(bText.replace(/[^0-9.-]/g, ''));
const isNumeric = !isNaN(aNum) && !isNaN(bNum);
return newState === 'asc'
? (isNumeric ? aNum - bNum : aText.localeCompare(bText))
: (isNumeric ? bNum - aNum : bText.localeCompare(aText));
});
}
rows.forEach(row => tbody.appendChild(row));
}
async function loadArpTable() {
const tableBody = document.getElementById("arpTableBody");
try {
const response = await fetch("/arp/json");
const data = await response.json();
tableBody.innerHTML = "";
if (!data.arp_table || data.arp_table.length === 0) {
tableBody.innerHTML = "<tr><td colspan='4' class='text-center'>Geen data beschikbaar.</td></tr>";
return;
}
data.arp_table.forEach((entry, index) => {
const row = document.createElement("tr");
row.setAttribute("data-index", index);
row.innerHTML = `
<td>${entry.address}</td>
<td>${entry.interface}</td>
<td>${entry.link_layer_address}</td>
<td>${entry.state}</td>
`;
tableBody.appendChild(row);
});
} catch (err) {
tableBody.innerHTML = "<tr><td colspan='4' class='text-center text-danger'>Fout bij ophalen van data.</td></tr>";
}
}
window.addEventListener("DOMContentLoaded", () => {
loadArpTable();
});
function refreshArpTable() {
const searchInput = document.getElementById("arpSearch");
const refreshIcon = document.getElementById("refreshIcon");
const refreshSpinner = document.getElementById("refreshSpinner");
searchInput.value = ""; // Leegmaken van het zoekveld
refreshIcon.classList.add("d-none");
refreshSpinner.classList.remove("d-none");
loadArpTable().then(() => {
refreshIcon.classList.remove("d-none");
refreshSpinner.classList.add("d-none");
});
}
</script>
</body>
</html>

156
templates/bgp-route.html Normal file
View file

@ -0,0 +1,156 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | BGP route lookup</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="bgp-route-lookup">
<div class="input-group mb-3">
<input
type="text"
id="prefixInput"
class="form-control"
placeholder="Type the prefix here, for example, 2606:4700:4700::/48 or 1.1.1.0/24"
aria-label="Enter a prefix"
onkeydown="if(event.key === 'Enter') loadBGPRoute()"
/>
<button class="btn btn-outline-primary" type="button" onclick="loadBGPRoute()">Lookup</button>
</div>
<pre id="bgpOutput" aria-label="BGP Route Lookup output">Enter a prefix and click Lookup.</pre>
</section>
</main>
<footer class="text-center py-3">
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script>document.getElementById("year").textContent = new Date().getFullYear();</script>
</footer>
<script src="../static/js/materialize.min.js"></script>
<script>
function detectIpVersion(ip) {
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}(\/\d{1,2})?$/;
const ipv6Regex = /^([0-9a-fA-F:]+)(\/\d{1,3})?$/;
if (ipv4Regex.test(ip)) {
return "ipv4";
} else if (ipv6Regex.test(ip)) {
return "ipv6";
} else {
return null;
}
}
function highlightBGPOutput(text) {
text = text.replace(/\b215085\b/g, '<span class="asn215085-highlight">215085</span>');
text = text.replace(/\b\d{4,6}\b/g, match => {
if (match === '215085') return match;
return `<span class="asn-highlight">${match}</span>`;
});
text = text.split('\n').map(line => {
if (line.toLowerCase().includes('best')) {
return `<strong class="best-line">${line}</strong>`;
}
return line;
}).join('\n');
text = text.split('\n').map(line => {
if (line.toLowerCase().includes('table entry')) {
return `<strong class="best-line">${line}</strong>`;
}
return line;
}).join('\n');
text = text.split('\n').map(line => {
if (line.toLowerCase().includes('multipath')) {
return `<strong class="best-line">${line}</strong>`;
}
return line;
}).join('\n');
return text.replace(/\n/g, '<br>');
}
function displayBGPRoute(data) {
const outputElem = document.getElementById("bgpOutput");
const rawText = data.data || JSON.stringify(data, null, 2);
const highlighted = highlightBGPOutput(rawText);
outputElem.innerHTML = highlighted;
}
async function loadBGPRoute() {
const outputElem = document.getElementById("bgpOutput");
const prefix = document.getElementById("prefixInput").value.trim();
if (!prefix) {
outputElem.textContent = "Voer eerst een geldig prefix in.";
return;
}
const ip_version = detectIpVersion(prefix);
if (!ip_version) {
outputElem.textContent = "Ongeldig IP-adres of prefix.";
return;
}
outputElem.textContent = "Loading...";
const postData = {
ip_version: ip_version,
bgprouteprefix: prefix
};
try {
const response = await fetch("/bgp-route/lookup", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
if (!response.ok) throw new Error("Network response was not ok");
const data = await response.json();
if (data.error) {
outputElem.textContent = "Error: " + data.error;
return;
}
displayBGPRoute(data);
} catch (error) {
outputElem.textContent = "Fout bij ophalen van data: " + error.message;
}
}
</script>
</body>
</html>

297
templates/bgp.html Normal file
View file

@ -0,0 +1,297 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | BGP table</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="bgp-v4">
<h4>IPv4 Unicast Summary</h4>
<p id="ipv4Summary">Loading summary...</p>
<div class="input-group mb-3">
<input type="text" id="ipv4Search" class="form-control" placeholder="Search in IPv4 table..." onkeyup="filterTable('ipv4Search', 'ipv4Table')">
<button id="refreshButton" class="btn btn-outline-primary" type="button" onclick="refreshBGPTable()">
<span id="refreshIcon"></span>
<span id="refreshSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
Refresh data
</button>
</div>
<table class="striped" id="ipv4Table">
<thead>
<tr>
<th class="sortable" onclick="sortTable('ipv4Table', 0, this)">Neighbor <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 1, this)">Version <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 2, this)">AS Number <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 3, this)">Messages Received <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 4, this)">Messages Sent <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 5, this)">Table Version <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 6, this)">Inbound Queue <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 7, this)">Outbound Queue <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 8, this)">Up/Down <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 9, this)">State/PfxRcd <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 10, this)">Prefix Sent <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv4Table', 11, this)">Description <span class="sort-arrow"></span></th>
</tr>
</thead>
<tbody id="ipv4TableBody">
<tr>
<td colspan="12" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
</tbody>
</table>
<p><a href="/bgp/json">JSON version</a></p>
</section>
<section id="bgp-v6">
<h4>IPv6 Unicast Summary</h4>
<p id="ipv6Summary">Loading summary...</p>
<div class="input-group mb-3">
<input type="text" id="ipv6Search" class="form-control" placeholder="Search in IPv6 table..." onkeyup="filterTable('ipv6Search', 'ipv6Table')">
<button id="refreshButton" class="btn btn-outline-primary" type="button" onclick="refreshBGPTable()">
<span id="refreshIcon2"></span>
<span id="refreshSpinner2" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
Refresh data
</button>
</div>
<table class="striped" id="ipv6Table">
<thead>
<tr>
<th class="sortable" onclick="sortTable('ipv6Table', 0, this)">Neighbor <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 1, this)">Version <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 2, this)">AS Number <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 3, this)">Messages Received <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 4, this)">Messages Sent <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 5, this)">Table Version <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 6, this)">Inbound Queue <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 7, this)">Outbound Queue <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 8, this)">Up/Down <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 9, this)">State/PfxRcd <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 10, this)">Prefix Sent <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('ipv6Table', 11, this)">Description <span class="sort-arrow"></span></th>
</tr>
</thead>
<tbody id="ipv6TableBody">
<tr>
<td colspan="12" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
</tbody>
</table>
<p><a href="/bgp/json">JSON version</a></p>
</section>
</main>
<footer>
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script> document.getElementById("year").textContent = new Date().getFullYear(); </script>
</footer>
<script src="../static/js/materialize.min.js"></script>
<script>
function filterTable(searchInputId, tableId) {
const input = document.getElementById(searchInputId);
const filter = input.value.toUpperCase();
const table = document.getElementById(tableId);
const tr = table.getElementsByTagName("tr");
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "none";
const td = tr[i].getElementsByTagName("td");
for (let j = 0; j < td.length; j++) {
if (td[j]) {
const txtValue = td[j].textContent || td[j].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
break;
}
}
}
}
}
function sortTable(tableId, columnIndex, th) {
const table = document.getElementById(tableId);
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
const headers = Array.from(th.parentNode.children);
headers.forEach(header => {
if (header !== th) {
header.classList.remove('asc', 'desc');
header.setAttribute('data-sort-state', 'none');
}
});
let currentState = th.getAttribute('data-sort-state') || 'none';
let newState = currentState === 'none' ? 'asc' : currentState === 'asc' ? 'desc' : 'none';
th.classList.remove('asc', 'desc');
if (newState !== 'none') th.classList.add(newState);
th.setAttribute('data-sort-state', newState);
if (newState === 'none') {
rows.sort((a, b) => parseInt(a.getAttribute('data-index')) - parseInt(b.getAttribute('data-index')));
} else {
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent.trim();
const bText = b.cells[columnIndex].textContent.trim();
const aNum = parseFloat(aText.replace(/[^0-9.-]/g, ''));
const bNum = parseFloat(bText.replace(/[^0-9.-]/g, ''));
const isNumeric = !isNaN(aNum) && !isNaN(bNum);
return newState === 'asc'
? (isNumeric ? aNum - bNum : aText.localeCompare(bText))
: (isNumeric ? bNum - aNum : bText.localeCompare(aText));
});
}
rows.forEach(row => tbody.appendChild(row));
}
async function loadBgpTables() {
const ipv4SummaryEl = document.getElementById("ipv4Summary");
const ipv6SummaryEl = document.getElementById("ipv6Summary");
const ipv4Body = document.getElementById("ipv4TableBody");
const ipv6Body = document.getElementById("ipv6TableBody");
try {
const response = await fetch("/bgp/json");
const data = await response.json();
ipv4SummaryEl.innerHTML = `
<p>BGP Router ID: ${data.ipv4_info.router_id}, Local AS Number: ${data.ipv4_info.local_as} VRF ID: ${data.ipv4_info.vrf_id}</p>
<p>BGP Table Version: ${data.ipv4_info.table_version}</p>
<p>RIB Entries: ${data.ipv4_info.rib_entries}, using ${data.ipv4_info.rib_memory}</p>
<p>Peers: ${data.ipv4_info.peers}, using ${data.ipv4_info.peers_memory}</p>
`;
ipv6SummaryEl.innerHTML = `
<p>BGP Router ID: ${data.ipv6_info.router_id}, Local AS Number: ${data.ipv6_info.local_as} VRF ID: ${data.ipv6_info.vrf_id}</p>
<p>BGP Table Version: ${data.ipv6_info.table_version}</p>
<p>RIB Entries: ${data.ipv6_info.rib_entries}, using ${data.ipv6_info.rib_memory}</p>
<p>Peers: ${data.ipv6_info.peers}, using ${data.ipv6_info.peers_memory}</p>
`;
ipv4Body.innerHTML = "";
if (data.ipv4_peers.length === 0) {
ipv4Body.innerHTML = `<tr><td colspan="12" class="text-center">Geen data beschikbaar.</td></tr>`;
} else {
data.ipv4_peers.forEach((peer, index) => {
const row = document.createElement("tr");
row.setAttribute("data-index", index);
row.innerHTML = `
<td>${peer.neighbor}</td>
<td>${peer.version}</td>
<td>${peer.as_number}</td>
<td>${peer.msg_received}</td>
<td>${peer.msg_sent}</td>
<td>${peer.table_version}</td>
<td>${peer.in_queue}</td>
<td>${peer.out_queue}</td>
<td>${peer.up_down}</td>
<td>${peer.state_pfx_rcd}</td>
<td>${peer.prefix_sent}</td>
<td>${peer.description}</td>
`;
ipv4Body.appendChild(row);
});
}
ipv6Body.innerHTML = "";
if (data.ipv6_peers.length === 0) {
ipv6Body.innerHTML = `<tr><td colspan="12" class="text-center">Geen data beschikbaar.</td></tr>`;
} else {
data.ipv6_peers.forEach((peer, index) => {
const row = document.createElement("tr");
row.setAttribute("data-index", index);
row.innerHTML = `
<td>${peer.neighbor}</td>
<td>${peer.version}</td>
<td>${peer.as_number}</td>
<td>${peer.msg_received}</td>
<td>${peer.msg_sent}</td>
<td>${peer.table_version}</td>
<td>${peer.in_queue}</td>
<td>${peer.out_queue}</td>
<td>${peer.up_down}</td>
<td>${peer.state_pfx_rcd}</td>
<td>${peer.prefix_sent}</td>
<td>${peer.description}</td>
`;
ipv6Body.appendChild(row);
});
}
} catch (error) {
ipv4SummaryEl.textContent = "Fout bij ophalen IPv4 data.";
ipv6SummaryEl.textContent = "Fout bij ophalen IPv6 data.";
ipv4Body.innerHTML = `<tr><td colspan="12" class="text-center text-danger">Fout bij ophalen van data.</td></tr>`;
ipv6Body.innerHTML = `<tr><td colspan="12" class="text-center text-danger">Fout bij ophalen van data.</td></tr>`;
}
}
window.addEventListener("DOMContentLoaded", () => {
loadBgpTables();
});
function refreshBGPTable() {
const searchInput = document.getElementById("ipv4Search");
const searchInput2 = document.getElementById("ipv6Search");
const refreshIcon = document.getElementById("refreshIcon");
const refreshIcon2 = document.getElementById("refreshIcon2");
const refreshSpinner = document.getElementById("refreshSpinner");
const refreshSpinner2 = document.getElementById("refreshSpinner2");
searchInput.value = ""; // Leegmaken van het zoekveld
searchInput2.value = ""; // Leegmaken van het zoekveld
refreshIcon.classList.add("d-none");
refreshIcon2.classList.add("d-none");
refreshSpinner.classList.remove("d-none");
refreshSpinner2.classList.remove("d-none");
loadBgpTables().then(() => {
refreshIcon.classList.remove("d-none");
refreshIcon2.classList.remove("d-none");
refreshSpinner.classList.add("d-none");
refreshSpinner2.classList.add("d-none");
});
}
</script>
</body>
</html>

64
templates/index.html Normal file
View file

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | Home</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="overview">
<h2>Welcome</h2>
<p>Welcome to the <b>AS215085 Network tools</b></p>
<p>On this tool you cn get in depth insight into our network</p>
<p>In case of emergency you can contact us at: noc AT pixelhosting DOT nl</p>
<p>PeeringDB: <a href="https://www.peeringdb.com/net/35968" target="_blank" style="color: #07AAF9;">https://www.peeringdb.com/net/35968</a></p>
<p>BGPTools: <a href="https://bgp.tools/as/215085" target="_blank" style="color: #07AAF9;">https://bgp.tools/as/215085</a></p>
<p>Lookingglas: <a href="https://lookingglass.as215085.net" target="_blank" style="color: #07AAF9;">https://lookingglass.as215085.net</a></p>
<p>Geofeed: <a href="https://as215085.net/geofeed.csv" target="_blank" style="color: #07AAF9;">https://as215085.net/geofeed.csv</a></p>
</section>
<section id="status">
<h2>Status</h2>
<p>Here you can find the network status of <b>AS215085</b>: <a href="https://status.as215085.net" target="_blank" style="color: #07AAF9;">https://status.as215085.net</a></p>
<p>Here you can find the service status of <b>PixelHosting</b>: <a href="https://status.pixelhosting.nl/" target="_blank" style="color: #07AAF9;">https://status.pixelhosting.nl</a></p>
</section>
<section id="bgptools-map">
<h2>BGP.Tools network map</h2>
<img id="pathimg" usemap="#world" src="{{ bgp_image }}">
</section>
</main>
<footer>
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script> document.getElementById("year").textContent = new Date().getFullYear(); </script>
</footer>
<script src="../static/js/materialize.min.js"></script>
</body>
</html>

197
templates/interfaces.html Normal file
View file

@ -0,0 +1,197 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | Interfaces</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="interfaces">
<div class="input-group mb-3">
<input type="text" id="interfaceSearch" class="form-control" placeholder="Search in Interface table..." onkeyup="filterTable('interfaceSearch', 'interfaceTable')">
<button id="refreshButton" class="btn btn-outline-primary" type="button" onclick="refreshTable()">
<span id="refreshIcon"></span>
<span id="refreshSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
Refresh data
</button>
</div>
<p></p> <p>Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down</p>
<table class="striped table table-bordered" id="interfaceTable">
<thead>
<tr>
<th class="sortable" onclick="sortTable('interfaceTable', 0, this)">Interface <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 1, this)">IP Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 2, this)">MAC Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 3, this)">VRF <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 4, this)">MTU <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 5, this)">Status <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('interfaceTable', 6, this)">Description <span class="sort-arrow"></span></th>
</tr>
</thead>
<tbody id="interfaceTableBody">
<tr id="interface-loading-row">
<td colspan="7" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
</tbody>
</table>
<p><a href="/interfaces/json">JSON version</a></p>
</section>
</main>
<footer>
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script> document.getElementById("year").textContent = new Date().getFullYear(); </script>
</footer>
<script src="../static/js/materialize.min.js"></script>
<script>
function filterTable(searchInputId, tableId) {
const input = document.getElementById(searchInputId);
const filter = input.value.toUpperCase();
const table = document.getElementById(tableId);
const tr = table.getElementsByTagName("tr");
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "none";
const td = tr[i].getElementsByTagName("td");
for (let j = 0; j < td.length; j++) {
if (td[j]) {
const txtValue = td[j].textContent || td[j].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
break;
}
}
}
}
}
function sortTable(tableId, columnIndex, th) {
const table = document.getElementById(tableId);
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
const headers = Array.from(th.parentNode.children);
// Reset other headers
headers.forEach(header => {
if (header !== th) {
header.classList.remove('asc', 'desc');
header.setAttribute('data-sort-state', 'none');
}
});
let currentState = th.getAttribute('data-sort-state') || 'none';
let newState = currentState === 'none' ? 'asc' : currentState === 'asc' ? 'desc' : 'none';
th.classList.remove('asc', 'desc');
if (newState !== 'none') th.classList.add(newState);
th.setAttribute('data-sort-state', newState);
if (newState === 'none') {
rows.sort((a, b) => parseInt(a.getAttribute('data-index')) - parseInt(b.getAttribute('data-index')));
} else {
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent.trim();
const bText = b.cells[columnIndex].textContent.trim();
const aNum = parseFloat(aText.replace(/[^0-9.-]/g, ''));
const bNum = parseFloat(bText.replace(/[^0-9.-]/g, ''));
const isNumeric = !isNaN(aNum) && !isNaN(bNum);
return newState === 'asc'
? (isNumeric ? aNum - bNum : aText.localeCompare(bText))
: (isNumeric ? bNum - aNum : bText.localeCompare(aText));
});
}
rows.forEach(row => tbody.appendChild(row));
}
async function loadInterfaceTable() {
const tableBody = document.getElementById("interfaceTableBody");
const loadingRow = document.getElementById("interface-loading-row");
try {
const response = await fetch("/interfaces/json");
const data = await response.json();
// Leeg de body
tableBody.innerHTML = "";
if (!data.interface_table || data.interface_table.length === 0) {
tableBody.innerHTML = "<tr><td colspan='7' class='text-center'>Geen data beschikbaar.</td></tr>";
return;
}
data.interface_table.forEach((entry, index) => {
const row = document.createElement("tr");
row.setAttribute("data-index", index);
row.innerHTML = `
<td>${entry.interface}</td>
<td>${entry.ip_address}</td>
<td>${entry.mac_address}</td>
<td>${entry.vrf}</td>
<td>${entry.mtu}</td>
<td>${entry.status}</td>
<td>${entry.description}</td>
`;
tableBody.appendChild(row);
});
} catch (err) {
tableBody.innerHTML = "<tr><td colspan='7' class='text-center text-danger'>Fout bij ophalen van data.</td></tr>";
}
}
window.addEventListener("DOMContentLoaded", () => {
loadInterfaceTable();
});
function refreshTable() {
const searchInput = document.getElementById("interfaceSearch");
const refreshIcon = document.getElementById("refreshIcon");
const refreshSpinner = document.getElementById("refreshSpinner");
searchInput.value = ""; // Leegmaken van het zoekveld
refreshIcon.classList.add("d-none");
refreshSpinner.classList.remove("d-none");
loadInterfaceTable().then(() => {
refreshIcon.classList.remove("d-none");
refreshSpinner.classList.add("d-none");
});
}
</script>
</body>
</html>

187
templates/neighbors.html Normal file
View file

@ -0,0 +1,187 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AS215085 - Router tools | Neighbors</title>
<link rel="icon" type="image/png" href="../static/img/cropped-Pixelhosting-logo-favicon-32x32.png">
<script src="../static/js/bootstrap.bundle.min.js"></script>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/style.css" rel="stylesheet">
</head>
<body>
<header>
<div class="header-content">
<a href="/"> <img src="../static/img/as215085-logo.png" alt="AS215085 Logo" style="height: 120px; opacity: 1;!important"> </a>
<h2><b>core1.doet.pixelhosting.nl</b></h2>
<p><b>Proudly delivering the backbone for PixelHostings services</b></p>
</div>
</header>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #07AAF9;">
<div class="container-fluid">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav justify-content-center w-100">
<li class="nav-item"><a class="nav-link text-white" href="/">Home</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp">BGP summary</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/bgp-route">BGP route</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/arp">ARP table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/neighbors">Neighbors table</a></li>
<li class="nav-item"><a class="nav-link text-white" href="/interfaces">Interfaces table</a></li>
</ul>
</div>
</div>
</nav>
<main>
<section id="arp">
<div class="input-group mb-3">
<input type="text" id="neighborsSearch" class="form-control" placeholder="Search in Neighbors table..." onkeyup="filterTable('neighborsSearch', 'neighborsTable')">
<button id="refreshButton" class="btn btn-outline-primary" type="button" onclick="refreshNeighborsTable()">
<span id="refreshIcon"></span>
<span id="refreshSpinner" class="spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
Refresh data
</button>
</div>
<table class="striped table table-bordered" id="neighborTable">
<thead>
<tr>
<th class="sortable" onclick="sortTable('neighborsTable', 0, this)">Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('neighborsTable', 1, this)">Interface <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('neighborsTable', 2, this)">Link Layer Address <span class="sort-arrow"></span></th>
<th class="sortable" onclick="sortTable('neighborsTable', 3, this)">State <span class="sort-arrow"></span></th>
</tr>
</thead>
<tbody id="neighborsTableBody">
<tr id="arp-loading-row">
<td colspan="4" class="text-center">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
</tbody>
</table>
<p><a href="/neighbors/json">JSON version</a></p>
</section>
</main>
<footer>
<p>&copy; 2020 <span id="year"></span> AS215085 (PixelHosting). All rights reserved.</p>
<script> document.getElementById("year").textContent = new Date().getFullYear(); </script>
</footer>
<script src="../static/js/materialize.min.js"></script>
<script>
function filterTable(searchInputId, tableId) {
const input = document.getElementById(searchInputId);
const filter = input.value.toUpperCase();
const table = document.getElementById(tableId);
const tr = table.getElementsByTagName("tr");
for (let i = 1; i < tr.length; i++) {
tr[i].style.display = "none";
const td = tr[i].getElementsByTagName("td");
for (let j = 0; j < td.length; j++) {
if (td[j]) {
const txtValue = td[j].textContent || td[j].innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
break;
}
}
}
}
}
function sortTable(tableId, columnIndex, th) {
const table = document.getElementById(tableId);
const tbody = table.tBodies[0];
const rows = Array.from(tbody.rows);
const headers = Array.from(th.parentNode.children);
headers.forEach(header => {
if (header !== th) {
header.classList.remove('asc', 'desc');
header.setAttribute('data-sort-state', 'none');
}
});
let currentState = th.getAttribute('data-sort-state') || 'none';
let newState = currentState === 'none' ? 'asc' : currentState === 'asc' ? 'desc' : 'none';
th.classList.remove('asc', 'desc');
if (newState !== 'none') th.classList.add(newState);
th.setAttribute('data-sort-state', newState);
if (newState === 'none') {
rows.sort((a, b) => parseInt(a.getAttribute('data-index')) - parseInt(b.getAttribute('data-index')));
} else {
rows.sort((a, b) => {
const aText = a.cells[columnIndex].textContent.trim();
const bText = b.cells[columnIndex].textContent.trim();
const aNum = parseFloat(aText.replace(/[^0-9.-]/g, ''));
const bNum = parseFloat(bText.replace(/[^0-9.-]/g, ''));
const isNumeric = !isNaN(aNum) && !isNaN(bNum);
return newState === 'asc'
? (isNumeric ? aNum - bNum : aText.localeCompare(bText))
: (isNumeric ? bNum - aNum : bText.localeCompare(aText));
});
}
rows.forEach(row => tbody.appendChild(row));
}
async function loadneighborsTable() {
const tableBody = document.getElementById("neighborsTableBody");
try {
const response = await fetch("/neighbors/json");
const data = await response.json();
tableBody.innerHTML = "";
if (!data.neighbors_table || data.neighbors_table.length === 0) {
tableBody.innerHTML = "<tr><td colspan='4' class='text-center'>Geen data beschikbaar.</td></tr>";
return;
}
data.neighbors_table.forEach((entry, index) => {
const row = document.createElement("tr");
row.setAttribute("data-index", index);
row.innerHTML = `
<td>${entry.address}</td>
<td>${entry.interface}</td>
<td>${entry.link_layer_address}</td>
<td>${entry.state}</td>
`;
tableBody.appendChild(row);
});
} catch (err) {
tableBody.innerHTML = "<tr><td colspan='4' class='text-center text-danger'>Fout bij ophalen van data.</td></tr>";
}
}
window.addEventListener("DOMContentLoaded", () => {
loadneighborsTable();
});
function refreshNeighborsTable() {
const searchInput = document.getElementById("neighborsSearch");
const refreshIcon = document.getElementById("refreshIcon");
const refreshSpinner = document.getElementById("refreshSpinner");
searchInput.value = ""; // Leegmaken van het zoekveld
refreshIcon.classList.add("d-none");
refreshSpinner.classList.remove("d-none");
loadneighborsTable().then(() => {
refreshIcon.classList.remove("d-none");
refreshSpinner.classList.add("d-none");
});
}
</script>
</body>
</html>