Ansible ad-hoc 執行流程
背景
Ansible 封裝了很多指令碼,以 Module、Play 的形式呈現,這裡以一條簡單的 shell 命令作為切入點。
在開始前,將目標機的資訊,先寫入 cat /etc/ansible/hosts
中。
9.134.124.159:36000
所用到的命令如下:
ansible all -vvv -a "ls /root" -u root
通過開啟一些 debug 日誌,可以確定執行連線操作時,一定會執行 ansible/lib/ansible/plugins/connection/ssh.py
中的程式碼。
執行步驟
完整的日誌
META: ran handlers
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'echo ~root && sleep 0'"'"''
<9.134.124.159> (0, b'/root\n', b'')
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036 `" && echo ansible-tmp-1599536717.558343-92837-98923700243036="` echo /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036 `" ) && sleep 0'"'"''
<9.134.124.159> (0, b'ansible-tmp-1599536717.558343-92837-98923700243036=/root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036\n', b'')
<9.134.124.159> Attempting python interpreter discovery
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'echo PLATFORM; uname; echo FOUND; command -v '"'"'"'"'"'"'"'"'/usr/bin/python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.5'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/libexec/platform-python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/bin/python3'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python'"'"'"'"'"'"'"'"'; echo ENDFOUND && sleep 0'"'"''
<9.134.124.159> (0, b'PLATFORM\nLinux\nFOUND\n/usr/bin/python\n/usr/bin/python3.6\n/usr/bin/python2.7\n/usr/bin/python2.6\n/usr/libexec/platform-python\n/usr/bin/python3\n/usr/bin/python\nENDFOUND\n', b'')
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
<9.134.124.159> (0, b'{"osrelease_content": "NAME=\\"Tencent tlinux\\"\\nVERSION=\\"2.2 (Final)\\"\\nID=\\"tlinux\\"\\nID_LIKE=\\"rhel fedora centos\\"\\nVERSION_ID=\\"2.2\\"\\nPRETTY_NAME=\\"Tencent tlinux 2.2 (Final)\\"\\nANSI_COLOR=\\"0;31\\"\\nCPE_NAME=\\"cpe:/o:tlinux:linux:2\\"\\nHOME_URL=\\"http://tlinux.oa.com/\\"\\nBUG_REPORT_URL=\\"http://tapd.oa.com/tlinux/bugtrace/bugreports/my_view/\\"\\n\\n", "platform_dist_result": ["centos", "7.2", "Final"]}\n', b'')
Using module file /Users/yangyu/projects/ansible/lib/ansible/modules/command.py
<9.134.124.159> PUT /Users/yangyu/.ansible/tmp/ansible-local-9283485g9ot9_/tmpkr7p4e1t TO /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/AnsiballZ_command.py
<9.134.124.159> SSH: EXEC sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 '[9.134.124.159]'
<9.134.124.159> (0, b'sftp> put /Users/yangyu/.ansible/tmp/ansible-local-9283485g9ot9_/tmpkr7p4e1t /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/AnsiballZ_command.py\n', b'')
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/ /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/AnsiballZ_command.py && sleep 0'"'"''
<9.134.124.159> (0, b'', b'')
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 -tt 9.134.124.159 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/AnsiballZ_command.py && sleep 0'"'"''
<9.134.124.159> (0, b'\r\n{"changed": true, "end": "2020-09-08 11:45:53.183432", "stdout": "11.sh\\n11.zip\\nInstallHalyard.sh\\n[\\nabc.txt\\nagent.zip\\nbefore.txt\\ncloud-agent.log\\nclouddriver-prome.yaml\\nclouddriver.git?token=bd40137b365a6a789b7f5904cff87958ba5b158b\\nclouddriver.log\\ncoding-cd\\ncoding-cd-grpc.yaml\\nconfig_hub_oa_com.sh\\nconfig_vscode_server.sh\\ndemo\\ndemo.yaml\\ndev\\ndevopsAgent\\ndevopsDaemon\\nenable_internet_proxy.sh\\nfile.txt\\nfix_devcloud.logs\\ngeneric-aloe.txt\\nhi.db\\niProxy.sh\\nindex.html\\ninit_data_disk.sh\\ninit_devcloud_remote.sh\\ninstall.sh\\ninstallAgent.sh\\ninstall_ift.sh\\njre\\njre.zip\\nlog.txt\\nlogs\\npost-script.text\\nprome.yaml\\npush_master\\nrevert_image_source.sh\\nruntime\\nset_linux_welcome.sh\\nstart.sh\\nstop.sh\\nszx\\ntelegraf.conf\\nuninstall.sh\\nworker-agent.jar\\nworkspace", "cmd": ["ls", "/root"], "rc": 0, "start": "2020-09-08 11:45:53.178756", "stderr": "", "delta": "0:00:00.004676", "invocation": {"module_args": {"creates": null, "executable": null, "_uses_shell": false, "strip_empty_ends": true, "_raw_params": "ls /root", "removes": null, "argv": null, "warn": false, "chdir": null, "stdin_add_newline": true, "stdin": null}}}\r\n', b'Shared connection to 9.134.124.159 closed.\r\n')
<9.134.124.159> ESTABLISH SSH CONNECTION FOR USER: root
<9.134.124.159> SSH: EXEC ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'rm -f -r /root/.ansible/tmp/ansible-tmp-1599536717.558343-92837-98923700243036/ > /dev/null 2>&1 && sleep 0'"'"''
<9.134.124.159> (0, b'', b'')
9.134.124.159 | CHANGED | rc=0 >>
11.sh
11.zip
InstallHalyard.sh
[
abc.txt
...
META: ran handlers
META: ran handlers
Process finished with exit code 0
對上述日誌進行簡化,可知其大致有 7 個步驟,分別如下:
簡化後,其所執行的命令如下:
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'echo ~root && sleep 0'"'"''
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140 `" && echo ansible-tmp-1599546740.791937-1678-254955456024140="` echo /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140 `" ) && sleep 0'"'"''
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'echo PLATFORM; uname; echo FOUND; command -v '"'"'"'"'"'"'"'"'/usr/bin/python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.5'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/libexec/platform-python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/bin/python3'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python'"'"'"'"'"'"'"'"'; echo ENDFOUND && sleep 0'"'"''
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
sftp -b - -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 '[9.134.124.159]'
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140/ /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140/AnsiballZ_command.py && sleep 0'"'"''
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 -tt 9.134.124.159 '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140/AnsiballZ_command.py && sleep 0'"'"''
ssh -C -o ControlMaster=auto -o ControlPersist=60s -o Port=36000 -o KbdInteractiveAuthentication=no -o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey -o PasswordAuthentication=no -o 'User="root"' -o ConnectTimeout=10 -o ControlPath=/Users/yangyu/.ansible/cp/4846fe66a5 9.134.124.159 '/bin/sh -c '"'"'rm -f -r /root/.ansible/tmp/ansible-tmp-1599546740.791937-1678-254955456024140/ > /dev/null 2>&1 && sleep 0'"'"''
上傳到被控端的檔案
上傳到被控制的檔案,在 ~/.ansible/tmp/xxx
下,是一個 Python 指令碼,名稱為:AnsiballZ_command.py
,裡面還包含一段壓縮檔案的 base64。程式碼如下:
#!/usr/bin/python
# -*- coding: utf-8 -*-
_ANSIBALLZ_WRAPPER = True # For test-module.py script to tell this is a ANSIBALLZ_WRAPPER
def _ansiballz_main():
import os
import os.path
import sys
import __main__
scriptdir = None
try:
scriptdir = os.path.dirname(os.path.realpath(__main__.__file__))
except (AttributeError, OSError):
pass
excludes = set(('', '.', scriptdir))
sys.path = [p for p in sys.path if p not in excludes]
import base64
import runpy
import shutil
import tempfile
import zipfile
if sys.version_info < (3,):
PY3 = False
else:
PY3 = True
# ZIPDATA 的值已經省略
ZIPDATA = """xxxxxxxxxxx"""
def invoke_module(modlib_path, temp_path, json_params):
z = zipfile.ZipFile(modlib_path, mode='a')
sitecustomize = u'import sys\nsys.path.insert(0,"%s")\n' % modlib_path
sitecustomize = sitecustomize.encode('utf-8')
zinfo = zipfile.ZipInfo()
zinfo.filename = 'sitecustomize.py'
zinfo.date_time = ( 2020, 9, 7, 5, 54, 28)
z.writestr(zinfo, sitecustomize)
z.close()
sys.path.insert(0, modlib_path)
from ansible.module_utils import basic
basic._ANSIBLE_ARGS = json_params
runpy.run_module(mod_name='ansible.modules.command', init_globals=None, run_name='__main__', alter_sys=True)
print('{"msg": "New-style module did not handle its own exit", "failed": true}')
sys.exit(1)
def debug(command, zipped_mod, json_params):
basedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'debug_dir')
args_path = os.path.join(basedir, 'args')
if command == 'explode':
z = zipfile.ZipFile(zipped_mod)
for filename in z.namelist():
if filename.startswith('/'):
raise Exception('Something wrong with this module zip file: should not contain absolute paths')
dest_filename = os.path.join(basedir, filename)
if dest_filename.endswith(os.path.sep) and not os.path.exists(dest_filename):
os.makedirs(dest_filename)
else:
directory = os.path.dirname(dest_filename)
if not os.path.exists(directory):
os.makedirs(directory)
f = open(dest_filename, 'wb')
f.write(z.read(filename))
f.close()
f = open(args_path, 'wb')
f.write(json_params)
f.close()
print('Module expanded into:')
print('%s' % basedir)
exitcode = 0
elif command == 'execute':
sys.path.insert(0, basedir)
with open(args_path, 'rb') as f:
json_params = f.read()
from ansible.module_utils import basic
basic._ANSIBLE_ARGS = json_params
runpy.run_module(mod_name='ansible.modules.command', init_globals=None, run_name='__main__', alter_sys=True)
print('{"msg": "New-style module did not handle its own exit", "failed": true}')
sys.exit(1)
else:
print('WARNING: Unknown debug command. Doing nothing.')
exitcode = 0
return exitcode
ANSIBALLZ_PARAMS = '{"ANSIBLE_MODULE_ARGS": {"_raw_params": "ls /root", "_ansible_check_mode": false, "_ansible_no_log": false, "_ansible_debug": false, "_ansible_diff": false, "_ansible_verbosity": 3, "_ansible_version": "2.11.0.dev0", "_ansible_module_name": "ansible.legacy.command", "_ansible_syslog_facility": "LOG_USER", "_ansible_selinux_special_fs": ["fuse", "nfs", "vboxsf", "ramfs", "9p", "vfat"], "_ansible_string_conversion_action": "warn", "_ansible_socket": null, "_ansible_shell_executable": "/bin/sh", "_ansible_keep_remote_files": false, "_ansible_tmpdir": "/root/.ansible/tmp/ansible-tmp-1599457948.85933-15430-201759115318770/", "_ansible_remote_tmp": "~/.ansible/tmp"}}'
if PY3:
ANSIBALLZ_PARAMS = ANSIBALLZ_PARAMS.encode('utf-8')
try:
temp_path = tempfile.mkdtemp(prefix='ansible_ansible.legacy.command_payload_')
zipped_mod = os.path.join(temp_path, 'ansible_ansible.legacy.command_payload.zip')
with open(zipped_mod, 'wb') as modlib:
modlib.write(base64.b64decode(ZIPDATA))
if len(sys.argv) == 2:
exitcode = debug(sys.argv[1], zipped_mod, ANSIBALLZ_PARAMS)
else:
invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
finally:
try:
shutil.rmtree(temp_path)
except (NameError, OSError):
pass
sys.exit(exitcode)
if __name__ == '__main__':
_ansiballz_main()
當 AnsiballZ_command.py
執行時,會首先將 ZIPDATA
還原成一個壓縮檔案,然後在壓縮檔案中加入一個 sitecustomize.py
。準備完成後,將以 runpy.run_module()
的方式,執行壓縮檔案中的 Python 程式碼。可以看出,我們所需要執行的命令,儲存在 ANSIBALLZ_PARAMS
變數中,並賦值給了 basic._ANSIBLE_ARGS
。
ZIPDATA 壓縮包
首先,壓縮檔案中的內容如下。此壓縮包通過手段獲取到後,解壓後,名稱為 ansible
,裡面的檔案如 basic.py、command.py 來自所執行命令 ansible 所在的包下。
╰─$ tree ansible
ansible
├── __init__.py
├── module_utils
│ ├── __init__.py
│ ├── _text.py
│ ├── basic.py
│ ├── common
│ │ ├── __init__.py
│ │ ├── _collections_compat.py
│ │ ├── _json_compat.py
│ │ ├── _utils.py
│ │ ├── collections.py
│ │ ├── file.py
│ │ ├── parameters.py
│ │ ├── process.py
│ │ ├── sys_info.py
│ │ ├── text
│ │ │ ├── __init__.py
│ │ │ ├── converters.py
│ │ │ └── formatters.py
│ │ ├── validation.py
│ │ └── warnings.py
│ ├── compat
│ │ ├── __init__.py
│ │ ├── _selectors2.py
│ │ └── selectors.py
│ ├── distro
│ │ ├── __init__.py
│ │ └── _distro.py
│ ├── parsing
│ │ ├── __init__.py
│ │ └── convert_bool.py
│ ├── pycompat24.py
│ └── six
│ └── __init__.py
└── modules
├── __init__.py
└── command.py
其次,壓縮檔案中程式碼的執行。從 runpy.run_module
中的 mod_name='ansible.modules.command'
猜測,是要執行壓縮包下的 modules/command.py
。此檔案的執行,包括了一個 AnsibleModule
的初始化,在它的建構函式中,可以看出來,獲取了 basic._ANSIBLE_ARGS
的值。
class AnsibleModule(object):
def __init__(self, argument_spec, bypass_checks=False, no_log=False,
mutually_exclusive=None, required_together=None,
required_one_of=None, add_file_common_args=False,
supports_check_mode=False, required_if=None, required_by=None):
...
self._load_params()
...
##############################basic.py##############################
def _load_params(self):
self.params = _load_params()
##############################basic.py##############################
def _load_params():
global _ANSIBLE_ARGS
if _ANSIBLE_ARGS is not None:
buffer = _ANSIBLE_ARGS
....
最終,從 command.py 執行到 basic.py,以開啟執行系統命令,作為我們所要指令碼(即:ls /root
)執行的開始
cmd = subprocess.Popen(args, **kwargs)
等待 shell 命令執行完畢
在此處,為 cmd 的 stdout、stderr 註冊了可讀事件到 selector,並在一個死迴圈中輪訓 selector,如果有 cmd.stdout、cmd.stderr 可讀事件,就將對應的資料,追加到相應的變數上。當 cmd 執行完成後,退出死迴圈,並返回 cmd 執行後的 stdout、stderr。
selector.register(cmd.stdout, selectors.EVENT_READ)
selector.register(cmd.stderr, selectors.EVENT_READ)
...
while True:
events = selector.select(1)
for key, event in events:
b_chunk = key.fileobj.read()
if b_chunk == b(''):
selector.unregister(key.fileobj)
if key.fileobj == cmd.stdout:
stdout += b_chunk
elif key.fileobj == cmd.stderr:
stderr += b_chunk
...
# only break out if no pipes are left to read or
# the pipes are completely read and
# the process is terminated
if (not events or not selector.get_map()) and cmd.poll() is not None:
break
# No pipes are left to read but process is not yet terminated
# Only then it is safe to wait for the process to be finished
# NOTE: Actually cmd.poll() is always None here if no selectors are left
elif not selector.get_map() and cmd.poll() is None:
cmd.wait()
# The process is terminated. Since no pipes to read from are
# left, there is no need to call select() again.
break
...
return (rc, stdout, stderr)
控制中心
是什麼地方定義上上面這 8 個步驟?產生上面 8 個步驟的地方,開始於:ansible/plugins/action/command.py
,其中 self._execute_module()
完成了前面的 7 個操作,self._remove_tmp_path()
完成了刪除 ~/.ansible/tmp/xxxxx
的任務。
重試機制
- 什麼時候進行重試
- an exception is caught
- ssh returns 255(ControlPersist 超時不能用或者遠端主機連不上)
- 什麼時候不進行重試。
- sshpass returns 5 (invalid password, to prevent account lockouts)
- remaining_tries is < 2
- retries limit reached
重試的方式,是通過一個對連線函式的閉包操作完成。
def _ssh_retry(func):
@wraps(func)
def wrapped(self, *args, **kwargs):
remaining_tries = int(C.ANSIBLE_SSH_RETRIES) + 1
cmd_summary = u"%s..." % to_text(args[0])
conn_password = self.get_option('password') or self._play_context.password
for attempt in range(remaining_tries):
...
return wrapped
################################################################
@_ssh_retry
def _run(self, cmd, in_data, sudoable=True, checkrc=True):
"""Wrapper around _bare_run that retries the connection
"""
return self._bare_run(cmd, in_data, sudoable=sudoable, checkrc=checkrc)
可借鑑點
- 複用已有連結:https://ldpreload.com/blog/ssh-control
相關文章
- Ansible playbook 執行流程
- 大話Ansible Ad-Hoc命令
- Ansible 執行分析工具ARA
- python ansible如何執行指令碼?Python指令碼
- ansible執行playbook報Host Key checking
- MyBatis執行流程MyBatis
- Mysql 執行流程MySql
- MapReduce執行流程
- HA執行流程
- SpringMVC執行流程SpringMVC
- ansible基於密碼sudo執行命令密碼
- for 迴圈執行流程
- Dapr Outbox 執行流程
- 「MySQL」 MySQL執行流程MySql
- javaWeb的執行流程JavaWeb
- MapReduce程式執行流程
- 【ansible】關於ansible執行過程中載入環境變數問題變數
- Python呼叫ansible API系列(二)執行adhoc和playbookPythonAPI
- 深入理解執行緒池的執行流程執行緒
- Spark學習(一)——執行模式與執行流程Spark模式
- eBPF 執行原理和流程eBPF
- 執行流程原始碼分析原始碼
- SQL 解析與執行流程SQL
- PHP執行流程回顧PHP
- 框架執行流程總結框架
- MapReduce的執行流程概述
- thinkphp3.2 執行流程PHP
- SpringMvc - SpringMvc的執行流程SpringMVC
- Seata的AT模式的執行流程模式
- 深入Mybatis原始碼——執行流程MyBatis原始碼
- Mybatis執行流程原始碼分析MyBatis原始碼
- WEB程式執行的基本流程Web
- 【java學習】控制執行流程Java
- 執行緒池的工作流程執行緒
- Servlet基本概念及執行流程Servlet
- Java類初始化執行流程Java
- Mybatis原始碼系列 執行流程(一)MyBatis原始碼
- 從ReentrantLock看AQS (AbstractQueuedSynchronizer) 執行流程ReentrantLockAQS