작성일: 2023년 12월 12일
Juniper Switch를 원격에서 제어할 수 있는 API를 찾아보니 아래와 같은 것들이 있었다.
- Python PKG (Juniper가 공식적으로 개발 및 배포하고 Example이 다양하게 많아서 사용하기 좋았다.)
- Ansible Module (Juniper가 공시적으로 배포하지만, 실전에서 사용하기에는 Example이 부족했다)
- SNMP (Juniper가 SNMP 서비스를 제공하지만, 제약이 많다)
- NET-CONF (업계 표준이니까 제공해주지만, 쌩코딩해서 사용하기에는 시간적으로 부담이 된다)
구현 시간을 아끼고, 예제도 많은 Python PKG를 선택해서 개발하기로 마음을 굳혔다.
그리고 나중에 시간이 생기면 Ansible playbook을 작성해서 조금 더 추상화해볼 계획이다.
참고:
아래의 예제는 [ Junos PyEX 개발자 가이드 ] 문서 중에서 업무에 필요한 부분만 추려서 실습하고 작성한 것이다.
시간이 넉넉한 사람은 아래 개발자 가이드를 다 읽어보고 Junos PyEX를 사용하고,
시간이 넉넉하지 않은 사람은 아래 요약된 실습 내용만 따라하면 된다.
https://www.juniper.net/documentation/kr/ko/software/junos-pyez/junos-pyez-developer/index.html
준비 작업 / Juniper Switch 장비 설정 작업
Client 장비의 Junos PyEX가 접근할 수 있도록 Juniper Switch에서는 [ SSH 서비스, NETCONF, Junos XML API ]를 활성화(Enable)해야 한다.
설명은 거창하지만, 아래의 예제 명령을 한줄 입력해주면 끝이다.
> configure // 설정 모드로 진입하기 위한 명령을 입력
Entering configuration mode
Users currently editing the configuration:
... 중간 생략 ...
myuser# set netconf ssh // NETCONF-over-SSH 서비스를 활성화하는 명령을 입력
myuser# commit // 위에서 변경한 내용을 시스템에 반영하기 위한 명령을 입력
Junos PyEX 패키지(jnpr.junos)의 각 모듈에 대한 설명
Module |
Description |
device |
Junos 디바이스를 표현하는 클래스를 정의하고, Device spec 및 상태를 조회할 수 있는 기능을 지원한다. |
command |
CLI, vty 명령을 지원한다. 표 형태의 View 출력 Form을 지원한다. |
exception |
Exception handling |
factory |
사용자가 지정한 형태의 Table, View 출력이 가능하도록 한다. 예: loadyaml() 함수 |
facts |
Device에 대한 정보를 추출하기 위한 Object |
op |
RPC 응답의 XML 출력을 Filtering |
resources |
... 설명 생략 ... |
transport |
... 설명 생략 ... |
utils |
Configuration utility, Shell utility, Software installation, Security utility, etc ... |
준비 작업 / Client 장비에 Python 패키지 설치 작업
내가 사용한 OS: Ubuntu 22.04
Juniper Switch를 관리할 Client 장비(Ubuntu 22.04)에서 아래 명령을 수행하여 Junos PyEX 패키지를 설치한다.
$ sudo pip3 install junos-eznc
작업 끝. (너무 간단하다 ^^)
[ 예제 1 ] 네트워크 포트 Up & Down 예제 프로그램
아래 예제는 Juniper Switch(10.1.1.2) 장치에 접속해서 Giga Ethernet을 Down & Up 상태로 변경하고
Port 상태를 조회하는 기능을 수행한다.
아래와 같이 Python 예제 코드를 작성한다.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
from jnpr.junos.op.ethport import EthPortTable
import time
with Device(host='10.1.1.2', user='myadmin', password='mypasswd') as dev:
## 포트 상태를 Up/Down 상태로 변경하기
with Config(dev, mode='ephemeral') as cu:
cu.load('set interfaces ge-0/0/8 disable', format='set') # Port Down 상태로 변경
#cu.load('delete interfaces ge-0/0/8 disable', format='set') # Port Up 상태로 변경
cu.commit()
time.sleep(5) ## Config를 commit하고도 2~3초 정도 지나야 Port state 값이 반영되므로 5초 정도 sleep 한다.
## 전체 포트 상태 정보를 가져오기
eths = EthPortTable(dev)
eths.get()
for item in eths:
print ("{}: {}".format(item.name, item.oper))
# 1개 포트 정보만 가져오기
eths.get("ge-0/0/8")
# 포트 정보의 모든 컬럼의 내용을 보고 싶다면...
print(eths.values())
for item in eths:
print ("name: {} operating state: {} admin state: {} mac: {} mtu: {}".format(item.name, item.oper, item.admin, item.macaddr, item.mtu))
위에서 작성한 Python 예제 코드를 아래와 같이 실행한다.
$ python3 my_example_1.py
ge-0/0/0: down
ge-0/0/1: down
ge-0/0/2: down
ge-0/0/3: down
ge-0/0/4: down
ge-0/0/5: down
ge-0/0/6: down
ge-0/0/7: down
ge-0/0/8: up <-- 내가 변경한 포트의 상태
ge-0/0/9: down
ge-0/0/10: down
ge-0/0/11: down
ge-0/0/12: down
ge-0/0/13: down
ge-0/0/14: down
ge-0/0/15: down
ge-0/0/16: down
ge-0/0/17: down
ge-0/0/18: down
ge-0/0/19: down
ge-0/0/20: down
ge-0/0/21: down
ge-0/0/22: down
ge-0/0/23: up
[[('oper', 'up'), ('admin', 'up'), ('description', None), ('mtu', 1514), ('link_mode', 'Full-duplex'), ('macaddr', '60:c7:8d:a3:78:74'), ('rx_bytes', '2124'), ('rx_packets', '24'), ('tx_bytes', '586755785'), ('tx_packets', '7013363'), ('running', True), ('present', True)]]
name: ge-0/0/8 operating state: up admin state: up mac: 60:c3:8a:a3:78:74 mtu: 1514
$
[ 예제 2 ] Juniper Switch의 VLAN 별, 포트 별 Learning된 MAC Address List를 조회
아래 예제는 Juniper Switch(10.1.1.2) 장치에 접속해서 Juniper Switch가 MAC Learning한 결과물을 조회한다.
Learning된 MAC Address를 각각의 VLAN 별, 포트별로 표현된다.
조회 결과물이 XML이기 때문에 XML 데이터 포맷을 처리하기 위한 Python 패키지가 필요한다.
$ pip install xmltodict
아래와 같이 Python 예제 코드를 작성한다.
import pprint
import xmltodict
from xml.etree import ElementTree
from jnpr.junos import Device
from jnpr.junos.op.ethernetswitchingtable import EthernetSwitchingTable
with Device(host='10.1.1.2', user='myadmin', password='mypasswd') as dev:
eth_table = dev.rpc.get_ethernet_switching_table_information()
dict_eth_table = xmltodict.parse(ElementTree.tostring(eth_table, method="xml"))
pprint.pprint(dict_eth_table, indent=2)
위에서 작성한 Python 예제 코드를 아래와 같이 실행한다.
$ python3 my_example_2.py
{ 'l2ng-l2ald-rtb-macdb':
{ 'l2ng-l2ald-mac-entry-vlan':
{ '@style': 'brief-rtb',
'l2ng-l2-mac-routing-instance': 'default-switch',
'l2ng-l2-vlan-id': '100',
'l2ng-mac-entry': [
{ 'l2ng-l2-mac-address': '00:03:51:17:07:14',
'l2ng-l2-mac-age': '-',
'l2ng-l2-mac-flags': 'D',
'l2ng-l2-mac-fwd-next-hop': '0',
'l2ng-l2-mac-logical-interface': 'ge-0/0/23.0',
'l2ng-l2-mac-rtr-id': '0',
'l2ng-l2-mac-vlan-name': 'vlan-100'},
{ 'l2ng-l2-mac-address': '00:03:51:20:02:e2',
'l2ng-l2-mac-age': '-',
'l2ng-l2-mac-flags': 'D',
'l2ng-l2-mac-fwd-next-hop': '0',
'l2ng-l2-mac-logical-interface': 'ge-0/0/23.0',
'l2ng-l2-mac-rtr-id': '0',
'l2ng-l2-mac-vlan-name': 'vlan-100'},
... 중간 생략 ...
{ 'l2ng-l2-mac-address': 'fc:34:51:b6:35:eb',
'l2ng-l2-mac-age': '-',
'l2ng-l2-mac-flags': 'D',
'l2ng-l2-mac-fwd-next-hop': '0',
'l2ng-l2-mac-logical-interface': 'ge-0/0/23.0',
'l2ng-l2-mac-rtr-id': '0',
'l2ng-l2-mac-vlan-name': 'vlan-100'},
{ 'l2ng-l2-mac-address': 'fc:34:51:e1:35:ea',
'l2ng-l2-mac-age': '-',
'l2ng-l2-mac-flags': 'D',
'l2ng-l2-mac-fwd-next-hop': '0',
'l2ng-l2-mac-logical-interface': 'ge-0/0/23.0',
'l2ng-l2-mac-rtr-id': '0',
'l2ng-l2-mac-vlan-name': 'vlan-100'}
],
'learnt-mac-count': '59',
'mac-count-global': '59'
}
}
}
$
[ 예제 3 ] Juniper Switch의 장치 정보, Spec 등 조회
아래 예제는 Juniper Switch(10.1.1.2) 장치에 접속해서 Juniper Switch의 장치 정보 및 Spec을 조회한다. (아래 항목을 참고)
- Switch model name
- Switch status
- 기동하여 운영되고 있는 시간 (up time)
- Switch 장비 이중화 정보 (Master, Member, FWDD 등)
- FQDN (domain name)
- Switch 장치의 Hostname
- JunOS 정보 (os name, version)
- etc
아래와 같이 Python 예제 코드를 작성한다.
from pprint import pprint
from jnpr.junos import Device
from jnpr.junos import Device
with Device(host='10.1.1.2', user='myuser', password='mypasswd1234' ) as dev:
pprint(dev.facts)
pprint(dev.facts['version'])
pprint(dev.facts['switch_style'])
위에서 작성한 Python 예제 코드를 아래와 같이 실행한다.
$ python3 my_example_3.py
{'2RE': False,
'HOME': '/var/home/admin',
'RE0': {'last_reboot_reason': '0x1:power cycle/failure',
'mastership_state': 'master',
'model': 'RE-EX2300-24T',
'status': 'OK',
'up_time': '6 days, 23 hours, 50 minutes, 36 seconds'},
'RE1': None,
'RE_hw_mi': False,
'current_re': ['master',
'node',
'fwdd',
'member',
'pfem',
're0',
'fpc0',
'localre'],
... 중간 생략 ...
'version': '20.4R3-S2.6',
'version_info': junos.version_info(major=(20, 4), type=R, minor=3-S2, build=6),
'virtual': False}
'20.4R3-S2.6'
'VLAN_L2NG'
$
[ 예제 4 ] Juniper Switch의 Shell 명령, CLI 명령을 원격으로 실행
아래 예제는 Juniper Switch(10.1.1.2) 장치에 접속해서 Juniper Switch의 shell 명령(CLI 명령)을 실행하는 코드이다.
아래와 같이 Python 예제 코드를 작성한다.
from jnpr.junos import Device
from jnpr.junos.utils.start_shell import StartShell
dev = Device(host='10.1.1.2', user='myadmin', password='mypasswd1234' )
ss = StartShell(dev)
ss.open()
version = ss.run('cli -c "show version | no-more"')
print(version)
ether_tbl = ss.run('cli -c "show ethernet-switching table interface ge-0/0/23 | no-more"')
print(ether_tbl)
ss.close()
위에서 작성한 Python 예제 코드를 아래와 같이 실행한다.
$ python3 my_example_4.py
True, 'cli -c "show version | no-more"\r\r\n
fpc0:\r\n
--------------------------------------------------------------------------\r\n
Model: ex2300-24t\r\n
Junos: 20.4R3-S2.6\r\n
JUNOS OS Kernel 32-bit [20211117.c779bdc_builder_stable_11-204ab]\r\n
... 중간 생략 ...
'
)
(True, 'cli -c "show ethernet-switching table interface ge-0/0/23 | no-more"\r\r\n\r\n
MAC database for interface ge-0/0/23\r\n\r\n
MAC database for interface ge-0/0/23.0\r\n\r\n
MAC flags (S - static MAC, D - dynamic MAC, L - locally learned, P - Persistent static, C - Control MAC\r\n
SE - statistics enabled, NM - non configured MAC, R - remote PE MAC, O - ovsdb MAC)\r\n\r\n\r\n
Ethernet switching table : 61 entries, 61 learned\r\n
Vlan MAC MAC Age Logical NH RTR \r\n
name address flags interface Index ID\r\n
default 00:03:5a:17:07:14 D - ge-0/0/23.0 0 0 \r\n
default 00:03:5a:20:02:e2 D - ge-0/0/23.0 0 0 \r\n
... 중간 생략 ...
default fc:34:5a:e1:35:ea D - ge-0/0/23.0 0 0 \r\n% '
)
$
참고 문서
Junos PyEX 파이썬 패키지를 사용하여 Juniper Switch 구성 정보를 가져오기
Junos PyEX 파이썬 패키지를 사용하여 출력 내용을 구조화하기 (출력할 컬럼을 선별하여 요약본 만들기)