2013-05-16 11 views
5

Ho una grande lista di sottoreti in una rete memorizzata nel seguente layout. Viene utilizzato come tabella principale per archiviare le risorse che verranno utilizzate per analizzare automaticamente lo stato mediante uno script Python a intervalli regolari.PostgreSQL che espande il cidr in singoli indirizzi

CREATE TEMP TABLE tmp_networks (
    network cidr PRIMARY KEY 
); 

lascia supporre il suo pieno di questi valori per il bene della manifestazione:

  • 10.0.0.0/8
  • 10.0.1.0/24
  • 192.168.0.0/24

Quando eseguo lo script, lo script Python eseguirà la seguente query per rimuovere eventuali sovrapposizioni:

SELECT network 
    FROM tmp_networks 
    WHERE NOT EXISTS (
     SELECT network 
     FROM tmp_networks n 
     WHERE n.network >> tmp_networks.network 
); 

Questo funziona benissimo, ad eccezione di un piccolo problema; Ho anche una lista di singoli indirizzi che dovrebbero essere esclusi dal lavoro. Questa è anche una tabella nel database:

CREATE TEMP TABLE tmp_except (
    address inet PRIMARY KEY 
); 

lascia supporre questo contiene i seguenti indirizzi:

  • 10.0.0.100
  • 192.168.0.10

Ora, ho fallito trovare un buon metodo per rimuovere questi indirizzi spesific dall'output del database. Nei miei pensieri, la soluzione sarebbe qualcosa di simile:

  • selezionare tutte le sottoreti
  • eventuale indirizzo di eccezione si trova all'interno della sottorete, dividere la sottorete in pezzi più piccoli fino a quando l'indirizzo unica eccezione può essere rimosso e tutti gli altri restano gli indirizzi

Ho cercato di indagare se qualcosa di simile è possibile fare in PostgreSQL puro, ma non sono riuscito a trovare alcun modo per risolverlo. Qualche suggerimento su come questo dovrebbe essere risolto?

+0

Qual è il risultato che ti aspetti dalla tua richiesta? È sempre * gli indirizzi (/ 32) * o è * gli indirizzi di rete se possibile e solo gli indirizzi, se c'è un'eccezione *? – Beryllium

+0

Salve, I eccetto gli indirizzi di rete, se possibile, OR/32 se non esistono blocchi più grandi. – agnsaft

risposta

5

Mi avvicinerei a questo con due funzioni. La prima funzione accetta un cidr e un indirizzo di eccezione e restituisce un insieme di cidr che sono equivalenti al cidr originale meno l'indirizzo di eccezione. La funzione funziona dividendo il cidr in due metà e quindi rimuovendo in modo ricorsivo l'indirizzo di eccezione dalla metà in cui si trova. Un algoritmo più sofisticato potrebbe evitare alcune delle divisioni non necessarie. La semplice funzione simile a questa:

CREATE OR REPLACE FUNCTION split_cidr(net cidr, exc inet) returns setof cidr language plpgsql AS $$ 
DECLARE 
    r cidr; 
    lower cidr; 
    upper cidr; 
BEGIN 
    IF masklen(net) >= 32 THEN RETURN; END IF; 
    lower = set_masklen(net, masklen(net)+1); 
    upper = set_masklen((lower | ~ netmask(lower)) + 1, masklen(lower)); 
    IF exc << upper THEN 
    RETURN NEXT lower; 
    FOR r IN SELECT * from split_cidr(upper, exc) 
    LOOP RETURN NEXT r; 
    END LOOP; 
    ELSE 
    FOR r IN SELECT * from split_cidr(lower, exc) 
    LOOP RETURN NEXT r; 
    END LOOP; 
    RETURN NEXT upper; 
    END IF; 
    RETURN; 
END $$; 

Armati di questa funzione, si potrebbe quindi scorrere l'elenco delle reti applicandola a quelle reti che contenevano un indirizzo eccezione. La seguente funzione suddivide l'elenco degli indirizzi di rete in quelli che contengono eccezioni e quelli che non lo fanno. Quelli che non vengono restituiti, quelli che hanno la funzione sopra applicata. Questo non riguarda il caso in cui una rete contiene più di un indirizzo di eccezione.

CREATE OR REPLACE FUNCTION DOIT() RETURNS Setof cidr language plpgsql AS $$ 
DECLARE 
r cidr; 
x cidr; 
z inet; 
BEGIN 
-- these are the rows where the network has no exceptions 
FOR r in SELECT network FROM tmp_networks n WHERE NOT EXISTS (
    SELECT address FROM tmp_except WHERE address << n.network) 
LOOP RETURN NEXT r; 
END LOOP; 

-- these are the rows where the network has an exception 
FOR r,z in SELECT network, address from tmp_networks full join tmp_except on true where address << network 
LOOP 
    FOR x IN SELECT * FROM split_cidr(r, z) 
    LOOP RETURN NEXT x; 
    END LOOP; 
END LOOP; 
END $$; 

avrei affrontare il caso di più indirizzi di eccezione per rete modificando split_cidr prendere una matrice di indirizzi di eccezione piuttosto che un singolo indirizzo di eccezione, e quindi aggregando le eccezioni per ogni rete in un array e chiamando split_cidr_array per la rete e la sua serie di eccezioni.