parent
ab4a4cee9a
commit
0360b39598
6 changed files with 334 additions and 0 deletions
@ -0,0 +1,159 @@ |
|||||||
|
import assert from 'assert'; |
||||||
|
import { RequestHandler } from 'express'; |
||||||
|
|
||||||
|
import { REP_UUID } from '../../consts'; |
||||||
|
|
||||||
|
import { getAnvilData, getHostData, getNetworkData } from '../../accessModule'; |
||||||
|
import { sanitize } from '../../sanitize'; |
||||||
|
import { stderr } from '../../shell'; |
||||||
|
|
||||||
|
const degrade = (current: string) => |
||||||
|
current === 'optimal' ? 'degraded' : current; |
||||||
|
|
||||||
|
const compare = (a: string, b: string) => (a > b ? 1 : -1); |
||||||
|
|
||||||
|
const buildSubnodeBonds = ( |
||||||
|
ifaces: AnvilDataSubnodeNetwork['interface'], |
||||||
|
): AnvilDetailSubnodeBond[] => { |
||||||
|
const bondList = Object.entries(ifaces) |
||||||
|
.sort(([an, { type: at }], [bn, { type: bt }]) => { |
||||||
|
const ab = at === 'bond'; |
||||||
|
const bb = bt === 'bond'; |
||||||
|
|
||||||
|
if (ab && bb) return compare(an, bn); |
||||||
|
if (ab) return -1; |
||||||
|
if (bb) return 1; |
||||||
|
|
||||||
|
return compare(an, bn); |
||||||
|
}) |
||||||
|
.reduce<{ |
||||||
|
[bondUuid: string]: AnvilDetailSubnodeBond; |
||||||
|
}>((previous, [ifname, ifvalue]) => { |
||||||
|
const { type } = ifvalue; |
||||||
|
|
||||||
|
if (type === 'bond') { |
||||||
|
const { active_interface, uuid: bondUuid } = |
||||||
|
ifvalue as AnvilDataHostNetworkBond; |
||||||
|
|
||||||
|
previous[bondUuid] = { |
||||||
|
active_interface, |
||||||
|
bond_name: ifname, |
||||||
|
bond_uuid: bondUuid, |
||||||
|
links: [], |
||||||
|
}; |
||||||
|
} else if (type === 'interface') { |
||||||
|
const { |
||||||
|
bond_uuid: bondUuid, |
||||||
|
operational, |
||||||
|
speed, |
||||||
|
uuid: linkUuid, |
||||||
|
} = ifvalue as AnvilDataHostNetworkLink; |
||||||
|
|
||||||
|
// Link without bond UUID can be ignored
|
||||||
|
if (!REP_UUID.test(bondUuid)) return previous; |
||||||
|
|
||||||
|
const { |
||||||
|
[bondUuid]: { active_interface, links }, |
||||||
|
} = previous; |
||||||
|
|
||||||
|
let linkState: string = operational === 'up' ? 'optimal' : 'down'; |
||||||
|
|
||||||
|
links.forEach((xLink) => { |
||||||
|
const { link_speed: xlSpeed, link_state: xlState } = xLink; |
||||||
|
|
||||||
|
if (xlSpeed < speed) { |
||||||
|
// Seen link is slower than current link, mark seen link as 'degraded'
|
||||||
|
xLink.link_state = degrade(xlState); |
||||||
|
} else if (xlSpeed > speed) { |
||||||
|
// Current link is slower than seen link, mark current link as 'degraded'
|
||||||
|
linkState = degrade(linkState); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
links.push({ |
||||||
|
is_active: ifname === active_interface, |
||||||
|
link_name: ifname, |
||||||
|
link_speed: speed, |
||||||
|
link_state: linkState, |
||||||
|
link_uuid: linkUuid, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return previous; |
||||||
|
}, {}); |
||||||
|
|
||||||
|
return Object.values(bondList); |
||||||
|
}; |
||||||
|
|
||||||
|
export const getAnvilNetwork: RequestHandler< |
||||||
|
AnvilDetailParamsDictionary |
||||||
|
> = async (request, response) => { |
||||||
|
const { |
||||||
|
params: { anvilUuid: rAnUuid }, |
||||||
|
} = request; |
||||||
|
|
||||||
|
const anUuid = sanitize(rAnUuid, 'string', { modifierType: 'sql' }); |
||||||
|
|
||||||
|
try { |
||||||
|
assert( |
||||||
|
REP_UUID.test(anUuid), |
||||||
|
`Param UUID must be a valid UUIDv4; got [${anUuid}]`, |
||||||
|
); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to assert value during get anvil network; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(400).send(); |
||||||
|
} |
||||||
|
|
||||||
|
let ans: AnvilDataAnvilListHash; |
||||||
|
let hosts: AnvilDataHostListHash; |
||||||
|
|
||||||
|
try { |
||||||
|
ans = await getAnvilData(); |
||||||
|
hosts = await getHostData(); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to get anvil and host data; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
|
||||||
|
const { |
||||||
|
anvil_uuid: { |
||||||
|
[anUuid]: { |
||||||
|
anvil_node1_host_uuid: n1uuid, |
||||||
|
anvil_node2_host_uuid: n2uuid, |
||||||
|
}, |
||||||
|
}, |
||||||
|
} = ans; |
||||||
|
|
||||||
|
const rsbody: AnvilDetailNetworkSummary = { hosts: [] }; |
||||||
|
|
||||||
|
for (const hostUuid of [n1uuid, n2uuid]) { |
||||||
|
try { |
||||||
|
const { |
||||||
|
host_uuid: { |
||||||
|
[hostUuid]: { short_host_name: hostName }, |
||||||
|
}, |
||||||
|
} = hosts; |
||||||
|
|
||||||
|
const { [hostName]: subnodeNetwork } = await getNetworkData( |
||||||
|
hostUuid, |
||||||
|
hostName, |
||||||
|
); |
||||||
|
|
||||||
|
const { interface: ifaces } = subnodeNetwork as AnvilDataSubnodeNetwork; |
||||||
|
|
||||||
|
rsbody.hosts.push({ |
||||||
|
bonds: buildSubnodeBonds(ifaces), |
||||||
|
host_name: hostName, |
||||||
|
host_uuid: hostUuid, |
||||||
|
}); |
||||||
|
} catch (error) { |
||||||
|
stderr(`Failed to get host ${hostUuid} network data; CAUSE: ${error}`); |
||||||
|
|
||||||
|
return response.status(500).send(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return response.json(rsbody); |
||||||
|
}; |
Loading…
Reference in new issue