2013-05-16 18 views
5

Mam dużą listę podsieci w sieci przechowywane w następującym układzie. Jest to używane jako tabela główna do przechowywania zasobów, które będą używane do automatycznego sprawdzania statusu przez skrypt w języku Python w regularnych odstępach czasu.Rozszerzenie cidr PostgreSQL na indywidualne adresy

CREATE TEMP TABLE tmp_networks (
    network cidr PRIMARY KEY 
); 

Załóżmy jego wypełniona tych wartości dla dobra demonstracji:

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

Po uruchomieniu skryptu skrypt Python wykona następujące zapytanie, aby usunąć wszystkie nakładki:

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

Działa to świetnie, z wyjątkiem jednego małego problemu; Mam również listę indywidualnych adresów, które powinny być wykluczone z pracy. Jest to również tabela w bazie danych:

CREATE TEMP TABLE tmp_except (
    address inet PRIMARY KEY 
); 

Załóżmy ten zawiera następujące adresy:

  • 10.0.0.100
  • 192.168.0.10

Teraz Zawiodłem znaleźć dobrą metodę usuwania tych spesywnych adresów z danych wyjściowych bazy danych. W moich myślach, rozwiązaniem byłoby coś jak:

  • zaznaczyć wszystkie podsieci
  • jeśli adres wyjątek znajduje się w podsieci, podzielić podsieć na mniejsze kawałki, aż pojedynczy adres Wyjątkiem mogą być usunięte i wszystkie inne adresy pozostają

Próbowałem sprawdzić, czy coś takiego można zrobić w czystym PostgreSQL, ale nie udało się znaleźć żadnego rozwiązania. Jakieś wskazówki, jak to rozwiązać?

+0

Jaki jest wynik, którego oczekujesz od zapytania? Czy zawsze jest * adresy (/ 32) * czy jest to * adresy sieciowe, jeśli to możliwe, i tylko adresy, jeśli istnieje wyjątek *? – Beryllium

+0

Witam, oprócz adresów sieciowych, jeśli to możliwe OR/32, jeśli nie ma większych bloków. – agnsaft

Odpowiedz

5

Podejdę do tego z dwiema funkcjami. Pierwsza funkcja pobiera adres cidr i wyjątek i zwraca zestaw cidrów, które są równoważne oryginalnemu cidr minus adres wyjątku. Funkcja działa poprzez podzielenie cidr na dwie połówki, a następnie rekursywne usunięcie adresu wyjątku z połowy jego zawartości. Bardziej wyrafinowany algorytm może uniknąć niepotrzebnych podziałów. Prosta funkcja wygląda tak:

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 $$; 

Armed z tej funkcji, można następnie iterację liście sieci stosując ją do tych sieci, które zawierały adres wyjątku. Poniższa funkcja dzieli listę adresów sieciowych na te, które zawierają wyjątki, a które nie. Te, które nie są zwracane, te, które mają zastosowaną powyższą funkcję. Nie dotyczy to przypadku, w którym sieć zawiera więcej niż adres wyjątku.

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 $$; 

Chciałbym podejść do sprawy z wielu adresów wyjątków na sieci poprzez modyfikację split_cidr wziąć tablicę adresów raczej wyjątek niż jednego adresu wyjątku, a następnie sumując wyjątki dla każdej sieci w tablicy i nazywając split_cidr_array dla sieć i jej zestaw wyjątków.