DHCP Linux + TextFSM
We wpisie Cisco CLI + TextFSM pokazałem jak można wykorzystać bibliotekę textfsm do obrabiania wyników komend na urządzeniach z Cisco IOS. W tym wpisie pokażę jak uniwersalna jest ta biblioteka na przykładzie serwera DHCP postawionego na systemie Linux i wyciągania danych z pliku konfiguracyjnego.
Plik konfiguracyjny serwera ISC-DHCP znajduje się domyślnie w /etc/dhcpd.conf i ma postać:
ddns-update-style none; log-facility local7; subnet 10.99.10.0 netmask 255.255.255.0 { option routers 10.99.10.1; option subnet-mask 255.255.255.0; option broadcast-address 10.99.10.255; option domain-name-servers 10.99.10.100; option ntp-servers 10.99.10.1; option netbios-name-servers 10.99.10.1; option netbios-node-type 2; default-lease-time 86400; max-lease-time 86400; host KOMPUTER_01 {hardware ethernet 28:d2:44:fa:2c:49; fixed-address 10.99.10.11;} #Komputer 01 host KOMPUTER_02 {hardware ethernet 00:CC:D1:38:AC:45; fixed-address 10.99.10.12;} #Komputer 02 host KOMPUTER_03 {hardware ethernet 01:AC:D2:33:AA:41; fixed-address 10.99.10.13;} #Komputer 03 host KOMPUTER_04 {hardware ethernet 44:1E:A1:2F:62:A0; fixed-address 10.99.10.14;} #Komputer 04 host KOMPUTER_05 {hardware ethernet EC:F4:BB:47:0B:0D; fixed-address 10.99.10.15;} #Komputer 05 host KOMPUTER_06 {hardware ethernet f0:de:f1:61:c3:91; fixed-address 10.99.10.16;} #Komputer 06 host KOMPUTER_02 {hardware ethernet 50:7B:9D:04:CD:0D; fixed-address 10.99.10.17;} #Komputer 07 host KOMPUTER_02 {hardware ethernet C8:5B:76:5B:22:05; fixed-address 10.99.10.18;} #Komputer 08 #host KOMPUTER_02 {hardware ethernet 00:CC:D1:38:AC:45; fixed-address 10.99.10.19;} #Komputer 09 } subnet 10.152.187.0 netmask 255.255.255.0 { option routers 10.152.187.1; option subnet-mask 255.255.255.0; option broadcast-address 10.152.187.255; option domain-name-servers 194.168.4.100; option ntp-servers 10.152.187.1; option netbios-name-servers 10.152.187.1; option netbios-node-type 2; default-lease-time 86400; max-lease-time 86400; host KOMPUTER_11 {hardware ethernet 00:22:AA:66:55:9B; fixed-address 10.152.187.2;} #Komputer 11 }
Załóżmy że z tego pliku chcemy wyciągnąć takie informacje jak:
nazwa hosta, adres MAC, adres IP oraz komentarz.
Możemy do tego celu użyć dostępnych w linuxie narzędzi do pracy z tekstem (grep, awk, cut), natomiast dużo łatwiej i szybciej możemy napisać skrypt w Pythonie wykorzystujący bibliotekę textFSM.
Skrypt realizujący to zdanie dhcp.py wygląda następująco:
#!/usr/bin/python import textfsm file = open("/etc/dhcpd.conf","r") text = file.read() file.close() re_table = textfsm.TextFSM(open("dhcp.textfsm")) fsm_results = re_table.ParseText(text) for i in fsm_results: i[1] = i[1].lower() print i[0]+','+i[1]+','+i[2]+','+i[3]
Plik dhcp.textfsm zawierający szablon z wyrażeniami regularnymi ma postać:
Value hostname (\S+) Value mac ([^ ;]+) Value ip ([^ ;]+) Value description (.+) Start ^\s*host\s+${hostname}\s+{hardware\s+ethernet\s+${mac}\S+\s+fixed-address\s+${ip}+\S+\s+#${description} -> Record
Wynik działania powyższego skryptu to:
root@vm01:~# ./dhcp.py KOMPUTER_01,28:d2:44:fa:2c:49,10.99.10.11,Komputer 01 KOMPUTER_02,00:cc:d1:38:ac:45,10.99.10.12,Komputer 02 KOMPUTER_03,01:ac:d2:33:aa:41,10.99.10.13,Komputer 03 KOMPUTER_04,44:1e:a1:2f:62:a0,10.99.10.14,Komputer 04 KOMPUTER_05,ec:f4:bb:47:0b:0d,10.99.10.15,Komputer 05 KOMPUTER_06,f0:de:f1:61:c3:91,10.99.10.16,Komputer 06 KOMPUTER_02,50:7b:9d:04:cd:0d,10.99.10.17,Komputer 07 KOMPUTER_02,c8:5b:76:5b:22:05,10.99.10.18,Komputer 08 KOMPUTER_11,00:22:aa:66:55:9b,10.152.187.2,Komputer 11
Otrzymaliśmy więc to co chcieliśmy, czyli nazwę hosta, adres MAC, adres IP, oraz komentarz. W celach kosmetycznych skrypt wypisuje wyniki na ekranie oddzielone przecinkami, czyli możemy sobie takie wyniki zaimportować np. do Excela.
OMÓWIENIE SKRYPTU dhcp.py
Przejdźmy teraz do omówienia skryptu linijka po linijce.
#!/usr/bin/python —> powoduje uruchomienie pliku jako program w Pythonie
import textfsm —> import biblioteki textfsm
file=open(„/etc/dhcpd.conf”,”r”) —> do otwarcia pliku wykorzystamy wbudowaną funkcję open, plik otwieramy do odczytu “r”
text=file.read() —> funkcja read() odczytuje zawartość pliku i przypisuje całą zawartość do zmiennej text
file.close() —> funkcja zamykająca plik
re_table=textfsm.TextFSM(open(„dhcp.textfsm”)) —> otwarcie pliku szablonu dhcp.textfsm przez textfsm
fsm_results=re_table.ParseText(text) —> wykonanie parsowania tekstu ze zmiennej text i przypisanie wyniku do listy fsm_results
for i in fsm_results: –> petla for, która przetwarza każdy element z listy fsm_results, przetwarzany element jest również listą
i[1] = i[1].lower() —> dla elementu na drugiej pozycji przetwarzanej listy (w tym przypadku jest to adres MAC, który może być zarówno małymi jak i dużymi literami pisany) wykonaj funkcję lower(), czyli zamień duże na małe litery
print i[0]+’,’+i[1]+’,’+i[2]+’,’+i[3] —> wypisanie na ekranie 4 elementów listy (hostname, adres mac, adres IP, komentarz) przedzielonych przecinkami
OMÓWIENIE SZABLONU dhcp.textfsm
W przypadku braku znajomości wyrażeń regularnych tworzenie szablonu dhcp.textfsm najlepiej wykonywać z wykorzystaniem strony regex101.com. Poniżej screen z tej strony z wyrażeniem regularnych użytym w skrypcie dhcp.py:
Z powyższego screena widać, że wszystkie interesujące nas informacje zostały dopasowane prawidłowo
- Group1 – kolor zielony – hostname
- Group2 – kolor czerwony – adres MAC
- Group3 – kolor pomarańczowy – adres IP
- Group4 – kolor fioletowy – komentarz
Jeśli weźmiemy plik dhcp.textfsm:
Value hostname (\S+) Value mac ([^ ;]+) Value ip ([^ ;]+) Value description (.+) Start ^\s*host\s+${hostname}\s+{hardware\s+ethernet\s+${mac}\S+\s+fixed-address\s+${ip}+\S+\s+#${description} -> Record
i podstawimy do wyrażenia regularnego występującego po słowie Start wyrażenia regularne dla odpowiednich wartości, to otrzymamy dokładnie to wyrażenie które zostało użyte w polu REGULAR EXPRESSION na stronie regex101.com:
^\s*host\s+(\S+)\s+{hardware\s+ethernet\s+([^ ;]+)\S+\s+fixed-address\s+([^ ;]+)+\S+\s+#(.+) -> Record
W powyższym wyrażeniu regularnym korzystamy właściwie tylko z kombinacji znaków które występują w każdej linijce (typu: hardware, ethernet, host, fixed-address) oraz z następujących wzorców:
\s+ spacja występująca raz lub więcej razy
\S+ dowolny znak bez spacji występujących raz lub więcej razy
.+ dowolny znak występujący raz lub więcej razy
[^ ;]+ dowolna kombinacja znaków nie zawierająca spacji oraz średnika występując raz lub więcej razy
W pliku dhcp.textfsm po wyrażeniu regularnym w sekcji Start występuje coś takiego: „-> Record”. Oznacza to tyle, że po znalezieniu dopasowania wyniki są zapisywane do tablicy i następuje analiza kolejnej linii. Dzięki temu możemy otrzymać wiele wyników, a nie tak jak we wpisie o Cisco CLI i textFSM tylko jeden.
Strona regex101.com jest bardzo pomocna w debugowaniu przyczyn błędnego wyciągania informacji przez textfsm. Jeśli nasze wyrażenie regularne po wklejeniu go na stronę oraz tekstu, na którym będzie wykonywanie to wyrażenie nie będzie dopasowywało tak jak tego oczekujemy, to tym bardziej nasz skrypt również tego nie dopasuje.