使用Python利用SSH控制pickering的LXI設備

SSH解釋

百度百科解釋:

SSH 爲 Secure Shell 的縮寫,由 IETF 的網絡小組(Network Working Group)所制定;SSH 爲建立在應用層基礎上的安全協議。SSH 是目前較可靠,專爲遠程登錄會話和其他網絡服務提供安全性的協議。利用 SSH 協議可以有效防止遠程管理過程中的信息泄露問題。SSH最初是UNIX系統上的一個程序,後來又迅速擴展到其他操作平臺。SSH在正確使用時可彌補網絡中的漏洞。SSH客戶端適用於多種平臺。幾乎所有UNIX平臺—包括HP-UX、Linux、AIX、Solaris、Digital UNIX、Irix,以及其他平臺,都可運行SSH。

傳統的網絡服務程序,如:ftp、pop和telnet在本質上都是不安全的,因爲它們在網絡上用明文傳送口令和數據,別有用心的人非常容易就可以截獲這些口令和數據。而且,這些服務程序的安全驗證方式也是有其弱點的, 就是很容易受到“中間人”(man-in-the-middle)這種方式的攻擊。所謂“中間人”的攻擊方式, 就是“中間人”冒充真正的服務器接收你傳給服務器的數據,然後再冒充你把數據傳給真正的服務器。服務器和你之間的數據傳送被“中間人”一轉手做了手腳之後,就會出現很嚴重的問題。通過使用SSH,你可以把所有傳輸的數據進行加密,這樣"中間人"這種攻擊方式就不可能實現了,而且也能夠防止DNS欺騙和IP欺騙。使用SSH,還有一個額外的好處就是傳輸的數據是經過壓縮的,所以可以加快傳輸的速度。SSH有很多功能,它既可以代替Telnet,又可以爲FTP、PoP、甚至爲PPP提供一個安全的"通道"

python中的SSH

Python中的paramiko包提供了便捷的ssh連接功能。官網連接如下,功能比較全,官方API解釋比較詳細。
http://www.paramiko.org/
Pickering的LXI設備提供使用SSH控制的方式,其開啓SSH服務的指導連接如下:
https://www.pickeringtest.com/zh-cn/kb/hardware-topics/lxi-ethernet-specific-pages/information-pickering-lxi-products/ssh-control-of-lxi-devices
其原理爲在ssh連上以後,系統會在shell中打開一個程序,用戶直接在面板中輸入相應的命令,實現對設備的控制。
常規的一個簡單的paramiko包的應用如下:

import paramiko
 
# 實例化SSHClient
client = paramiko.SSHClient()
 
# 自動添加策略,保存服務器的主機名和密鑰信息,如果不添加,那麼不再本地know_hosts文件中記錄的主機將無法連接
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
# 連接SSH服務端,以用戶名和密碼進行認證
client.connect(hostname='192.168.1.105', port=22, username='root', password='123456')
 
# 打開一個Channel並執行命令
stdin, stdout, stderr = client.exec_command('df -h ')  # stdout 爲正確輸出,stderr爲錯誤輸出,同時是有1個變量有值
 
# 打印執行結果
print(stdout.read().decode('utf-8'))
 
# 關閉SSHClient
client.close()

但是在使用的過程中,發現提交命令後在stdout.read()這步程序會鎖住,沒辦法讀取到返回值。經過長時間的嘗試,是因爲在連接到設備後,設備開始執行設備控制程序,此時paramiko認爲命令一直在執行,所以一直在等到返回值,其通道沒有關閉,所以就卡在了read上邊。
經過思考,發現其實在連接設備以後,我們需要的並不是執行系統命令,而是只需要往shell中發送字符串就可以,於是有了接下來的程序,這個程序在連接到設備後,不是執行命令,而是建立一個channel,然後發送字符串和接收輸出。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author:Frank

import paramiko,time



class PiSSH():
    def __init__(self,host:str='',port:int = 22,username:str = '',password:str = '',timeout:float =1.0):
        '''
        :param host:
        :param port:
        :param username:
        :param password:
        :param timeout: 該參數是連接ssh時的超時設置,單位爲秒
        '''
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.timeout = timeout               #該參數是連接ssh時的超時設置,單位爲秒
        self.channel = None
        self.ssh_client = None
        self.read_recv_time_delay = 0.05      #發送指令後回讀的延時,若send函數的回讀值不完整適當調大該參數,單位是秒


    def __new__(cls, *args, **kwargs):
        '''
        單例模式,保證只有一個連接
        :param args:
        :param kwargs:
        :return:
        '''
        if not hasattr(PiSSH,'_instance'):
            cls._instance = super(PiSSH, cls).__new__(cls)
        return cls._instance




    def _connect(self):
        '''
        連接sshhost,並且開啓一個shell的channel,用於信息的收發,相當於直接在遠程機上打開shell,
        一般在實例化對象之後,只要不銷燬,就只會連接一次,若連接超時或者突然斷開連接會拋出異常
        :return:
        '''
        if not self._connected():

            self.ssh_client = paramiko.SSHClient()

            self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy)
            self.ssh_client.connect(hostname = self.host,
                                    port = self.port,
                                    username= self.username,
                                    password = self.password,
                                    timeout = self.timeout
                                    )
            self.channel = self.ssh_client.invoke_shell()



    def _connected(self):
        '''
        檢測是否已連接
        :return:
        '''
        if self.channel and self.ssh_client.get_transport():
            if self.ssh_client.get_transport().is_active():
                return True
            else:
                return False
        return False


    def send(self,command:str) -> str:
        '''
        發送指令,返回指令執行結果的字符串
        :param command:
        :return:
        '''
        self._connect()
        self.channel.send(command+'\n')
        time.sleep(self.read_recv_time_delay)
        result =''
        while self.channel.recv_ready():
            result += self.channel.recv(1024).decode('utf-8')

        return result


    def __del__(self):
        '''
        銷燬的時候自動關閉連接
        :return:
        '''
        if self._connected():
            self.channel.close()
            self.ssh_client.close()

使用這個類進行驗證:

from pissh import PiSSH

host = '169.254.217.14'    #設置爲機箱IP
username = 'sshuser'
password = '123456'         #需要開啓機箱的SSH功能,
port = 22

pi =PiSSH(host=host,port=port,username=username,password=password,timeout=1)  #實例化
pi.read_recv_time_delay =0.05                    #設置回讀延時,若回讀不完整可以調大,目前默認0.05秒,我在我的電腦上測試沒問題,默認就是0.05s

if __name__ == '__main__':

    while True:
        command = input('請輸入指令》》').strip().upper()
        if command == 'Q':
            break    #按q退出
        res = pi.send(command)
        print(res)

可以正確的執行指令,測試成功

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章