簡體   English   中英

如何在python子列表中填寫未使用的IP地址?

[英]How do I fill in unused ip addresses in a python sublist?

我創建了一個 python 腳本,試圖讓我作為系統管理員的生活變得更輕松。 此腳本的重點是將 Microsoft DHCP 服務器轉儲文件轉換為已排序的 CSV 文件。

我將在此處包含代碼,並對各種改進表示感謝。

我的問題

我的腳本創建了一個列表列表(每個 dhcp 保留一個)。 例如:

[
  # [DHCP SERVER, IP ADDRESS, MAC ADDRESS, HOSTNAME, DESCRIPTION]
  [server1,172.16.0.120,31872fcefa33,wks120.domain.net,Description of client]
  [server1,172.16.0.125,4791ca3d7279,wks125.domain.net,Description of client]
  [server1,172.16.0.132,6035a71c930c,wks132.domain.net,Description of client]
  ...
]

未使用的 IP 地址未列出。 但我希望我的腳本自動為中間所有未使用的 IP 地址添加子列表,並給他們一個評論,說“未注冊”或其他什么。

我什至不知道如何開始在谷歌上搜索如何完成這項任務,所以任何幫助將不勝感激:)

劇本

#!/usr/bin/python
import sys, shlex
from operator import itemgetter

# Function: get_dhcp_reservations
#
# Extracts a list of ip reservations from a Microsoft DHCP server dump file
# then it stores the processed reservations them in a nested list

def get_dhcp_reservations(dmpFile):

  # Setup empty records list
  records = []

  # Open dump file for reading
  dmpFile = open(dmpFile,"r")

  # Iterate dump file line by line
  for line in dmpFile:

    # Only user lines with the word "reservedip" in it
    if "reservedip" in line:

      # Split the line into fields excluding quoted substrings
      field = shlex.split(line)

      # Create a list of only the required fields
      result = [field[2][1:9], field[7], field[8], field[9], field[10]]

      # Append each new record as a nested list
      records.append(result)

  # Return the rendered data
  return records


# Function: sort_reservations_by_ip
#
# Sorts all records by the IPv4 address field

def sort_reservations_by_ip(records):

  # Temporarily convert dotted IPv4 address to tuples for sorting
  for record in records:
    record[1] = ip2tuple(record[1])

  # Sort sublists by IP address
  records.sort(key=itemgetter(1)) 

  # Convert tuples back to dotted IPv4 addresses
  for record in records:
    record[1] = tuple2ip(record[1])

  return records


# Function: ip2tuple
#
# Split ip address into a tuple of 4 integers (for sorting)

def ip2tuple(address):
  return tuple(int(part) for part in address.split('.'))


# Function: tuple2ip
#
# Converts the tuple of 4 integers back to an dotted IPv4 address

def tuple2ip(address):

  result = ""

  for octet in address:
    result += str(octet)+"."

  return result[0:-1]


# Get DHCP reservations
records = get_dhcp_reservations(sys.argv[1])

# Sort reservations by IP address
records = sort_reservations_by_ip(records)

# Print column headings
print "DHCP Server,Reserved IP,MAC Address,Hostname,Description"

# Print in specified format records
for record in records:
  print record[0]+","+record[1]+",\""+record[2]+"\","+record[3]+","+record[4]

注意:我也嘗試過使用 python socket.inet_ntoa按照本網站其他主題中的建議進行 IPv4 排序,但沒有成功使其正常工作。

轉儲文件示例

每個請求,這里是一些轉儲文件

[Ommited content]

# ======================================================================
#  Start Add ReservedIp to the Scope : 172.16.0.0, Server : server1.domain.net            
# ======================================================================


    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.76 0800278882ae "wks126devlin.domain.net" "Viana (VM)" "BOTH"
    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.118 001e37322202 "WKS18.domain.net" "Kristof (linux)" "BOTH"
    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.132 000d607205a5 "WKS32.domain.net" "Lab PC" "BOTH"
    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.156 338925b532ca "wks56.domain.net" "Test PC" "BOTH"
    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.155 001422a7d474 "WKS55.domain.net" "Liesbeth" "BOTH"
    Dhcp Server \\server1.domain.net Scope 172.16.0.0 Add reservedip 172.16.0.15 0800266cfe31 "xpsystst.domain.net" "Pascal (VM)" "BOTH"

[Ommited content]

我首先創建了一個包含所有空預訂的列表,然后用您開始的非空列表覆蓋它:

#!/usr/bin/env python

reservations = [
    # [DHCP SERVER, IP ADDRESS, MAC ADDRESS, HOSTNAME, DESCRIPTION]
    ['server1','172.16.0.120','31872fcefa33','wks120.domain.net','Description of client'],
    ['server1','172.16.0.125','4791ca3d7279','wks125.domain.net','Description of client'],
    ['server1','172.16.0.132','6035a71c930c','wks132.domain.net','Description of client'],
]

def reservationlist(reservations, serverpattern, addresspattern, hostpattern,
        start, end):
    result = []
    for i in range(start, end + 1):
        result.append([
            serverpattern % i,
            addresspattern % i,
            '[no mac]',
            hostpattern % i,
            'Unregistered'])

    for reservation in reservations:
        index = int(reservation[1].split('.')[3]) - start
        result[index] = reservation

    return result

print reservationlist(
    reservations,
    'server%d',
    '172.16.0.%d',
    'wks%d.domain.net',
    120,
    132)

最終結果如下:

[['server1', '172.16.0.120', '31872fcefa33', 'wks120.domain.net', 'Description of client'],
['server121', '172.16.0.121', '[no mac]', 'wks121.domain.net', 'Unregistered'],
['server122', '172.16.0.122', '[no mac]', 'wks122.domain.net', 'Unregistered'],
['server123', '172.16.0.123', '[no mac]', 'wks123.domain.net', 'Unregistered'],
['server124', '172.16.0.124', '[no mac]', 'wks124.domain.net', 'Unregistered'],
['server1', '172.16.0.125', '4791ca3d7279', 'wks125.domain.net', 'Description of client'],
['server126', '172.16.0.126', '[no mac]', 'wks126.domain.net', 'Unregistered'],
['server127', '172.16.0.127', '[no mac]', 'wks127.domain.net', 'Unregistered'],
['server128', '172.16.0.128', '[no mac]', 'wks128.domain.net', 'Unregistered'],
['server129', '172.16.0.129', '[no mac]', 'wks129.domain.net', 'Unregistered'],
['server130', '172.16.0.130', '[no mac]', 'wks130.domain.net', 'Unregistered'],
['server131', '172.16.0.131', '[no mac]', 'wks131.domain.net', 'Unregistered'],
['server1', '172.16.0.132', '6035a71c930c', 'wks132.domain.net', 'Description of client']]

呸! 我無法自拔。 此版本接受 IP 地址作為起始值和結束值:

#!/usr/bin/env python

reservations = [
    # [DHCP SERVER, IP ADDRESS, MAC ADDRESS, HOSTNAME, DESCRIPTION]
    ['server1','172.16.0.120','31872fcefa33','wks120.domain.net','Description of client'],
    ['server1','172.16.0.125','4791ca3d7279','wks125.domain.net','Description of client'],
    ['server1','172.16.0.132','6035a71c930c','wks132.domain.net','Description of client'],
]

def addr_to_int(address):
    """Convert an IP address to a 32-bit int"""
    a, b, c, d = map(int, address.split('.'))
    return a * 256 * 256 * 256 + b * 256 * 256 + c * 256 + d

def int_to_addr(value):
    """Convert a 32-bit int into a tuple of its IPv4 byte values"""
    return value >> 24, value >> 16 & 255, value >> 8 & 255, value & 255

def reservationlist(reservations, serverpattern, addresspattern, hostpattern,
        start, end):

    reservationdict = dict((addr_to_int(item[1]), item)
            for item in reservations)
    startint = addr_to_int(start)
    endint = addr_to_int(end)
    for i in range(startint, endint + 1):
        try:
            item = reservationdict[i]
        except KeyError:
            addressbytes = int_to_addr(i)
            item = [
                serverpattern.format(*addressbytes),
                addresspattern.format(*addressbytes),
                '[no mac]',
                hostpattern.format(*addressbytes),
                'Unregistered']
        yield item

for entry in reservationlist(
    reservations,
    'server{3}',
    '172.16.{2}.{3}',
    'wks{3}.domain.net',
    '172.16.0.120',
    '172.16.1.132'):
    print entry

此版本使用yield關鍵字將reservationlist()轉換為生成器。 它不是一次將所有值保存在 RAM 中,而是一次只發出一個值,直到循環完成。 對於每次通過循環,它都會嘗試從您的預訂列表中獲取實際值(使用dict進行快速訪問)。 如果不能,它使用string.format方法用 IPv4 地址字節填充字符串模板。

關於地址操作的快速說明

int_to_addr函數采用 32 位 IP 地址,例如:

AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD

並返回 0-255 范圍內的 4 個字節,例如:

AAAAAAAA, BBBBBBBB, CCCCCCCC, DDDDDDDD

在該函數中, >>表示“將值向右旋轉那么多位”,“& 255”表示“僅返回最后 8 位 (128 + 64 + 32 + 16 + 8 + 4 + 2 + 1)” .

因此,如果我們傳入上面的“AAAA...DDDD”數字:

  • value >> 24 => AAAAAAA
  • value >> 16 => AAAAAAAABBBBBBBB。 該值 & 255 => BBBBBBBB
  • value >> 8 => AAAAAAAABBBBBBBBCCCCCCCC。 該值 & 255 => CCCCCCCC
  • value & 255 => DDDDDDDD

這是將 32 位 IPv4 地址轉換為 4 字節列表的或多或少的標准方法。 當您用一個點將這些值連接在一起時,您將獲得正常的“ABCD”地址格式。

這是一個計划:

  1. 將所有 IP 轉換為int 也就是說,對於IP[0]*(256**3) + IP[1]*(256**2) + IP[2]*256 + IP[3]IP[0]*(256**3) + IP[1]*(256**2) + IP[2]*256 + IP[3]
  2. 將您獲得的所有 IP 存儲在 dict 中,以 IP_as_int 作為鍵
  3. 遍歷子網的所有 ip(作為整數,為此使用range() ),並為每個 IP get() dict。 如果get()返回 None,則打印您的默認消息以及當前 IP。 否則,打印返回的列表。

所以,我們計划的第一步:

def ip_tuple2int(ip_tuple):
     return sum(ip_part * 256**(len(ip_tuple) - no) for no, ip_part in enumerate(ip_tuple, start=1))

我們將需要一個函數來稍后打印它們。 假設我們使用:

def ip_int2str(ip_int):
    l = []
    while ip_int > 0:
        l.insert(0, ip_int % 256)
        ip_int /= 256

    if len(l) > 4:
        return socket.inet_ntop(socket.AF_INET6, ''.join(chr(i) for i in l))
    else:
        return '.'.join(str(i) for i in l)

第二步:

d = {}
for rec in records:
    d[ip_tuple2int(ip2tuple(rec[1]))] = rec

第三步,我們需要網絡掩碼。 我們假設它存儲在nmask ,像這樣: nmask = ip_tuple2int(ip2tuple("255.255.254.0")) (是的,這個掩碼是不尋常的,因為最好解決更普遍的問題。

min_ip = d.keys()[0] & nmask
max_ip = min_ip | nmask ^ 2**int(math.ceil(math.log(nmask, 2))) - 1
    # the right-hand of the '|' is just the bitwise inversion of nmask
    # because ~nmask gives a negative number in python

for ip_int in range(min_ip, max_ip + 1):
    row = d.get(ip_int)
    if row:
        print row
    else:
        print [None, ip_int2str(ip_int), None, None, None]

.

所以,這結束了解決方案。 此處提供的代碼支持 IPv4 和 IPv6:已針對這兩種情況對某些輸入進行了測試,使用以下ip2tuple()

def ip2tuple(ip_str):
    try:
        ip_bin = socket.inet_pton(socket.AF_INET, ip_str)
    except socket.error:
        ip_bin = socket.inet_pton(socket.AF_INET6, ip_str)

    return [ord(c) for c in ip_bin]

如果您還想接受 IPv6,您的問題中的代碼仍然需要進行調整。

最后,此代碼還支持任何網絡掩碼,只要設置了地址類型的最高有效位即可。

編輯:更多關於兩個復雜的行:min_ip 和 max_ip

所以,我們有

min_ip = d.keys()[0] & nmask
max_ip = min_ip | nmask ^ 2**int(math.ceil(math.log(nmask, 2))) - 1

計算我們范圍的最小和最大 ip。 讓我們分解它們!

min_ip = d.keys()[0] & nmask

我們在這里取一個任意 ip d.keys()[0] ,並將其與網絡掩碼相加:我們按原樣保留 ip 的常量位,並將構成可變部分的位歸零。

max_ip = min_ip | nmask ^ 2**int(math.ceil(math.log(nmask, 2))) - 1

為了計算最大 ip,我們取存儲在 min_ip 中的子網 ips 的常量部分,然后我們“添加”(二進制或)ips 的可變部分,所有位都設置為 1。這是通過計算一個與 nmask 相同大小的 1 的二進制行2**int(math.ceil(math.log(nmask, 2))) - 1和與 nmask 的異或,以便 nmask 中設置為 1 的所有位變為 0,並且所有網絡掩碼的低階 0 變為 1。

為什么是這個解決方案? 因為即使不太清楚,它也會自動適應地址類型。 它甚至可能支持 4096 位地址——甚至更多!

用 numpy 完成

import numpy as np

reservations = [
    # [DHCP SERVER, IP ADDRESS, MAC ADDRESS, HOSTNAME, DESCRIPTION]
    ['server1','172.16.0.120','31872fcefa33','wks120.domain.net',
    'Description of client'],
    ['server1','172.16.0.125','4791ca3d7279','wks125.domain.net',
    'Description of client'],
    ['server1','172.16.0.132','6035a71c930c','wks132.domain.net',
    'Description of client'],
]
occupied_ip = []
for i in reservations:
    occupied_ip.append(int(i[1][-3:]))
occupied_ip = np.array(occupied_ip)

iplist = np.arange(256)
idx = np.in1d(iplist,occupied_ip)    #Where are the two arrays equual?
idx = np.logical_not(idx)            #Where are they NOT equal
freeip = iplist[idx]

unreserved = []
for i in range(len(freeip)):
    unreserved.append(["server1", "172.16.0."+str(freeip[i]), "Unassigned MAC",
    "unknown domain"])
    print unreserved[i]

生產

....
    ['server1', '172.16.0.117', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.118', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.119', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.121', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.122', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.123', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.124', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.126', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.127', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.128', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.129', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.130', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.131', 'Unassigned MAC', 'unknown domain']
    ['server1', '172.16.0.133', 'Unassigned MAC', 'unknown domain']
...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM