2013-05-16 47 views
5

Tôi có một danh sách lớn các mạng con trong một mạng được lưu trữ trong bố cục sau. Điều này được sử dụng như một bảng chủ để lưu trữ các tài sản sẽ được sử dụng để tự động dò tìm trạng thái bằng một tập lệnh Python theo các khoảng thời gian đều đặn.PostgreSQL mở rộng cidr thành các địa chỉ riêng lẻ

CREATE TEMP TABLE tmp_networks (
    network cidr PRIMARY KEY 
); 

Cho phép giả định của nó chứa đầy những giá trị này vì lợi ích của cuộc biểu tình:

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

Khi tôi chạy tập lệnh, tập lệnh Python sẽ thực hiện truy vấn sau để xóa mọi lớp phủ:

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

Điều này rất hữu ích, ngoại trừ một vấn đề nhỏ; Tôi cũng có một danh sách các địa chỉ riêng lẻ cần được loại trừ khỏi công việc. Đây cũng là một bảng trong cơ sở dữ liệu:

CREATE TEMP TABLE tmp_except (
    address inet PRIMARY KEY 
); 

Cho phép giả định này có chứa các địa chỉ sau:

  • 10.0.0.100
  • 192.168.0.10

Bây giờ, tôi đã thất bại để tìm một phương pháp tốt để loại bỏ các địa chỉ spesific này khỏi đầu ra của cơ sở dữ liệu. Trong suy nghĩ của tôi, giải pháp sẽ là một cái gì đó như:

  • chọn tất cả các mạng con
  • nếu có địa chỉ ngoại lệ được tìm thấy trong các subnet, chia subnet thành từng miếng nhỏ cho đến khi địa chỉ ngoại lệ duy nhất có thể được loại bỏ và tất cả khác địa chỉ vẫn là

Tôi đã cố gắng điều tra xem điều gì đó như thế này có thể thực hiện trong PostgreSQL thuần túy hay không, nhưng không tìm được cách nào để giải quyết vấn đề này. Bất kỳ con trỏ nào về cách giải quyết vấn đề này?

+0

Kết quả bạn mong đợi từ truy vấn của mình là gì? Có phải luôn luôn * địa chỉ (/ 32) * hoặc là nó * địa chỉ mạng nếu có thể và chỉ địa chỉ, nếu có ngoại lệ *? – Beryllium

+0

Xin chào, tôi ngoại trừ địa chỉ mạng nếu có thể OR/32 nếu không có khối lớn hơn tồn tại. – agnsaft

Trả lời

5

Tôi sẽ tiếp cận điều này với hai chức năng. Hàm đầu tiên lấy một cidr và một địa chỉ ngoại lệ, và trả về một tập hợp các cidrs tương đương với cidr ban đầu trừ đi địa chỉ ngoại lệ. Hàm này hoạt động bằng cách chia tách cidr thành hai nửa, và sau đó đệ quy loại bỏ địa chỉ ngoại lệ khỏi nửa phần còn lại. Thuật toán phức tạp hơn có thể tránh được một số phân tách không cần thiết. Chức năng đơn giản trông giống như sau:

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

Được trang bị chức năng này, người dùng có thể lặp qua danh sách mạng áp dụng cho những mạng có chứa địa chỉ ngoại lệ. Hàm sau tách danh sách các địa chỉ mạng thành các địa chỉ chứa các ngoại lệ và các địa chỉ không có. Những người không được trả lại, những người có chức năng trên được áp dụng. Điều này không giải quyết được trường hợp mạng chứa nhiều hơn địa chỉ ngoại lệ.

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

tôi sẽ tiếp cận với trường hợp nhiều địa chỉ ngoại lệ cho mỗi mạng bằng cách sửa đổi split_cidr để có một loạt các địa chỉ ngoại lệ chứ không phải là một địa chỉ ngoại lệ duy nhất, và sau đó tập hợp các trường hợp ngoại lệ đối với từng mạng vào một mảng và gọi split_cidr_array cho mạng và mảng ngoại lệ của nó.

Các vấn đề liên quan