|
@@ -9468,7 +9468,6 @@ async function loadDynamicRouting(showToastMsg = false) {
|
|
|
fetchSafeJson(`${API_BASE}/routing/ospf/neighbors`),
|
|
fetchSafeJson(`${API_BASE}/routing/ospf/neighbors`),
|
|
|
fetchSafeJson(`${API_BASE}/routing/ospf/lsa`),
|
|
fetchSafeJson(`${API_BASE}/routing/ospf/lsa`),
|
|
|
fetchSafeJson(`${API_BASE}/routing/filters`),
|
|
fetchSafeJson(`${API_BASE}/routing/filters`),
|
|
|
- fetchSafeJson(`${API_BASE}/routing/filters/select-rules`),
|
|
|
|
|
fetchSafeJson(`${API_BASE}/routing/filters/community-lists`),
|
|
fetchSafeJson(`${API_BASE}/routing/filters/community-lists`),
|
|
|
fetchSafeJson(`${API_BASE}/routing/filters/as-lists`)
|
|
fetchSafeJson(`${API_BASE}/routing/filters/as-lists`)
|
|
|
]);
|
|
]);
|
|
@@ -9487,7 +9486,6 @@ async function loadDynamicRouting(showToastMsg = false) {
|
|
|
if (ospfNbrs) state.ospfNeighbors = ospfNbrs;
|
|
if (ospfNbrs) state.ospfNeighbors = ospfNbrs;
|
|
|
if (ospfLsa) state.ospfLSA = ospfLsa;
|
|
if (ospfLsa) state.ospfLSA = ospfLsa;
|
|
|
if (filters) state.routingFilters = filters;
|
|
if (filters) state.routingFilters = filters;
|
|
|
- if (selectRules) state.routingSelectRules = selectRules;
|
|
|
|
|
if (commLists) state.routingCommunityLists = commLists;
|
|
if (commLists) state.routingCommunityLists = commLists;
|
|
|
if (asLists) state.routingASLists = asLists;
|
|
if (asLists) state.routingASLists = asLists;
|
|
|
|
|
|
|
@@ -12171,7 +12169,7 @@ if (!state.rfSort) state.rfSort = {};
|
|
|
|
|
|
|
|
function switchRoutingFilterTab(tab) {
|
|
function switchRoutingFilterTab(tab) {
|
|
|
state.activeRoutingFilterTab = tab;
|
|
state.activeRoutingFilterTab = tab;
|
|
|
- const tabs = ['rules', 'select-rules', 'community-lists', 'as-lists'];
|
|
|
|
|
|
|
+ const tabs = ['rules', 'community-lists', 'as-lists'];
|
|
|
|
|
|
|
|
tabs.forEach(t => {
|
|
tabs.forEach(t => {
|
|
|
const btn = document.getElementById(`tab-rf-${t}`);
|
|
const btn = document.getElementById(`tab-rf-${t}`);
|
|
@@ -12191,7 +12189,7 @@ function switchRoutingFilterTab(tab) {
|
|
|
|
|
|
|
|
const chainContainer = document.getElementById('rf-chain-filter-container');
|
|
const chainContainer = document.getElementById('rf-chain-filter-container');
|
|
|
if (chainContainer) {
|
|
if (chainContainer) {
|
|
|
- if (tab === 'rules' || tab === 'select-rules') chainContainer.classList.remove('hidden');
|
|
|
|
|
|
|
+ if (tab === 'rules') chainContainer.classList.remove('hidden');
|
|
|
else chainContainer.classList.add('hidden');
|
|
else chainContainer.classList.add('hidden');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -12205,7 +12203,6 @@ function selectFilterRow(tab, id) {
|
|
|
state.selectedFilterRow[tab] = id;
|
|
state.selectedFilterRow[tab] = id;
|
|
|
}
|
|
}
|
|
|
if (tab === 'rules') renderRoutingFilterRules();
|
|
if (tab === 'rules') renderRoutingFilterRules();
|
|
|
- else if (tab === 'select-rules') renderRoutingFilterSelectRules();
|
|
|
|
|
else if (tab === 'community-lists') renderRoutingFilterCommunityLists();
|
|
else if (tab === 'community-lists') renderRoutingFilterCommunityLists();
|
|
|
else if (tab === 'as-lists') renderRoutingFilterASLists();
|
|
else if (tab === 'as-lists') renderRoutingFilterASLists();
|
|
|
}
|
|
}
|
|
@@ -12213,7 +12210,6 @@ function selectFilterRow(tab, id) {
|
|
|
function openRoutingFilterNewModal() {
|
|
function openRoutingFilterNewModal() {
|
|
|
const tab = state.activeRoutingFilterTab || 'rules';
|
|
const tab = state.activeRoutingFilterTab || 'rules';
|
|
|
if (tab === 'rules') openAddRoutingFilterModal();
|
|
if (tab === 'rules') openAddRoutingFilterModal();
|
|
|
- else if (tab === 'select-rules') openAddSelectRuleModal();
|
|
|
|
|
else if (tab === 'community-lists') openAddCommunityListModal();
|
|
else if (tab === 'community-lists') openAddCommunityListModal();
|
|
|
else if (tab === 'as-lists') openAddASListModal();
|
|
else if (tab === 'as-lists') openAddASListModal();
|
|
|
}
|
|
}
|
|
@@ -12226,7 +12222,6 @@ function toggleSelectedRoutingFilter(disabled) {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (tab === 'rules') toggleRoutingFilterState(id, disabled);
|
|
if (tab === 'rules') toggleRoutingFilterState(id, disabled);
|
|
|
- else if (tab === 'select-rules') toggleSelectRuleState(id, disabled);
|
|
|
|
|
else if (tab === 'community-lists') toggleCommunityListState(id, disabled);
|
|
else if (tab === 'community-lists') toggleCommunityListState(id, disabled);
|
|
|
else if (tab === 'as-lists') toggleASListState(id, disabled);
|
|
else if (tab === 'as-lists') toggleASListState(id, disabled);
|
|
|
}
|
|
}
|
|
@@ -12239,7 +12234,6 @@ function removeSelectedRoutingFilter() {
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
if (tab === 'rules') deleteRoutingFilter(id);
|
|
if (tab === 'rules') deleteRoutingFilter(id);
|
|
|
- else if (tab === 'select-rules') deleteSelectRule(id);
|
|
|
|
|
else if (tab === 'community-lists') deleteCommunityList(id);
|
|
else if (tab === 'community-lists') deleteCommunityList(id);
|
|
|
else if (tab === 'as-lists') deleteASList(id);
|
|
else if (tab === 'as-lists') deleteASList(id);
|
|
|
}
|
|
}
|
|
@@ -12262,7 +12256,6 @@ function sortFilterTable(tab, col) {
|
|
|
|
|
|
|
|
let list = [];
|
|
let list = [];
|
|
|
if (tab === 'rules') list = state.routingFilters || [];
|
|
if (tab === 'rules') list = state.routingFilters || [];
|
|
|
- else if (tab === 'select-rules') list = state.routingSelectRules || [];
|
|
|
|
|
else if (tab === 'community-lists') list = state.routingCommunityLists || [];
|
|
else if (tab === 'community-lists') list = state.routingCommunityLists || [];
|
|
|
else if (tab === 'as-lists') list = state.routingASLists || [];
|
|
else if (tab === 'as-lists') list = state.routingASLists || [];
|
|
|
|
|
|
|
@@ -12286,7 +12279,6 @@ function sortFilterTable(tab, col) {
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
if (tab === 'rules') renderRoutingFilterRules();
|
|
if (tab === 'rules') renderRoutingFilterRules();
|
|
|
- else if (tab === 'select-rules') renderRoutingFilterSelectRules();
|
|
|
|
|
else if (tab === 'community-lists') renderRoutingFilterCommunityLists();
|
|
else if (tab === 'community-lists') renderRoutingFilterCommunityLists();
|
|
|
else if (tab === 'as-lists') renderRoutingFilterASLists();
|
|
else if (tab === 'as-lists') renderRoutingFilterASLists();
|
|
|
}
|
|
}
|
|
@@ -12316,7 +12308,6 @@ function renderRoutingFilters() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
renderRoutingFilterRules();
|
|
renderRoutingFilterRules();
|
|
|
- renderRoutingFilterSelectRules();
|
|
|
|
|
renderRoutingFilterCommunityLists();
|
|
renderRoutingFilterCommunityLists();
|
|
|
renderRoutingFilterASLists();
|
|
renderRoutingFilterASLists();
|
|
|
}
|
|
}
|
|
@@ -12415,73 +12406,7 @@ function renderRoutingFilterRules() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// -------------------------------------------------------------
|
|
// -------------------------------------------------------------
|
|
|
-// TAB 2: SELECT RULES (/routing filter select-rule)
|
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
|
-
|
|
|
|
|
-function renderRoutingFilterSelectRules() {
|
|
|
|
|
- const tbody = document.getElementById('routing-filter-select-rules-tbody');
|
|
|
|
|
- if (!tbody) return;
|
|
|
|
|
- const empty = document.getElementById('rf-select-rules-empty');
|
|
|
|
|
- const statusCount = document.getElementById('rf-status-count');
|
|
|
|
|
-
|
|
|
|
|
- let list = (state.routingSelectRules || []).slice();
|
|
|
|
|
-
|
|
|
|
|
- if (state.filterChainSelected && state.filterChainSelected !== 'all') {
|
|
|
|
|
- list = list.filter(r => r.chain === state.filterChainSelected);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const q = state.rfSearchQuery;
|
|
|
|
|
- if (q) {
|
|
|
|
|
- list = list.filter(r =>
|
|
|
|
|
- (r.chain && r.chain.toLowerCase().includes(q)) ||
|
|
|
|
|
- (r.rule && r.rule.toLowerCase().includes(q)) ||
|
|
|
|
|
- (r.comment && r.comment.toLowerCase().includes(q))
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (statusCount && state.activeRoutingFilterTab === 'select-rules') {
|
|
|
|
|
- statusCount.innerText = `${list.length} item${list.length === 1 ? '' : 's'}`;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (list.length === 0) {
|
|
|
|
|
- tbody.innerHTML = '';
|
|
|
|
|
- if (empty) empty.classList.remove('hidden');
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
- if (empty) empty.classList.add('hidden');
|
|
|
|
|
-
|
|
|
|
|
- tbody.innerHTML = list.map(r => {
|
|
|
|
|
- const isSelected = state.selectedFilterRow?.['select-rules'] === r.id;
|
|
|
|
|
- const flagA = !r.disabled ? '<span class="text-cyan-400 font-bold">A</span>' : '';
|
|
|
|
|
- const flagX = r.disabled ? '<span class="text-rose-400 font-bold">X</span>' : '';
|
|
|
|
|
-
|
|
|
|
|
- return `
|
|
|
|
|
- <tr onclick="selectFilterRow('select-rules', '${r.id}')" class="cursor-pointer transition select-none ${isSelected ? 'bg-emerald-950/40 ring-1 ring-emerald-500/40' : 'hover:bg-slate-800/40'}">
|
|
|
|
|
- <td class="py-2 px-2 text-center tracking-wider">${flagA}${flagX}</td>
|
|
|
|
|
- <td class="py-2 px-2 font-mono text-slate-400 font-bold">${r.rule_order || 10}</td>
|
|
|
|
|
- <td class="py-2 px-3">
|
|
|
|
|
- <span class="px-2 py-0.5 rounded text-[11px] font-mono bg-slate-800 text-cyan-300 border border-slate-700 font-semibold">${escapeHtml(r.chain)}</span>
|
|
|
|
|
- </td>
|
|
|
|
|
- <td class="py-2 px-3 break-all max-w-xl font-mono text-emerald-400">${escapeHtml(r.rule || '')}</td>
|
|
|
|
|
- <td class="py-2 px-3 text-slate-400 font-sans truncate max-w-xs">${escapeHtml(r.comment || '')}</td>
|
|
|
|
|
- <td class="py-2 px-3 text-right space-x-1 whitespace-nowrap font-sans" onclick="event.stopPropagation()">
|
|
|
|
|
- <button onclick="toggleSelectRuleState('${r.id}', ${!r.disabled})" class="px-2 py-0.5 rounded text-[10px] ${r.disabled ? 'bg-emerald-950 hover:bg-emerald-900 text-emerald-400 border border-emerald-800/50' : 'bg-slate-800 hover:bg-slate-700 text-slate-300 border border-slate-700'}">
|
|
|
|
|
- ${r.disabled ? 'Enable' : 'Disable'}
|
|
|
|
|
- </button>
|
|
|
|
|
- <button onclick="openEditSelectRuleModal('${r.id}')" class="px-2 py-0.5 rounded text-[10px] bg-slate-800 hover:bg-slate-700 text-emerald-300 border border-slate-700">
|
|
|
|
|
- Edit
|
|
|
|
|
- </button>
|
|
|
|
|
- <button onclick="deleteSelectRule('${r.id}')" class="px-2 py-0.5 rounded text-[10px] bg-rose-950/60 hover:bg-rose-900 text-rose-300 border border-rose-900/50">
|
|
|
|
|
- ✕
|
|
|
|
|
- </button>
|
|
|
|
|
- </td>
|
|
|
|
|
- </tr>
|
|
|
|
|
- `;
|
|
|
|
|
- }).join('');
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
|
-// TAB 3: COMMUNITY LISTS (/routing filter community-list)
|
|
|
|
|
|
|
+// TAB 2: COMMUNITY LISTS (/routing filter community-list)
|
|
|
// -------------------------------------------------------------
|
|
// -------------------------------------------------------------
|
|
|
|
|
|
|
|
function renderRoutingFilterCommunityLists() {
|
|
function renderRoutingFilterCommunityLists() {
|
|
@@ -12681,7 +12606,9 @@ function generateScriptFromWizard() {
|
|
|
const actions = [];
|
|
const actions = [];
|
|
|
if (lp > 0) actions.push(`set bgp-local-pref ${lp};`);
|
|
if (lp > 0) actions.push(`set bgp-local-pref ${lp};`);
|
|
|
if (prep > 0) actions.push(`set bgp-path-prepend ${prep};`);
|
|
if (prep > 0) actions.push(`set bgp-path-prepend ${prep};`);
|
|
|
- actions.push(`${act};`);
|
|
|
|
|
|
|
+ if (act && act !== 'passthrough') {
|
|
|
|
|
+ actions.push(`${act};`);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
let code = '';
|
|
let code = '';
|
|
|
if (conds.length > 0) {
|
|
if (conds.length > 0) {
|
|
@@ -12878,103 +12805,6 @@ async function toggleRoutingFilterState(id, disabled) {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
|
-// MODAL CRUD: SELECT RULES (/routing filter select-rule)
|
|
|
|
|
-// -------------------------------------------------------------
|
|
|
|
|
-
|
|
|
|
|
-function openAddSelectRuleModal() {
|
|
|
|
|
- document.getElementById('modal-rf-sr-id').value = '';
|
|
|
|
|
- document.getElementById('modal-rf-sr-title').innerText = 'Add Routing Select Rule';
|
|
|
|
|
- document.getElementById('modal-rf-sr-chain').value = state.filterChainSelected && state.filterChainSelected !== 'all' ? state.filterChainSelected : 'bgp-out';
|
|
|
|
|
- document.getElementById('modal-rf-sr-order').value = ((state.routingSelectRules || []).length + 1) * 10;
|
|
|
|
|
- document.getElementById('modal-rf-sr-rule').value = 'if (protocol bgp && dst in 10.0.0.0/8) { accept; }';
|
|
|
|
|
- document.getElementById('modal-rf-sr-comment').value = '';
|
|
|
|
|
- document.getElementById('modal-rf-sr-disabled').checked = false;
|
|
|
|
|
- document.getElementById('modal-rf-select-rule').classList.remove('hidden');
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function openEditSelectRuleModal(id) {
|
|
|
|
|
- const r = (state.routingSelectRules || []).find(item => item.id === id);
|
|
|
|
|
- if (!r) return;
|
|
|
|
|
- document.getElementById('modal-rf-sr-id').value = r.id;
|
|
|
|
|
- document.getElementById('modal-rf-sr-title').innerText = 'Edit Routing Select Rule';
|
|
|
|
|
- document.getElementById('modal-rf-sr-chain').value = r.chain || '';
|
|
|
|
|
- document.getElementById('modal-rf-sr-order').value = r.rule_order || 10;
|
|
|
|
|
- document.getElementById('modal-rf-sr-rule').value = r.rule || '';
|
|
|
|
|
- document.getElementById('modal-rf-sr-comment').value = r.comment || '';
|
|
|
|
|
- document.getElementById('modal-rf-sr-disabled').checked = !!r.disabled;
|
|
|
|
|
- document.getElementById('modal-rf-select-rule').classList.remove('hidden');
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function closeSelectRuleModal() {
|
|
|
|
|
- document.getElementById('modal-rf-select-rule').classList.add('hidden');
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function submitSelectRule(e) {
|
|
|
|
|
- e.preventDefault();
|
|
|
|
|
- const payload = {
|
|
|
|
|
- id: document.getElementById('modal-rf-sr-id').value,
|
|
|
|
|
- chain: document.getElementById('modal-rf-sr-chain').value.trim(),
|
|
|
|
|
- rule_order: parseInt(document.getElementById('modal-rf-sr-order').value, 10) || 10,
|
|
|
|
|
- rule: document.getElementById('modal-rf-sr-rule').value.trim(),
|
|
|
|
|
- comment: document.getElementById('modal-rf-sr-comment').value.trim(),
|
|
|
|
|
- disabled: document.getElementById('modal-rf-sr-disabled').checked
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await fetch(`${API_BASE}/routing/filters/select-rules`, {
|
|
|
|
|
- method: 'POST',
|
|
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
- body: JSON.stringify(payload)
|
|
|
|
|
- });
|
|
|
|
|
- if (res.ok) {
|
|
|
|
|
- closeSelectRuleModal();
|
|
|
|
|
- showToast('Success', 'Select rule saved', 'success');
|
|
|
|
|
- loadDynamicRouting();
|
|
|
|
|
- } else {
|
|
|
|
|
- const err = await res.json();
|
|
|
|
|
- showToast('Error', err.error || 'Failed to save select rule', 'error');
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- showToast('Error', err.message, 'error');
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function deleteSelectRule(id) {
|
|
|
|
|
- if (!confirm('Are you sure you want to delete this select rule?')) return;
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await fetch(`${API_BASE}/routing/filters/select-rules/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
|
|
|
|
- if (res.ok) {
|
|
|
|
|
- showToast('Deleted', 'Select rule removed', 'info');
|
|
|
|
|
- loadDynamicRouting();
|
|
|
|
|
- } else {
|
|
|
|
|
- const err = await res.json();
|
|
|
|
|
- showToast('Error', err.error || 'Failed to delete select rule', 'error');
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- showToast('Error', err.message, 'error');
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-async function toggleSelectRuleState(id, disabled) {
|
|
|
|
|
- try {
|
|
|
|
|
- const res = await fetch(`${API_BASE}/routing/filters/select-rules/${encodeURIComponent(id)}/state`, {
|
|
|
|
|
- method: 'POST',
|
|
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
- body: JSON.stringify({ disabled })
|
|
|
|
|
- });
|
|
|
|
|
- if (res.ok) {
|
|
|
|
|
- showToast('Status Updated', `Select rule ${disabled ? 'disabled' : 'enabled'}`, 'info');
|
|
|
|
|
- loadDynamicRouting();
|
|
|
|
|
- } else {
|
|
|
|
|
- const err = await res.json();
|
|
|
|
|
- showToast('Error', err.error || 'Failed to update select rule state', 'error');
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- showToast('Error', err.message, 'error');
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
// -------------------------------------------------------------
|
|
// -------------------------------------------------------------
|
|
|
// MODAL CRUD: COMMUNITY LISTS (/routing filter community-list)
|
|
// MODAL CRUD: COMMUNITY LISTS (/routing filter community-list)
|
|
|
// -------------------------------------------------------------
|
|
// -------------------------------------------------------------
|