国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

使用paramiko遠程執行命令、下發文件的實例

2020-01-04 16:33:40
字體:
來源:轉載
供稿:網友

寫部署腳本時,難免涉及到一些遠程執行命令或者傳輸文件。

之前一直使用sh庫,調用sh.ssh遠程執行一些命令,sh.scp傳輸文件,但是實際使用中還是比較麻煩的,光是模擬用戶登陸這一點,還需要單獨定義方法模擬輸入。

感受一下:

from sh import sshPASS = 'xxxx'def ssh_interact(line, stdin):  line = line.strip()  print(line)  if line.endswith('password:'):    stdin.put(PASS)ssh('x.x.x.x', _out=ssh_interact)

來自官方文檔

后來發現paramiko庫更加優雅、便捷,所以準備用pramiko替換掉sh。

之前通過同事了解到,paramiko在遠程執行python腳本時,腳本中的輸出內容可能會通過stderr這個管道輸出出來,所以直接用paramiko的SSHClient類中的exec_command方法執行,通過讀stderr管道中有無輸出來判斷命令是否成功執行的方式是行不通的。所以用更底層一些的Channel類的recv_exit_status方法判斷執行退出碼更好一些。

安裝

可以通過使用pip install paramiko安裝,細節這里不再贅述。

封裝

首先定義幾個異常

# coding: utf-8import os.pathfrom paramiko import SSHClient, AutoAddPolicy, AuthenticationExceptionclass ConnectError(Exception):  """  連接錯誤時拋出的異常  """  passclass RemoteExecError(Exception):  """  遠程執行命令,失敗時拋出的異常  """  passclass SCPError(Exception):  """  遠程下發文件時拋出的異常  """  pass
...class Remote(object):  def __init__(self, host, username, password=None, port=22, key_filename=None):    self.host = host    self.username = username    self.password = password    self.port = port    self.key_filename = key_filename    self._ssh = None  def _connect(self):    self._ssh = SSHClient()    self._ssh.set_missing_host_key_policy(AutoAddPolicy())    try:      if self.key_filename:        self._ssh.connect(self.host, username=self.username, port=self.port, key_filename=self.key_filename)      else:        self._ssh.connect(self.host, username=self.username, password=self.password, port=self.port)    except AuthenticationException:       self._ssh = None      raise ConnectionError('連接失敗,請確認用戶名、密碼、端口或密鑰文件是否有效')    except Exception as e:      self._ssh = None      raise ConnectionError('連接時出現意料外的錯誤:%s' % e)  def get_ssh(self):    if not self._ssh:      self._connect()    return self._ssh

實例化SSHClient類,通過它的connect()方法獲取SSH連接。

需要注意的是,遠程訪問的主機若是第一次連接,屬于未知設備需要認證,通過set_missing_host_key_policy()方法設置一種策略,這里使用的是AutoAddPolicy()。

這里的_connect支持兩種方式登錄,一種是提供主機的用戶名密碼,另一種是通過密鑰文件。在連接時檢查如果指定了密鑰文件則使用這種方式登錄,否則通過用戶名密碼登錄。

_connect()雖然是實際的建立連接的方法,但實際對外接口是get_ssh(),如果已經有建立好的SSH連接直接返回,避免重復建立連接。

class Remote(object):  ...  def ssh(self, cmd, root_password=None, get_pty=False, super=False):    cmd = self._prepare_cmd(cmd, root_password, super)    stdout = self._exec(cmd, get_pty)    return stdout  def _prepare_cmd(self, cmd, root_password=None, super=False):    if self.username != 'root' and super:      if root_password:        cmd = "echo '{}'|su - root -c '{}'".format(root_password, cmd)      else:        cmd = "echo '{}'|sudo -p '' -S su - root -c '{}'".format(self.password, cmd)    return cmd  def _exec(self, cmd, gty_pty=False):    channel = self.get_ssh().get_transport().open_session()    if get_pty:      channel.get_pty()    channel.exec_command(cmd)    stdout = channel.makefile('r', -1).readlines()    stderr = channel.makefile_stderr('r', -1).readlines()    ret_code = channel.recv_exit_status()    if ret_code:      msg = ''.join(stderr) if stderr else ''.join(stdout)      raise RemoteExecError(msg)    return stdout

在遠程執行某些命令時,可能需要管理員權限,這種時候需要做一些判斷,首先判斷登錄提供的用戶名如果不是root,則需要對命令做一些修改。這里的修改有兩種情況,一是,該普通用戶本身就有sudo權限,只需要把執行的命令加到sudo之后執行就可以,還有一種是普通用戶沒有sudo權限,需要通過su先切換到root身份之后再執行,這種情況下需要提供root密碼。

還有一點要注意的是get_pty這個參數,實際在遠程執行sudo命令時,一般主機都會需要通過tty才能執行,通過把get_pty值設置為True,可以模擬tty,但是隨之而來也會有一個問題,如果是遠程執行一個需要長期運行的進程,例如啟動nginx服務,當遠程命令執行后SSH退出之后,此次運行的所有程序也會隨之結束,所以在需要通過遠程命令運行某些服務或程序時,是不能指定get_pty參數的;但同時,如果是普通用戶遠程登錄,是沒有權限執行service命令的。建議的一種方式是修改/etc/sudoers配置文件,注釋掉Defaults requiretty這行。

class Remote(object):  ...  def scp(self, local_file, remote_path):    if not os.path.exists(local_file):      raise SCPError("Local %s isn't exists" % local_file)    if not os.path.isfile(local_file):      raise SCPError("%s is not a File" % local_file)    sftp = self.get_ssh().open_sftp()    try:      sftp.put(local_file, remote_path)    except Exception as e:      raise SCPError(e)

先確認要下發的文件存在,并且是文件不是目錄,如果不是則拋出異常。同時,remote_path需要是遠程主機的文件絕對目錄,例如/tmp/xxx.xxx,而不能是/tmp。

使用

# coding: utf-8from remote_client import RemoteClientrc = RemoteClient('10.1.100.1', 'test', 'test_pass')rc.ssh('whoami')  # [u'test/n']rc.scp('/tmp/test.out', '/tmp/test.out')

總結

相較于sh,paramiko好用的不是一星半點,這里只是提供了一個簡單的封裝,paramiko本身還有很多其他用法,歡迎大家積極討論。

以上只是本人的一點理解,如果有錯誤之處,歡迎指正。

以上這篇使用paramiko遠程執行命令、下發文件的實例就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到python教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 娄底市| 航空| 兴国县| 东莞市| 威海市| 图片| 仁布县| 洛浦县| 大姚县| 北川| 文安县| 霸州市| 望奎县| 永善县| 五河县| 措勤县| 军事| 福建省| 泌阳县| 桂东县| 彭州市| 金堂县| 甘南县| 呼图壁县| 江陵县| 南漳县| 苏尼特左旗| 商丘市| 临潭县| 彰化市| 同德县| 澄迈县| 内乡县| 昔阳县| 金塔县| 黄骅市| 龙州县| 新密市| 柳河县| 台江县| 宣武区|