针对 python 云堆栈模块中可能存在的错误调试 ansible 跟踪

Debugging ansible trace for possible bug in python cloud stack module

考虑以下用于上传 ssh 密钥的 Ansible 任务的问题:

  File "/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py", line 113, in <module>
    _ansiballz_main()
  File "/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py", line 105, in _ansiballz_main
    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)
  File "/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py", line 48, in invoke_module
    imp.load_module('__main__', mod, module, MOD_DESC)
  File "/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py", line 267, in <module>
  File "/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py", line 258, in main
  File "/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py", line 133, in register_ssh_key
  File "/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py", line 211, in get_ssh_key
  File "/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py", line 227, in _get_ssh_fingerprint
  File "/usr/local/lib/python2.7/dist-packages/sshpubkeys/keys.py", line 157, in hash_md5
    fp_plain = hashlib.md5(self._decoded_key).hexdigest()
TypeError: md5() argument 1 must be string or buffer, not None

fatal: [localhost]: FAILED! => {
    "changed": false, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py\", line 113, in <module>\n    _ansiballz_main()\n  File \"/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py\", line 105, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/foo/.ansible/tmp/ansible-tmp-1539175437.69-201636907966313/AnsiballZ_cs_sshkeypair.py\", line 48, in invoke_module\n    imp.load_module('__main__', mod, module, MOD_DESC)\n  File \"/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py\", line 267, in <module>\n  File \"/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py\", line 258, in main\n  File \"/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py\", line 133, in register_ssh_key\n  File \"/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py\", line 211, in get_ssh_key\n  File \"/tmp/ansible_cs_sshkeypair_payload_Jb1ZG5/__main__.py\", line 227, in _get_ssh_fingerprint\n  File \"/usr/local/lib/python2.7/dist-packages/sshpubkeys/keys.py\", line 157, in hash_md5\n    fp_plain = hashlib.md5(self._decoded_key).hexdigest()\nTypeError: md5() argument 1 must be string or buffer, not None\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", 
    "rc": 1
}

虽然存在 ssh 密钥,但这似乎是一些错误,因为 md5() 参数可能会丢失。

无法在此表单中准确识别为已知问题。

Python 2.x 和 3.x 都会出现此问题。 (以下 Ubuntu 16.04/18.04 LTS 的 Dockerfiles)。

Ubuntu 16.04 LTS,Python 2

FROM ubuntu:16.04

ENV WORKDIR /work
WORKDIR $WORKDIR
COPY . $WORKDIR/example/


RUN mv example/.ssh/ ~/ && ls -alh ~/.ssh/ && \
    apt-get -y update && apt-get -y install software-properties-common && apt-add-repository ppa:ansible/ansible && \
    apt-get -y update && \
    apt-get -y install python-pip && pip install ansible cs sshpubkeys && \
    echo "List installed Python packages:" && pip list && python --version && ansible --version

Ubuntu 18.04 LTS,Python 3

FROM ubuntu:18.04

ENV WORKDIR /work
WORKDIR $WORKDIR
COPY . $WORKDIR/example/


RUN mv example/.ssh/ ~/ && ls -alh ~/.ssh/ && \
    apt-get -y update && apt-get -y install software-properties-common && apt-add-repository ppa:ansible/ansible && \
    apt-get -y update && \
    apt-get -y install python3-pip && pip3 install ansible cs sshpubkeys && \
    echo "List installed Python packages:" && pip3 list && python3 --version && ansible --version

系统似乎删除了临时生成的Python脚本。是否可以选择保留它们以进行调试?

旁注:example/.ssh/ 包含目标系统未知的新 SSH 公钥。

假设:虽然最终的动态文件名为 AnsiballZ_cs_sshkeypair.py,但它是否来自 Python cs(Apache Cloud Stack 绑定)模块?

默认情况下,临时脚本将被删除,因为它们基本上只是从 Ansible 主机传输的模块文件(包装到 Ansiballz 中)的副本。在那种情况下,我猜远程主机与 Ansible 主机相同,但这并不重要,因为概念是相同的。 ansiballz-wrapped 模块的命名方案大部分与您假设的一样。

但实际上您可以通过在您的 playbook 调用前添加 ANSIBLE_KEEP_REMOTE_FILES=1 来覆盖自动删除,如下所示:

ANSIBLE_KEEP_REMOTE_FILES=1 ansible-playbook <playbookname>.yml -vvv

这样可以保留 ansiballz*.py,但模块本身作为某种 base64 字符串传递。我认为这不会有太大帮助。

为了进一步调试,我建议您从 github 克隆 Ansible 存储库,然后执行(最好在额外的 venv 中)

source /path/to/dev/Ansible/hacking/env-setup

设置开发环境。然后,您可以直接使用 Python 解释器调用该模块,并使用 pdb 等工具。我建议您准备一个 json 文件,格式如下。

{"ANSIBLE_MODULE_ARGS": { "key1": "value1", "key2"... }}

然后您应该能够像这样调用模块:

python <module-name-plus-path> <json-file-plus-path>

希望对您有所帮助!

编辑:我忘了一件事。查看 Ansible dev documentation。如果你真的想深入研究这件事,这将对你有很大帮助。