背景
工作中經(jīng)常需要ssh到服務(wù)器上去操作彻秆,并且有些時候可能還會需要如下的情況:
- 通過跳板機(jī)登錄到具體的應(yīng)用服務(wù)器
- 用普通賬號登錄后再切換到admin用戶操作
困惑
- 登錄服務(wù)器需要記住一堆的服務(wù)器ip讨韭,目前這個已經(jīng)有很多的解決方案了
- 如果在ssh連接建立之后進(jìn)行更復(fù)雜的交互邏輯块茁,這個一直是個困惑。
解決問題
expect
expect使用比較廣泛辫狼,網(wǎng)上的教程也很多初斑,做一些簡單的當(dāng)前用戶操作完全沒有問題
#!/usr/bin/expect
# 參數(shù)1:遠(yuǎn)程host;參數(shù)2:遠(yuǎn)程password
if { $argc != 1 } {
send_user "Usage: host\n"
exit
}
set host [lindex $argv 0]
set USER xxx
set PASSWORD xxx
# Start the session
catch {spawn ssh -l $USER $host}
expect {
"*assword*"
{send "$PASSWORD\n"}
}
expect "Last*" { interact }
但是有個致命的問題膨处,不能解決上述我說的第二點(diǎn)問題见秤,不能有ssh嵌套或者切換用戶。
pexpect
后來很有幸發(fā)現(xiàn)了python的工具包:https://github.com/pexpect/pexpect 真椿; 發(fā)現(xiàn)可以擴(kuò)展的空間非常大
#!/usr/bin/env python3
import pexpect
import sys
# print(sys.argv[1])
host = sys.argv[1]
user = sys.argv[2]
password = sys.argv[3]
child = pexpect.spawn('ssh %s@%s' % (user, host))
# 設(shè)置字符窗口大小鹃答,非常重要,不然會有換行和覆蓋問題
child.setwinsize(400,400)
index = child.expect(['password', user, pexpect.EOF, pexpect.TIMEOUT])
# print("index %d" % (index) )
if index == 0:
child.sendline(password)
elif index == 1:
child.sendline(password)
elif index == 2:
print("EOF")
# child.sendline("\n")
elif index == 3:
print("timeout")
# child.sendline("\n")
# Send the characters pass123 and "enter"
child.expect(user, timeout=120)
child.sendline('cd /home/admin')
child.sendline('sudo su admin')
# child.expect('[sudo]')
child.sendline(password)
child.interact()
完全根據(jù)輸入突硝,輸出的字符來匹配测摔,調(diào)試起來非常方便基公。
workflow
由于平時workflow用的比較多障癌,所以順便把整個流程在alfred里玩下:
服務(wù)器ip列表查詢功能
#!/usr/bin/python
# encoding: utf-8
import sys
from workflow import Workflow
def main(wf):
# The Workflow instance will be passed to the function
# you call from `Workflow.run`. Not so useful, as
# the `wf` object created in `if __name__ ...` below is global.
#
# Your imports go here if you want to catch import errors (not a bad idea)
# or if the modules/packages are in a directory added via `Workflow(libraries=...)`
# import somemodule
# import anothermodule
# Get args from Workflow, already in normalized Unicode
# args = wf.args
# print args
query = None # Ensure `query` is initialised
if len(wf.args):
query = wf.args[0]
ipList = []
with open('iplist.txt') as fileObj:
for line in fileObj:
ipList.append(line.strip())
if query :
ipList = wf.filter(query,ipList, match_on=32)
if len(ipList) == 0 :
ipList.append(query)
for ip in ipList:
wf.add_item(title=ip, arg=ip, autocomplete=ip, valid=True)
# Send output to Alfred. You can only call this once.
# Well, you *can* call it multiple times, but Alfred won't be listening
# any more...
wf.send_feedback()
if __name__ == '__main__':
# Create a global `Workflow` object
wf = Workflow()
# Call your entry function via `Workflow.run()` to enable its helper
# functions, like exception catching, ARGV normalization, magic
# arguments etc.
# wf.run(main)
sys.exit(wf.run(main))
調(diào)用iterm2 啟動 pexpect
on alfred_script(q)
tell application "iTerm"
try
tell current window
create tab with default profile
end tell
on error msg
display dialog msg
create window with default profile
tell current window
create tab with default profile
end tell
end try
tell current tab of current window
select
tell the current session
write text "sussh " & q
end tell
end tell
end tell
end alfred_script
image.png