fix(switch-compat): physically validate Flexoptix suggestions by port speed + cage mechanics
getFlexoptixSuggestions matched ONLY by form factor, discarding the speed encoded in each ports_config key (e.g. '100G_QSFP28'). Corrupt transceiver speed_gbps values (400G/200G/128G/100000G mislabeled as QSFP28) leaked through, so a 100G switch showed impossible '400G QSFP28' / '100T QSFP28' suggestions. Now parses (speed, form_factor) from each port key and requires every suggested module to (a) mechanically seat in the cage — precise port-FF -> accepted-module-FF map, a QSFP28 cage takes QSFP+/28/56 but never QSFP-DD — and (b) have speed_gbps <= the port's speed. CX 10000-48Y6C (25G SFP28 + 100G QSFP28) now returns only valid <=25G SFP / <=100G QSFP modules; 0 physically impossible entries (was 4 garbage groups). Belt-and-suspenders: even with corrupt speed data, nothing oversized can reach a customer-facing suggestion.
This commit is contained in:
parent
53518fa15a
commit
2b7d5c7037
@ -318,23 +318,40 @@ export async function getCompatibleTransceivers(switchId: string) {
|
||||
*/
|
||||
export async function getFlexoptixSuggestions(switchId: string) {
|
||||
const result = await pool.query(
|
||||
`WITH switch_form_factors AS (
|
||||
`WITH switch_ports AS (
|
||||
-- Parse each ports_config key 'SPEED_FORMFACTOR' (e.g. '100G_QSFP28') into
|
||||
-- a numeric port speed (Gbps) + a cage family. Real transceivers are then
|
||||
-- bounded by the port speed so corrupt/oversized records cannot leak in.
|
||||
SELECT DISTINCT
|
||||
-- numeric speed prefix: 100G->100, 25G->25, 2.5G->2.5, 100M->0.1, 800G->800
|
||||
CASE
|
||||
WHEN k ~ '^[0-9.]+M' THEN (substring(k from '^([0-9.]+)M')::numeric / 1000)
|
||||
WHEN k ~ '^[0-9.]+G' THEN substring(k from '^([0-9.]+)G')::numeric
|
||||
WHEN k ~ '^[0-9.]+T' THEN (substring(k from '^([0-9.]+)T')::numeric * 1000)
|
||||
ELSE NULL
|
||||
END AS port_speed,
|
||||
-- the port's specific cage type (determines which modules mechanically seat)
|
||||
CASE
|
||||
WHEN k ILIKE '%QSFP-DD800%' THEN 'QSFP-DD800'
|
||||
WHEN k ILIKE '%QSFP-DD%' THEN 'QSFP-DD'
|
||||
WHEN k ILIKE '%OSFP224%' THEN 'OSFP224'
|
||||
WHEN k ILIKE '%OSFP%' THEN 'OSFP'
|
||||
WHEN k ILIKE '%QSFP112%' THEN 'QSFP112'
|
||||
WHEN k ILIKE '%QSFP56%' THEN 'QSFP56'
|
||||
WHEN k ILIKE '%QSFP28%' THEN 'QSFP28'
|
||||
WHEN k ILIKE '%QSFP+%' THEN 'QSFP+'
|
||||
WHEN k ILIKE '%QSFP%' THEN 'QSFP+'
|
||||
WHEN k ILIKE '%OSFP224%' THEN 'OSFP224'
|
||||
WHEN k ILIKE '%OSFP112%' THEN 'OSFP112'
|
||||
WHEN k ILIKE '%OSFP%' THEN 'OSFP'
|
||||
WHEN k ILIKE '%SFP56%' THEN 'SFP56'
|
||||
WHEN k ILIKE '%SFP28%' THEN 'SFP28'
|
||||
WHEN k ILIKE '%SFP+%' THEN 'SFP+'
|
||||
WHEN k ILIKE '%SFP%' THEN 'SFP+'
|
||||
WHEN k ILIKE '%CFP2%' THEN 'CFP2'
|
||||
WHEN k ILIKE '%CFP4%' THEN 'CFP4'
|
||||
WHEN k ILIKE '%CFP%' THEN 'CFP'
|
||||
END AS form_factor
|
||||
WHEN k ILIKE '%RJ45%' OR k ILIKE '%mGig%' THEN 'RJ45'
|
||||
ELSE NULL
|
||||
END AS port_ff
|
||||
FROM switches sw,
|
||||
jsonb_object_keys(sw.ports_config) AS k
|
||||
WHERE sw.id = $1 AND sw.ports_config IS NOT NULL
|
||||
@ -367,8 +384,38 @@ export async function getFlexoptixSuggestions(switchId: string) {
|
||||
LIMIT 1
|
||||
) so ON true
|
||||
WHERE LOWER(v.name) = 'flexoptix'
|
||||
AND t.form_factor IN (
|
||||
SELECT form_factor FROM switch_form_factors WHERE form_factor IS NOT NULL
|
||||
AND t.speed_gbps IS NOT NULL AND t.speed_gbps > 0
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM switch_ports sp
|
||||
WHERE sp.port_ff IS NOT NULL
|
||||
AND sp.port_speed IS NOT NULL
|
||||
-- module must mechanically seat in this port cage. A cage accepts its own
|
||||
-- type plus smaller/backward-compatible modules, never a larger one:
|
||||
-- QSFP-DD/OSFP cages take QSFP-family; QSFP28/56 cages take QSFP+/28/56
|
||||
-- (NOT QSFP-DD); SFP cages take all SFP variants. No cross-family.
|
||||
AND t.form_factor = ANY (
|
||||
CASE sp.port_ff
|
||||
WHEN 'QSFP-DD800' THEN ARRAY['QSFP-DD800','QSFP-DD','QSFP112','QSFP56','QSFP28','QSFP+']
|
||||
WHEN 'QSFP-DD' THEN ARRAY['QSFP-DD','QSFP112','QSFP56','QSFP28','QSFP+']
|
||||
WHEN 'QSFP112' THEN ARRAY['QSFP112','QSFP56','QSFP28','QSFP+']
|
||||
WHEN 'QSFP56' THEN ARRAY['QSFP56','QSFP28','QSFP+']
|
||||
WHEN 'QSFP28' THEN ARRAY['QSFP28','QSFP+']
|
||||
WHEN 'QSFP+' THEN ARRAY['QSFP+']
|
||||
WHEN 'OSFP224' THEN ARRAY['OSFP224','OSFP112','OSFP']
|
||||
WHEN 'OSFP112' THEN ARRAY['OSFP112','OSFP']
|
||||
WHEN 'OSFP' THEN ARRAY['OSFP']
|
||||
WHEN 'SFP56' THEN ARRAY['SFP56','SFP28','SFP+','SFP']
|
||||
WHEN 'SFP28' THEN ARRAY['SFP28','SFP+','SFP']
|
||||
WHEN 'SFP+' THEN ARRAY['SFP+','SFP']
|
||||
WHEN 'CFP2' THEN ARRAY['CFP2']
|
||||
WHEN 'CFP4' THEN ARRAY['CFP4']
|
||||
WHEN 'CFP' THEN ARRAY['CFP']
|
||||
WHEN 'RJ45' THEN ARRAY['RJ45','Copper']
|
||||
ELSE ARRAY[]::text[]
|
||||
END
|
||||
)
|
||||
-- speed must not exceed the port's speed (slower module in faster cage OK)
|
||||
AND t.speed_gbps <= sp.port_speed
|
||||
)
|
||||
ORDER BY t.speed_gbps DESC NULLS LAST, t.reach_meters ASC NULLS LAST`,
|
||||
[switchId]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user