robotframework-sshlibrary简介、关键字讲解、Read_until_xxx代码分析和实用关键字send_command的封装

南宫浩皛
2023-12-01

目录

简介

关键字讲解

Read_until_xxx代码分析

关键字send_command()封装


简介

Robot framework有三方库SSHLibrary,支持使用ssh远程登录设备执行命令,支持使用SFTP传输文件。

SSHlibray是在paramiko的基础上封装的。

pip install robotframework-sshlibrary进行安装。

SSHlibrary主要封装:对连接的控制。对读写操作进行封装,比如等待prompt、拼接output。输入、输出编码控制。提供输入prompt delimitor的接口。控制log。

SSHlibrary没有像netmiko一样针对网络设备进行额外的封装。包括:设置terminor_width。控制分页、区分处理show命令和config命令、处理backspace、enable的支持。也不支持prompt的自动发现。需要在使用Read Until Prompt之前自己设置,比如:

Library   SSHLibrary    prompt=REGEXP:#|\\$

这里可以看到,prompt可以是一个正则表达式,以“REGEXP:”开头表示是正则表达式,否则认为是普通的字符串。

SSHlibrary提供从输出中剥除command、剥除prompt的功能。

Netmiko处理输入输出换行的不同表示方式,这个SSHLibrary也支持。

关键字讲解

SSHLibrary的有34个关键字,关键字定义在library.py中的class SSHLibrary中。

常用的关键字有Open Connection、Login、Write、Read Until Regexp、Read Until Prompt、Close All Connections、Close Connection。

Open Connection returns the index of the new connection which can be used later to switch back to it. Indices start from 1.  可以指定host、port、prompt、timeout、encoding、newline、height、width。默认newline是LF,即\n,这个参数用于Write函数,如果远端系统是windows,要设置为\r\n。timeout用于指定wait until prompt和Read Until Regexp的超时时间,默认值是3秒。encoding默认是utf-8。DEFAULT_TERM_WIDTH = 80。DEFAULT_TERM_HEIGHT = 24。

open_connection()只是生成了paramiko.SSHClient()的实例client,然后向RF进行了注册,没有真的进行连接。

login()从pythonclient.py中可以看到其实就是调用paramiko.SSHClient的实例client的connect(),真正进行了连接,同时进行登录。参数look_for_keys设置为True时,则从~/.ssh/中查找key(私钥)文件,进行密钥/免密码登录。参数look_for_keys的默认值为false,即使用参数username、password提供的值进行密码登录。参数keep_alive_interval设置发送keep_alive的间隔,默认不发送keep_alive。

login_with_public_key()相比于login(),多提供了keyfile参数,用于指定keyfile(私钥)的路径。

Write在当前shell中执行一条命令。Paramiko没有提供read_until(),SSHLibrary通过读取和分析output来实现Read Until Prompt 和Read Until Regexp。可以使用Write和Read Until Prompt来实现在当前shell中的执行一条命令。特殊的情况使用Write和Read Until Regexp实现命令执行。

例如:

SSHLibrary.Write         sudo virsh console ${vmname} --force

#提示的内容为“Escape character is ^]”

SSHLibrary.Read Until Regexp     Escape\\scharacter\\sis\\s\\^\\]

再比如:SSHLibrary.Read Until Regexp     Password:

Read Until Regexp、Read Until Prompt读取到的内容不包含执行的命令,但是包含prompt。

Close All Connections关闭所有连接。

close_connection关闭当前连接。

其它关键字:

set_client_configuration设置/修改客户端端参数。包括了prompt、timeout、encoding、newline、height、width等。

使用Switch Connection利用index进行会话切换。

Start Command在后台执行一条命令,然后立刻返回。如果要获取输出需要使用Read Command Output。

Excecute Command是在一个新的子shell中执行命令,等到output就返回。注意它是在一个新的子shell中执行命令。Execute Command返回的output不包含执行的command,但是包含命令执行完成后的prompt。Excecute Command就是先调用Start Command,然后调用Read Command Output。

Excecute Command提供一个参数output_during_execution,默认值为False。如果值修改为True,可以在命令执行过程中打印输出。适用情况:执行需要很长时间,不断有的输出命令。比如系统升级、程序安装的命令。

Read_until_xxx代码分析

类SSHLibrary中的read_until_prompt()和read_until_regexp(),分别调用类AbstractSSHClient中的read_until_prompt()和read_until_regexp()。

AbstractSSHClient.read_until_prompt()中有

       if self.config.prompt.startswith('REGEXP:'):

              #偏移量为7是为了跳过开头的“REGEXP:”

              output = self.read_until_regexp(self.config.prompt[7:])

       else:

              output = self.read_until(self.config.prompt)

read_until_regexp()和read_until()其实都是调用_read_until(self, matcher, expected, timeout=None),只是传入的matcher不同,前者的matcher是lambda s: regexp.search(s),后者的matcher是lambda s: expected in s。

_read_until()中调用read_char()逐个读取字符,没有读到字符时,time.sleep(.00001),即sleep 10微秒,Release GIL so paramiko I/O thread can run。read_char()调用paramiko/channel.py中类Channel中的recv()。

主要逻辑就在_read_until()中,下面是其核心代码:

       max_time = time.time() + timeout.value

       while time.time() < max_time:

              char = self.read_char()

              if not char:

                     time.sleep(.00001)

              output += char

              if matcher(output):

                     return output

       raise SSHClientException("No match found for '%s' in %s\nOutput:\n%s."

                                                  % (expected, timeout, output))

这里没有读到字符时,也会拼接output,也会执行matcher(),可以改写为效率更高的方式:

       if char:

              output += char

              if matcher(output):

                     return output

       else:

              time.sleep(.00001)

      

关键字send_command()封装

可以封装出一个能适应很多种使用场景的关键字execute_command,在当前shell中执行命令。使用write执行命令。默认使用Read Until Prompt等待执行完成,提供一个可选参数regexp,如果设置了该参数则使用Read Until Regexp等待执行完成。有的命令执行后没有输出,比如要重启一台设备,先执行命令reload设备,然后输入y进行确认,之后就没有输出了。针对这种场景再添加参数wait_for_completion,默认值为True,如果命令没有输出,则将参数设置为False。还有一些特殊的场景,命令尾部没有换行,这个时候可以将参数no_newline设置位True。封装举例:

# s: SSHLibrary instance

# log是logging.getLogger()返回的logger。

def send_command(s, log, command, regexp="", wait_for_completion=True, no_newline=False):
    log.info(command)
    #Clear buffer
    unused= s.read()
    #Send Command
    if not no_newline:
        s.write(command)
    else:
        s.write_bare(command)
    if not wait_for_completion:
        return
	#Wait for execute completion.
    if not regexp:
        output = s.read_until_prompt()
    else:
        output = s.read_until_regexp(regexp)
    log.info(output)
    return output
#批量执行一组命令。
def send_command_set(s, log, *commands):
    output = ""
    for command in commands:
        output += send_command(s, log, command)
    return output

说明:这里实现成了python函数,如果在RF框架中使用,则可以修改成robot关键字。

使用示意:

from SSHLibrary import SSHLibrary

s = SSHLibrary()

s.open_connection("10.3.253.108",prompt= "REGEXP:#|\\$")

s.login("username","password")

#执行多条命令,注意:这里没有列上logger实例log的获取,大家根据自己的环境需要设置和生成logger实例。

send_command_set(s, log, "yum clean all","yum makecache")

send_command(s, log, "admin save")

send_command(s, log, "admin reboot upgrade", regexp=r"\(y/n\)\?")

# 输入"y"后,设备就重启了,所以没有输出了。

send_command(s, log, "y", wait_for_completion=False)

s.close_connection()

 类似资料: