我创建了一个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]
答案 0 :(得分:2)
我首先创建了一个包含所有空预订的列表,然后使用您开头的非空列表覆盖它:
#!/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
=> AAAAAAAA value >> 16
=> AAAAAAAABBBBBBBB。那个价值& 255 => BBBBBBBB value >> 8
=> AAAAAAAABBBBBBBBCCCCCCCC。那个价值& 255 => CCCCCCCC value & 255
=> DDDDDDDD 这是将32位IPv4地址转换为4字节列表的或多或少的标准方法。当您将这些值与点连接在一起时,您将获得正常的“A.B.C.D”地址格式。
答案 1 :(得分:1)
这是一个计划:
int
中的所有IP。也就是说,IP[0]*(256**3) + IP[1]*(256**2) + IP[2]*256 + IP[3]
代表IPv4。range()
),并get()
为每个IP设置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]
,并将它与网络掩码一起使用:我们保留as-is是ip的位是常量,并且将构成变量部分的位置为零。 / p>
max_ip = min_ip | nmask ^ 2**int(math.ceil(math.log(nmask, 2))) - 1
计算最大ip,我们采用子网的ips的常量部分,存储在min_ip中,并且我们“添加”(二进制OR)ips的可变部分,所有位都设置为1。
这是通过计算与nmask 2**int(math.ceil(math.log(nmask, 2))) - 1
大小相同的二进制行和使用nmask的XOR来完成的,这样nmask中设置为1的所有位都变为0,网络掩码的所有低位0都变为1
为何选择此解决方案?因为即使它不太清楚,它也会自动适应地址类型。它甚至可能支持4096位地址 - 还有更多!
答案 2 :(得分:0)
用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']
...