updates
This commit is contained in:
parent
97b3486c72
commit
e491e365cd
41
ex4.html
41
ex4.html
|
|
@ -176,7 +176,10 @@
|
||||||
}},
|
}},
|
||||||
{ selector: 'edge[label]', style: { 'label': 'data(label)' }},
|
{ selector: 'edge[label]', style: { 'label': 'data(label)' }},
|
||||||
{ selector: 'edge.route', style: { 'line-style': 'dashed','line-color': '#94a3b8','target-arrow-color': '#94a3b8' }},
|
{ selector: 'edge.route', style: { 'line-style': 'dashed','line-color': '#94a3b8','target-arrow-color': '#94a3b8' }},
|
||||||
{ selector: 'edge.sg-attach', style: { 'line-style': 'dotted','line-color': '#f59e0b','target-arrow-color': '#f59e0b' }}
|
{ selector: 'edge.sg-attach', style: { 'line-style': 'dotted','line-color': '#f59e0b','target-arrow-color': '#f59e0b' }},
|
||||||
|
{ selector: 'edge.sg-sg', style: { 'line-color': '#f59e0b','target-arrow-color': '#f59e0b','source-arrow-color': '#f59e0b' }},
|
||||||
|
{ selector: 'edge.sg-sg[tarrow > 0]', style: { 'target-arrow-shape': 'triangle' }},
|
||||||
|
{ selector: 'edge.sg-sg[sarrow > 0]', style: { 'source-arrow-shape': 'triangle' }}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -219,6 +222,32 @@
|
||||||
const label = `${sg.name || sg.id}\ningress: ${(rin[0]||'—')}${rin.length>1?'…':''}`;
|
const label = `${sg.name || sg.id}\ningress: ${(rin[0]||'—')}${rin.length>1?'…':''}`;
|
||||||
elements.push({ data: { id: sg.id, type: 'sg', label, rules: { in: rin, out: rout } } });
|
elements.push({ data: { id: sg.id, type: 'sg', label, rules: { in: rin, out: rout } } });
|
||||||
}
|
}
|
||||||
|
// SG<->SG edges based on rules referencing other SGs
|
||||||
|
function parseSgRef(rule){
|
||||||
|
// match: "proto port from sg-xxxx"
|
||||||
|
const m = (rule||'').match(/^(\S+)\s+([^\s]+)\s+from\s+(sg-[0-9a-zA-Z]+)/);
|
||||||
|
if(!m) return null; return { proto:m[1], port:m[2], sg:m[3] };
|
||||||
|
}
|
||||||
|
const pairMap = new Map(); // key: A|B -> {inOn:{}, outFrom:{}}
|
||||||
|
function keyAB(a,b){ return a<b ? (a+'|'+b) : (b+'|'+a); }
|
||||||
|
function ensurePair(a,b){ const k=keyAB(a,b); if(!pairMap.has(k)) pairMap.set(k,{inOn:{},outFrom:{}}); return pairMap.get(k); }
|
||||||
|
function addIn(target, src, proto, port){ const p=ensurePair(target,src); (p.inOn[target]||(p.inOn[target]=[])).push({proto,port,src}); }
|
||||||
|
function addOut(src, target, proto, port){ const p=ensurePair(src,target); (p.outFrom[src]||(p.outFrom[src]=[])).push({proto,port,target}); }
|
||||||
|
for (const sg of sgs) {
|
||||||
|
for (const r of (sg.rules_in||[])) { const x=parseSgRef(r); if(x && sgById[x.sg]) addIn(sg.id, x.sg, x.proto, x.port); }
|
||||||
|
for (const r of (sg.rules_out||[])) { const x=parseSgRef(r); if(x && sgById[x.sg]) addOut(sg.id, x.sg, x.proto, x.port); }
|
||||||
|
}
|
||||||
|
function summarize(list){ const uniq=new Set(); for(const it of (list||[])){ const text=`${it.proto} ${it.port}`; uniq.add(text); } return Array.from(uniq).join(', '); }
|
||||||
|
for (const [k, val] of pairMap.entries()){
|
||||||
|
const [a,b]=k.split('|');
|
||||||
|
const s=a, t=b; // orient stable by id
|
||||||
|
const s2tList = (val.outFrom[s]||[]).concat(val.inOn[t]||[]);
|
||||||
|
const t2sList = (val.outFrom[t]||[]).concat(val.inOn[s]||[]);
|
||||||
|
const tarrow = s2tList.length ? 1 : 0; // arrow at target if flow S->T
|
||||||
|
const sarrow = t2sList.length ? 1 : 0; // arrow at source if flow T->S
|
||||||
|
const label = [summarize(s2tList), summarize(t2sList)].filter(Boolean).join(' | ');
|
||||||
|
elements.push({ data: { id:`sg:${s}|${t}`, source:s, target:t, label, tarrow, sarrow }, classes:'sg-sg' });
|
||||||
|
}
|
||||||
// EC2
|
// EC2
|
||||||
for (const i of ec2s) {
|
for (const i of ec2s) {
|
||||||
const label = `${i.name || i.id}\n${i.id}\n${i.type || ''}\n${i.privateIp ? ('Private IP: ' + i.privateIp) : ''}`;
|
const label = `${i.name || i.id}\n${i.id}\n${i.type || ''}\n${i.privateIp ? ('Private IP: ' + i.privateIp) : ''}`;
|
||||||
|
|
@ -431,7 +460,7 @@
|
||||||
|
|
||||||
document.getElementById('toggle-sg').addEventListener('change',(e)=>{
|
document.getElementById('toggle-sg').addEventListener('change',(e)=>{
|
||||||
const show=e.target.checked;
|
const show=e.target.checked;
|
||||||
cy.batch(()=>{ cy.nodes('[type = "sg"]').style('display', show?'element':'none'); cy.edges('.sg-attach').style('display', show?'element':'none'); });
|
cy.batch(()=>{ cy.nodes('[type = "sg"]').style('display', show?'element':'none'); cy.edges('.sg-attach, .sg-sg').style('display', show?'element':'none'); });
|
||||||
});
|
});
|
||||||
document.getElementById('toggle-routes').addEventListener('change',(e)=>{
|
document.getElementById('toggle-routes').addEventListener('change',(e)=>{
|
||||||
const show=e.target.checked;
|
const show=e.target.checked;
|
||||||
|
|
@ -470,6 +499,12 @@
|
||||||
});
|
});
|
||||||
cy.fit(undefined,24);
|
cy.fit(undefined,24);
|
||||||
}
|
}
|
||||||
|
async function tryLoadStartupLayout(){
|
||||||
|
try{
|
||||||
|
const resp = await fetch('layout.json', { cache: 'no-cache' });
|
||||||
|
if(resp && resp.ok){ const data = await resp.json(); applyLayoutData(data); }
|
||||||
|
}catch(e){ /* ignore if missing */ }
|
||||||
|
}
|
||||||
document.getElementById('btn-save').addEventListener('click',()=> downloadJSON(snapshotLayout(),'vpc-layout.json'));
|
document.getElementById('btn-save').addEventListener('click',()=> downloadJSON(snapshotLayout(),'vpc-layout.json'));
|
||||||
document.getElementById('btn-load').addEventListener('click',()=> document.getElementById('load-file').click());
|
document.getElementById('btn-load').addEventListener('click',()=> document.getElementById('load-file').click());
|
||||||
document.getElementById('load-file').addEventListener('change',(e)=>{
|
document.getElementById('load-file').addEventListener('change',(e)=>{
|
||||||
|
|
@ -494,6 +529,8 @@
|
||||||
const LKEY='vpc-layout-autosave';
|
const LKEY='vpc-layout-autosave';
|
||||||
function saveLocal(){ try{ localStorage.setItem(LKEY, JSON.stringify(snapshotLayout())); }catch{} }
|
function saveLocal(){ try{ localStorage.setItem(LKEY, JSON.stringify(snapshotLayout())); }catch{} }
|
||||||
function restoreLocal(){ try{ const raw=localStorage.getItem(LKEY); if(raw) applyLayoutData(JSON.parse(raw)); }catch{} }
|
function restoreLocal(){ try{ const raw=localStorage.getItem(LKEY); if(raw) applyLayoutData(JSON.parse(raw)); }catch{} }
|
||||||
|
// Load file-based layout first (if present), then restore any local override
|
||||||
|
await tryLoadStartupLayout();
|
||||||
restoreLocal();
|
restoreLocal();
|
||||||
cy.on('position','node', saveLocal);
|
cy.on('position','node', saveLocal);
|
||||||
cy.on('mouseup', ()=> { if(labelMode) saveLocal(); });
|
cy.on('mouseup', ()=> { if(labelMode) saveLocal(); });
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue