สอนเขียน Python เพื่อใช้ SSH ด้วย Paramiko และ Netmiko แบบรวบรัด

Write Python for SSH using Paramiko and Netmiko Quickly

Nopnithi Khaokaew
May 27 · 6 min read
Image for post
  1. Paramiko
  2. Netmiko

Code ตัวอย่าง

git clone https://github.com/nopnithi/python_ssh_tutorial.git
pip3 install -r requirements.txt
Image for post

Paramiko

Paramiko คือ SSH library บน Python ที่ค่อนข้างดีและยืดหยุ่นกว่า Netmiko สำหรับ SSH ไปหาอุปกรณ์ปลายทาง หรือจะใช้ transfer ไฟล์ด้วย SFTP ก็ได้

ฟังก์ชั่นของ Paramiko เบื้องต้น

  • set_missing_host_key_policy(paramiko.AutoAddPolicy()) = เพิ่ม SSH key ไปยังไฟล์ known_hosts
  • client.connect() = เริ่ม connect ไปยัง SSH server
  • client.close() = ปิด SSH connection
  • client.invoke_shell() = สร้าง session (มองว่าเหมือนเปิด terminal)
  • session.send() = ส่ง command ผ่าน session จาก invoke_shell()
  • session.recv() = รับ response จาก server ผ่าน session จาก invoke_shell()

ติดตั้ง Paramiko บน Python

pip3 install paramiko

ตัวอย่าง 1: ใช้ Python ในการ SSH ด้วย Paramiko

# python_ssh_tutorial/ssh_paramiko.pyfrom paramiko import SSHClient, AutoAddPolicy
from time import sleep

with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(
hostname='10.1.30.101',
username='nopnithi',
password='P@ssw0rd',
look_for_keys=False
)
session = client.invoke_shell()
session.send('show version | include uptime' + '\n')
sleep(1)
output = session.recv(65535).decode('utf-8')
print(output.strip())
R1#show version | include uptime
R1 uptime is 1 hour, 35 minutes
R1#

ตัวอย่าง 2: ใช้ Python ในการ SSH ด้วย Paramiko (ดัก Prompt)

คราวนี้เป็นอีกตัวอย่างเพื่อให้เห็นถึงความ low level ของมันครับ โดยผมจะเพิ่มฟังก์ชั่นเพื่อดัก prompt ซึ่งถ้าหากไม่เจอ prompt ก็จะไม่ทำงานต่อ

def find_prompt(output):
last_line = output.splitlines()[-1].strip()
if match(r'([\w-]+)(>|#|\(config(?:.*)\)#)', last_line):
return True
return False
Image for post
# python_ssh_tutorial/ssh_paramiko_prompt.pyfrom paramiko import SSHClient, AutoAddPolicy
from time import sleep
from re import match

def find_prompt(output):
last_line = output.splitlines()[-1].strip()
if match(r'([\w-]+)(>|(?:\(config.*\))*#)', last_line):
return True
return False

def
main():
output = ''
with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(
hostname='10.1.30.101',
username='nopnithi',
password='P@ssw0rd',
look_for_keys=False
)
session = client.invoke_shell()
_output = session.recv(65535).decode('utf-8').rstrip()
output += _output
if find_prompt(_output):
print('Got prompt, login is successful')
session.send('show version | include uptime' + '\n')
sleep(1)
_output = session.recv(65535).decode('utf-8').rstrip()
output += _output
if find_prompt(_output):
print('Got prompt, sending command is successful')
print(output.strip())

if __name__ == '__main__':
main()
Got prompt, login is successful
Got prompt, sending command is successful
R1#show version | include uptime
R1 uptime is 22 hours, 44 minutes
R1#

ตัวอย่าง 3: ใช้ Python ในการ SSH ด้วย Paramiko (รอรับ Data จนกว่าจะเจอ Prompt)

เริ่มจากยกส่วนของการรับ data ไปไว้ในฟังก์ชั่นก่อน เพื่อให้ reuse code ได้ง่าย

def get_output(session):
return session.recv(65535).decode('utf-8').rstrip()
def send_command(session, command):
cmd_output = ''
session.send(command + '\n')
sleep(0.3)
while True:
_output = get_output(session)
cmd_output += _output
if find_prompt(_output):
break
return
cmd_output
# python_ssh_tutorial/ssh_paramiko_wait_data.pyfrom paramiko import SSHClient, AutoAddPolicy
from time import sleep
from re import match

def get_output(session):
return session.recv(65535).decode('utf-8').rstrip()

def find_prompt(output):
last_line = output.splitlines()[-1].strip()
if match(r'([\w-]+)(>|(?:\(config.*\))*#)', last_line):
return True
return False


def
send_command(session, command):
cmd_output = ''
session.send(command + '\n')
sleep(0.3)
while True:
_output = get_output(session)
cmd_output += _output
if find_prompt(_output):
break
return
cmd_output

def main():
output = ''
with SSHClient() as client:
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(
hostname='10.1.30.101',
username='nopnithi',
password='P@ssw0rd',
look_for_keys=False
)
session = client.invoke_shell()
_output = get_output(session)
output += _output
if find_prompt(_output):
cmd_output = send_command(session, 'terminal length 0')
output += cmd_output
cmd_output = send_command(session, 'show running-config')
output += cmd_output
print(output.strip())

if __name__ == '__main__':
main()
R1#terminal length 0
R1#show running-config
Building configuration...

Current configuration : 3849 bytes
!
! Last configuration change at 04:46:30 UTC Sat May 30 2020 by nopnithi
!
version 15.6
service timestamps debug datetime msec
service timestamps log datetime msec
no service password-encryption
!
hostname R1
!
boot-start-marker
boot-end-marker
!
!
!
aaa new-model
...
...

Netmiko

มาที่ Netmiko กันบ้าง สำหรับตัวนี้จะเน้นไปที่การ SSH ไปยังอุปกรณ์ network นะครับ แต่จะ support กับอุปกรณ์ใดบ้างก็ลองไปดูใน document ของเค้าได้เลย

ติดตั้ง Netmiko บน Python

pip3 install netmiko

ฟังก์ชั่นของ Netmiko เบื้องต้น

  • ConnectHandler() = สร้าง object ของ connection/session
  • enable() = เข้าสู่ privilege mode
  • config_mode() = เข้าสู่ configuration mode
  • exit_enable_mode() = ออกจาก privilege mode
  • exit_config_mode() = ออกจาก configuration mode
  • send_command() = ส่ง command และ return ค่า output
  • send_config_set() = ส่ง command เป็น list และ return ค่า output
  • disconnect() = ตัด SSH connection

ตัวอย่าง 1: ใช้ Python ในการ SSH ด้วย Netmiko

from netmiko import ConnectHandlerdevice = {
'host': '10.1.30.101',
'username': 'nopnithi',
'password': 'P@ssw0rd',
'secret': 'P@ssw0rd',
'device_type': 'cisco_ios',
'fast_cli': True
}
with ConnectHandler(**device) as net_connect:
output = net_connect.send_command('show version | include uptime')
print(output.strip())
R1 uptime is 46 minutes

เลือกใช้ตัวไหนดีระหว่าง Paramiko กับ Netmiko?

ถ้าอุปกรณ์ปลายทางของเราใช้กับ Netmiko ได้ ผมแนะนำให้ใช้ Netmiko ก่อนเสมอ แต่ถ้าไม่ support ก็ให้กลับมาหา Paramiko ครับ

สรุปส่งท้ายและคำแนะนำเพิ่มเติม

แม้ตัวอย่างที่ผมให้ไปจะค่อนข้าง practical ในระดับนึงแล้ว แต่ถ้าหากมีการนำไปใช้งานจริงใน script หรือ app ของคุณกับ network หรือ system ที่มี scale ใหญ่ ผมขอแนะนำให้เพิ่มเติมเรื่องเหล่านี้

  1. เรื่อง Timeout และ Retry ถ้าหาก code ที่คุณเขียนเป็นมากกว่า Python script สั้น ๆ การสร้างฟังก์ชั่น timeout สำหรับจำกัดเวลาในรอ response และการ retry เพื่อลองอีกครั้งเมื่อเกิดข้อผิดพลาดบางอย่างก็สำคัญไม่แพ้กัน
  2. เรื่อง Concurency ถ้าคุณเขียน Python เพื่อ SSH ไปยังอุปกรณ์ 5 หรือ 10 ตัวก็ไม่น่าจะติดอะไร แต่ถ้าเจอระดับ 50, 100, 1,000 หรือเป็น 20,000 ตัวเนี่ยไม่มีทางใช้ for loop ธรรมดาได้ครับ ดังนั้นเรื่อง concurrency อย่าง multi-threading, multiprocessing หรือ async IO ต้องมาแล้ว
  3. เรื่อง OOP (Object Oriented Programming) จาก code ตัวอย่างที่ผมให้ไปมัน simple มาก ผมก็เลยเขียนเป็นฟังก์ชั่นธรรมดาเพื่อให้เข้าใจได้ง่ายสำหรับคนส่วนใหญ่ แต่ในความเป็นจริงถ้า script เราเริ่มใหญ่ การเขียนเป็น class จะช่วยให้มัน clean และ maintainable มากกว่า

 https://medium.com/@nopnithi/%E0%B8%AA%E0%B8%AD%E0%B8%99%E0%B9%80%E0%B8%82%E0%B8%B5%E0%B8%A2%E0%B8%99-python-%E0%B9%80%E0%B8%9E%E0%B8%B7%E0%B9%88%E0%B8%AD%E0%B9%83%E0%B8%8A%E0%B9%89-ssh-%E0%B8%94%E0%B9%89%E0%B8%A7%E0%B8%A2-paramiko-%E0%B9%81%E0%B8%A5%E0%B8%B0-netmiko-%E0%B9%81%E0%B8%9A%E0%B8%9A%E0%B8%A3%E0%B8%A7%E0%B8%9A%E0%B8%A3%E0%B8%B1%E0%B8%94-c3f1a315801b

ความคิดเห็น

โพสต์ยอดนิยมจากบล็อกนี้

เริ่มพัฒนา Web Application กับภาษา Python ด้วย Django Framework