通过fabric 调用执行需要人机交互,输入确认信息时,一般有两种方案:
1、fabric 自身 with settings(prompts=prompt_restore)。
2、pexpect组件解决。
这里的fexpect,其实是对pexpect的包装。只是使用方式上与fabric 配搭起来更加灵活。其主要源码如下:
1、ilogue/fexpect/api.py #################################### import fabric.api from ilogue.fexpect.internals import wrapExpectations, wrapExpectationsLocal, ExpectationContext def expect(promptexpr, response, exitAfter=-1): if not exitAfter == -1: return [(promptexpr, response, exitAfter)] return [(promptexpr, response)] # Use inside an expect(), like: `expect('>>>', controlchar('D'))` def controlchar(char): char = char.lower() a = ord(char) if a >= 97 and a <= 122: a = a - ord('a') + 1 return chr(a) d = {'@': 0, '`': 0, '[': 27, '{': 27, '\': 28, '|': 28, ']': 29, '}': 29, '^': 30, '~': 30, '_': 31, '?': 127} if char not in d: return 0 return chr(d[char]) def expecting(e): return ExpectationContext(e) def run(cmd, **kwargs): #run wrapper if 'expectations' in fabric.state.env and len(fabric.state.env.expectations) > 0: cmd = wrapExpectations(cmd) return fabric.api.run(cmd, **kwargs) def sudo(cmd, **kwargs): #sudo wrapper if 'expectations' in fabric.state.env and len(fabric.state.env.expectations) > 0: cmd = wrapExpectations(cmd) return fabric.api.sudo(cmd, **kwargs) def local(cmd, **kwargs): #local wrapper if 'expectations' in fabric.state.env and len(fabric.state.env.expectations) > 0: cmd = wrapExpectationsLocal(cmd) return fabric.api.local(cmd, **kwargs) 2、ilogue/fexpect/internals.py ##################################### import shortuuid from StringIO import StringIO import fabric class ExpectationContext(object): def __init__(self,expectations): self.expectations = expectations def __enter__(self): fabric.state.env.expectations = self.expectations def __exit__(self, type, value, tb): fabric.state.env.expectations = [] def wrapExpectations(cmd): script = createScript(cmd) remoteScript = '/tmp/fexpect_'+shortuuid.uuid() import pexpect pexpect_module = pexpect.__file__ if pexpect_module.endswith('.pyc'): pexpect_module = pexpect_module[:-1] # If mode not set explicitly, and this is run as a privileged user, # later command from an unpriviliged user will fail due to the permissions # on /tmp/pexpect.py fabric.api.put(pexpect_module,'/tmp/', mode=0777) fabric.api.put(StringIO(script),remoteScript) wrappedCmd = 'python '+remoteScript return wrappedCmd def wrapExpectationsLocal(cmd): script = createScript(cmd) remoteScript = '/tmp/fexpect_'+shortuuid.uuid() with open(remoteScript, 'w') as filehandle: filehandle.write(script) wrappedCmd = 'python '+remoteScript return wrappedCmd def createScript(cmd): useShell =fabric.state.env.shell to = 30*60 # readline timeout 8 hours #write header: s = '#!/usr/bin/python ' s+= 'import sys ' s+= 'from time import sleep ' s+= 'import pexpect ' #write expectation list: s+= 'expectations=[' for e in fabric.state.env.expectations: s+= '"{0}",'.format(e[0]) s+= '] ' #start spwnTem = """child = pexpect.spawn("""{shellPrefix}{shell} "{cmd}" """,timeout={to}) """ s+= spwnTem.format(shell=useShell,cmd=cmd,to=to,shellPrefix=('' if useShell.startswith('/') else '/bin/')) s+= "child.logfile = sys.stdout " s+= "while True: " s+= " try: " s+= " i = child.expect(expectations) " i = 0 for e in fabric.state.env.expectations: ifkeyw = 'if' if i == 0 else 'elif' s+= " {0} i == {1}: ".format(ifkeyw,i) s+= " child.sendline('{0}') ".format(e[1]) s+= " expectations[i]="__MANGLE__" " if len(e)>2: s+= " sleep({0}) ".format(e[2]) s+= " print('Exiting fexpect for expected exit.') " s+= ' break ' i += 1 s+= ' except pexpect.EOF: ' s+= " print('Exiting fexpect for EOF.') " s+= ' break ' s+= ' ' return s