目前公司使用多个服务器对外提供服务。其中只有一台服务器有外网带宽,有几台内网业务服务器。这带有两个问题:
针对这两个问题,我们使用SSH/SCP和PEXPECT来解决。
我们使用SSH通过有外网的服务器建立起本地和没有外网的服务器的隧道,之后所有的操作都可以通过这个隧道来进行操作。
首先进行隧道的创建。
ssh -L [bind_address:]tunnelport:host:hostport <SSH hostname>
然后就是通过隧道使用SSH登陆无外网带宽的服务器
ssh -p tunnelport [email protected]
这里使用-p参数,把ssh使用的端口为之前绑定的隧道端口tunnelport。
使用SCP进行文件的传输操作。
scp -P tunnelport src_file [email protected]:dst_file
这里使用-P参数,把SCP使用的端口设置为之前绑定的隧道端口tunnelport。
Pexpect 是一个用来启动子程序并对其进行自动控制的 Python 模块。 Pexpect 可以用来和像 ssh、ftp、passwd、telnet 等命令行程序进行自动交互,方便在工作中实现与命令行交互的自动化。
在做实验的过程主要使用了spawn、sendline和expect三个函数来实现我们的要求。
spawn
class spawn:
def __init__(self,command,args=[],timeout=30,maxread=2000,searchwindowsize=None, logfile=None, cwd=None, env=None)
spawn是Pexpect模块主要的类,用以实现启动子程序,它有丰富的方法与子程序交互从而实现用户对子程序的控制。它主要使用 pty.fork() 生成子进程,并调用 exec() 系列函数执行 command 参数的内容。expect
expect(self, pattern, timeout=-1, searchwindowsize=None)
在参数中: pattern 可以是正则表达式, pexpect.EOF , pexpect.TIMEOUT ,或者由这些元素组成的列表。需要注意的是,当 pattern 的类型是一个列表时,且子程序输出结果中不止一个被匹配成功,则匹配返回的结果是缓冲区中最先出现的那个元素,或者是列表中最左边的元素。使用 timeout 可以指定等待结果的超时时间 ,该时间以秒为单位。当超过预订时间时, expect 匹配到pexpect.TIMEOUT。sendline
这些方法用来向子程序发送命令,会额外在后面多加个回车来模拟操作。与之相关的还有send和sendcontrol两个函数
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pexpect
import os
import time
#cmd需要向命令行输入的命令
#passeword host的密码
def ssh_login (cmd, password):
ssh_newkey = 'Are you sure you want to continue connecting'
new_ssh = pexpect.spawn(cmd)
i = new_ssh.expect([pexpect.TIMEOUT, ssh_newkey, 'password: ']) #登陆的时候会有两种状态
if i == 0: #如果是超时
print 'ERROR!'
print 'SSH could not login. Here is what SSH said:'
print new_ssh.before, new_ssh.after
return None
if i == 1: #如果需要输入信息登陆
new_ssh.sendline ('yes')
i = new_ssh.expect([pexpect.TIMEOUT, 'password: '])
if i == 0:
print 'ERROR!'
print 'SSH could not login. Here is what SSH said:'
print new_ssh.before, new_ssh.after
return None
new_ssh.sendline(password)
return new_ssh
#登出
def ssh_logout(newpexpect):
newpexpect.close()
#等待终端出输入符号
def ssh_wait_prompts(ssh):
ssh.expect([pexpect.EOF, '[$#>]'])
#由于scp需要在没有建立链接的时候需要验证,所以封装一下
def scp_run(cmd, password):
new_scp = ssh_login(cmd, password)
ssh_wait_prompts(new_scp)
ssh_logout(new_scp)
def main ():
#创建隧道,这个需要一直保持,直到不用这个隧道时才能释放
server1_tunnel = ssh_login ("ssh -L 8082:192.168.132.144:22 [email protected]","x")
slb = ssh_login('ssh [email protected]', 'x') #正常登陆服务器
server1 = ssh_login('ssh -p 8082 [email protected]', 'x') #通过隧道登陆服务器
scp_run('scp -P 8082 /home/x/test.py [email protected]:/home/x/tesdt.py', 'x') #通过隧道传送文件
ssh_wait_prompts(server1)
#通过ssh运行一个脚本,删除文件
server1.sendline('/home/x/del.sh')
#测试连通性
ssh_wait_prompts(server1)
server1.sendline('ls')
ssh_wait_prompts(server1)
print server1.before
ssh_logout(server1)
ssh_logout(slb)
ssh_logout(server1_tunnel)
if __name__ == '__main__':
try:
main()
except Exception, e:
print 'fail', str(e)
traceback.print_exc()
os._exit(1)