3

システム管理者としての生活をより簡単にするために、python スクリプトを作成しました。このスクリプトのポイントは、Microsoft DHCP サーバーのダンプ ファイルを並べ替えられた CSV ファイルに変換することです。

ここにコードを含めます。あらゆる種類の改善に感謝します。

私の問題

私のスクリプトは、リストのリストを作成します (DHCP 予約ごとに 1 つ)。例えば:

[
  # [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 アドレスのサブリストを自動的に追加し、「未登録」などのコメントを付けたいと思います。

このタスクを完了する方法についてGoogleの検索を開始する方法さえわからないので、助けていただければ幸いです:)

スクリプト

#!/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]
4

3 に答える 3

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 に保持する代わりに、ループが終了するまで一度に 1 つの値を出力します。ループを通過するたびに、予約のリストから実際の値を取得しようとします (dictすばやくアクセスするために a を使用します)。できない場合は、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=>あああああああ
  • value >> 16=>ああああああああああああああああああああああああああああああ。その値 & 255 => BBBBBBBB
  • value >> 8=> AAAAAAAAABBBBBBBBCCCCCCCC。その値 & 255 => CCCCCCCC
  • value & 255=> DDDDDDDD

これは、32 ビット IPv4 アドレスを 4 バイトのリストに変換する多かれ少なかれ標準的な方法です。これらの値をドットで結合すると、通常の「ABCD」アドレス形式になります。

于 2012-11-27T18:00:53.287 に答える
1

ここに計画があります:

  1. すべての IP を に変換しますint。つまり、IP[0]*(256**3) + IP[1]*(256**2) + IP[2]*256 + IP[3]IPv4 用です。
  2. IP_as_int をキーとして、取得したすべての IP を dict に保存します
  3. サブネットのすべてのIP(intとして、range()そのために使用)、およびget()各IPの辞書を反復処理します。None が返された場合get()は、現在の 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

3 番目のステップでは、ネットワーク マスクが必要です。次のように に格納されていると仮定します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も受け入れたい場合は、質問のコードを調整する必要があります。

最後に、このコードは、アドレス タイプの最上位ビットが設定されている限り、任意のネットワーク マスクもサポートします。

編集: 2 つの複雑な行の詳細: 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]、それをネットワーク マスクと AND します。定数である ip のビットをそのまま保存し、可変部分を構成するビットをゼロにします。

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

最大 IP を計算するには、min_ip に格納されているサブネットの IP の定数部分を取得し、すべてのビットが 1 に設定された IP の可変部分を「追加」 (バイナリ OR) します。 nmask と同じサイズの 1 のバイナリ行と、nmask2**int(math.ceil(math.log(nmask, 2))) - 1で 1 に設定されたすべてのビットが 0 になり、ネットワーク マスクのすべての下位 0 が 1 になるように、nmask との XOR。

なぜこのソリューションを?あまり明確ではありませんが、アドレスの種類に自動的に適応するためです。4096 ビット アドレスもサポートする可能性があります。

于 2012-11-27T18:00:16.813 に答える
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']
...
于 2012-11-27T18:22:58.873 に答える