[英]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
=> AAAAAAAvalue >> 16
=> AAAAAAAABBBBBBBB。 該值 & 255 => BBBBBBBBvalue >> 8
=> AAAAAAAABBBBBBBBCCCCCCCC。 該值 & 255 => CCCCCCCCvalue & 255
=> DDDDDDDD這是將 32 位 IPv4 地址轉換為 4 字節列表的或多或少的標准方法。 當您用一個點將這些值連接在一起時,您將獲得正常的“ABCD”地址格式。
這是一個計划:
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]
。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.