본문 바로가기
<개인공부> - IT/[Network&Security]

TextFSM을 사용한 네트워크 장비 출력 파싱 #2

by Aggies '19 2024. 5. 27.
반응형

  앞선 TextFSM의 포스팅 (TextFSM 포스팅 #1)에서는 TextFSM이란 무엇이고 가장 기본적인 CLI 출력 구문에 대한 예제까지 설명했다. 이번에는 조금더 다양한 예제 두 가지를 다뤄보고자 한다.

 

Filldown 예제

  TextFSM을 사용하다보면 Filldown 기능을 많이 사용하게 되는데, 쉽게 말해 이전 행의 값을 현재 행에 자동으로 채워 넣는 기능이다. 즉, 반복적인 데이터 구조를 다룰 때 유용하다.

 

1) Parsing을 하고자 하는 텍스트

Timestamp            Hostname    Status
2023-05-01 12:00:00  Device1     UP
                                Interface0/1  DOWN
                                Interface0/2  UP
2023-05-01 13:00:00  Device2     DOWN
                                Interface1/1  DOWN

 

2) textfsm 템플릿

Value Filldown TIMESTAMP (\d+-\d+-\d+\s+\d+:\d+:\d+)
Value Filldown HOSTNAME (\S+)
Value STATUS (\S+)
Value INTERFACE (\S+\d+/\d+)

Start
  ^${TIMESTAMP}\s+${HOSTNAME}\s+${STATUS} -> Record
  ^\s+${INTERFACE}\s+${STATUS} -> Record

 

이 템플릿은 Filldown을 통해서 TIMESTAMP와 HOSTNAME 값을 이전 행의 값으로 채워넣게 작성했다.

 

3) Parsing Script

import textfsm

# 파싱할 텍스트 데이터
raw_text_data = """
Timestamp            Hostname    Status
2023-05-01 12:00:00  Device1     UP
                                Interface0/1  DOWN
                                Interface0/2  UP
2023-05-01 13:00:00  Device2     DOWN
                                Interface1/1  DOWN
"""

# 템플릿 파일 경로
template_file = 'device_logs.textfsm'

# 템플릿 로드
with open(template_file) as template:
    fsm = textfsm.TextFSM(template)

# 텍스트 파싱
parsed_results = fsm.ParseText(raw_text_data)

# 결과 출력
for row in parsed_results:
    print(f"Timestamp: {row[0]}, Hostname: {row[1]}, Interface: {row[3] if row[3] else 'N/A'}, Status: {row[2]}")

 

해당 스크립트를 실행하고 난 결과물은 아래와 같다.

Timestamp: 2023-05-01 12:00:00, Hostname: Device1, Interface: N/A, Status: UP
Timestamp: 2023-05-01 12:00:00, Hostname: Device1, Interface: Interface0/1, Status: DOWN
Timestamp: 2023-05-01 12:00:00, Hostname: Device1, Interface: Interface0/2, Status: UP
Timestamp: 2023-05-01 13:00:00, Hostname: Device2, Interface: N/A, Status: DOWN
Timestamp: 2023-05-01 13:00:00, Hostname: Device2, Interface: Interface1/1, Status: DOWN

 

Next와 Continue 예제

  템플릿 상태를 변경하거나 매칭된 줄을 스킵하여 다음줄을 처리하는데 사용되는 Next와 Continue에 대한 예제를 알아보자.

  <NEXT 예제>

 

1) Parsing을 하고자 하는 텍스트

Routing Table:
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0           0 eth0

 

2) textfsm 템플릿

Value DESTINATION (\S+)
Value GATEWAY (\S+)
Value GENMASK (\S+)
Value FLAGS (\S+)
Value MSS (\d+)
Value WINDOW (\d+)
Value IRTT (\d+)
Value IFACE (\S+)

Start
  ^Routing Table: -> RouteLines

RouteLines
  ^${DESTINATION}\s+${GATEWAY}\s+${GENMASK}\s+${FLAGS}\s+${MSS}\s+${WINDOW}\s+${IRTT}\s+${IFACE} -> Record
  ^\s*$$ -> Next

 

새로운 구문이 하나 사용되었는데 ^\s*$$는 빈 줄을 만나면 다음 상태로 넘어가라는 의미이다.

 

3) Parsing Script

import textfsm

# 파싱할 텍스트 데이터
raw_text_data = """
Routing Table:
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0           0 eth0
"""

# 템플릿 파일 경로
template_file = 'routing_table.textfsm'

# 템플릿 로드
with open(template_file) as template:
    fsm = textfsm.TextFSM(template)

# 텍스트 파싱
parsed_results = fsm.ParseText(raw_text_data)

# 결과 출력
for row in parsed_results:
    print(f"Destination: {row[0]}, Gateway: {row[1]}, Genmask: {row[2]}, Flags: {row[3]}, MSS: {row[4]}, Window: {row[5]}, irtt: {row[6]}, Iface: {row[7]}")

 

출력 결과는 우리의 예상대로:

Destination: 0.0.0.0, Gateway: 192.168.1.1, Genmask: 0.0.0.0, Flags: UG, MSS: 0, Window: 0, irtt: 0, Iface: eth0
Destination: 192.168.1.0, Gateway: 0.0.0.0, Genmask: 255.255.255.0, Flags: U, MSS: 0, Window: 0, irtt: 0, Iface: eth0

 

  <CONTINUE 예제>

하지만 우리가 일반적으로 맞닥뜨리는 CLI 출력 결과는 보다 복잡한 모습을 띈다.

 

1) Parsing을 하고자 하는 텍스트

Routing Table:
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
                10.0.0.1        255.255.255.0   U         0 0           0 eth1
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0           0 eth0

 

Value DESTINATION (\S+)
Value GATEWAY (\S+)
Value GENMASK (\S+)
Value FLAGS (\S+)
Value MSS (\d+)
Value WINDOW (\d+)
Value IRTT (\d+)
Value IFACE (\S+)

Start
  ^Routing Table: -> RouteLines

RouteLines
  ^${DESTINATION}\s+${GATEWAY}\s+${GENMASK}\s+${FLAGS}\s+${MSS}\s+${WINDOW}\s+${IRTT}\s+${IFACE} -> Record
  ^\s+${GATEWAY}\s+${GENMASK}\s+${FLAGS}\s+${MSS}\s+${WINDOW}\s+${IRTT}\s+${IFACE} -> Continue.RouteLines

 

RouteLines 상태만 보도록 하자. 첫 번째 라인에서 각 필드를 추출하고 (Record), 두 번째 라인에서도 연속된 필드를 추출한다. 즉, Continue.RouteLines라는 action은 상태를 유지하면서 다음 줄을 처리하라는 구문이 된다.

 

3) Parsing Script

import textfsm

# 파싱할 텍스트 데이터
raw_text_data = """
Routing Table:
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth0
                10.0.0.1        255.255.255.0   U         0 0           0 eth1
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0           0 eth0
"""

# 템플릿 파일 경로
template_file = 'complex_routing_table.textfsm'

# 템플릿 로드
with open(template_file) as template:
    fsm = textfsm.TextFSM(template)

# 텍스트 파싱
parsed_results = fsm.ParseText(raw_text_data)

# 결과 출력
for row in parsed_results:
    print(f"Destination: {row[0]}, Gateway: {row[1]}, Genmask: {row[2]}, Flags: {row[3]}, MSS: {row[4]}, Window: {row[5]}, irtt: {row[6]}, Iface: {row[7]}")

 

해당 스크립트의 출력결과는:

Destination: 0.0.0.0, Gateway: 192.168.1.1, Genmask: 0.0.0.0, Flags: UG, MSS: 0, Window: 0, irtt: 0, Iface: eth0
Destination: 0.0.0.0, Gateway: 10.0.0.1, Genmask: 255.255.255.0, Flags: U, MSS: 0, Window: 0, irtt: 0, Iface: eth1
Destination: 192.168.1.0, Gateway: 0.0.0.0, Genmask: 255.255.255.0, Flags: U, MSS: 0, Window: 0, irtt: 0, Iface: eth0

 

결론

  TextFSM을 사용하면 네트워크 장비의 CLI 출력 결과를 손쉽게 파싱할 수 있다. 정규식을 기반으로 한 텍스트 템플릿을 사용하여 원하는 데이터를 추출할 수 있으며, 이는 정규화된 정보를 처리할 수 있기 때문에 네트워크 자동화 작업에 큰 도움이 됩다. Filldown, Next, Continue와 같은 기능을 사용하면 복잡한 텍스트 데이터도 효율적으로 파싱할 수 있으며, 여전히 나도 전문가 처럼 사용하는 것은 아니지만 현업에서 맨땅에 헤딩하던 몇 가지 부분을 정리해보았다. 원하는 정보를 명확하게 추출할 수 있다는 점으로 인해서 굉장히 강력한 도구임에는 틀림없다.

 

참고 링크:

반응형