fexpect 源码
通过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\n' s+= 'import sys\n' s+= 'from time import sleep\n' s+= 'import pexpect\n' #write expectation list: s+= 'expectations=[' for e in fabric.state.env.expectations: s+= '"{0}",'.format(e[0]) s+= ']\n' #start spwnTem = """child = pexpect.spawn(\"\"\"{shellPrefix}{shell} "{cmd}" \"\"\",timeout={to})\n""" s+= spwnTem.format(shell=useShell,cmd=cmd,to=to,shellPrefix=('' if useShell.startswith('/') else '/bin/')) s+= "child.logfile = sys.stdout\n" s+= "while True:\n" s+= "\ttry:\n" s+= "\t\ti = child.expect(expectations)\n" i = 0 for e in fabric.state.env.expectations: ifkeyw = 'if' if i == 0 else 'elif' s+= "\t\t{0} i == {1}:\n".format(ifkeyw,i) s+= "\t\t\tchild.sendline('{0}')\n".format(e[1]) s+= "\t\t\texpectations[i]=\"__MANGLE__\"\n\n" if len(e)>2: s+= "\t\t\tsleep({0})\n".format(e[2]) s+= "\t\t\tprint('Exiting fexpect for expected exit.')\n" s+= '\t\t\tbreak\n' i += 1 s+= '\texcept pexpect.EOF:\n' s+= "\t\tprint('Exiting fexpect for EOF.')\n" s+= '\t\tbreak\n' s+= '\n' return s