使用Ansible uri模块检查Jenkins重启后页面状态时遭遇ImportError: cannot import name 'prepare_multipart'问题的排查求助
ImportError: cannot import name 'prepare_multipart'的排查与解决 我在使用Ansible的uri模块检查Jenkins重启后的页面状态时,遇到了这个导入错误,模块代码和错误输出如下:
模块代码
- name: result-test uri: url: "http://{{ jenkins_cm_hostname }}:{{ jenkins_http_port }}{{ jenkins_url_prefix }}" method: GET return_content: "yes" timeout: 5 body_format: raw follow_redirects: "no" status_code: 200,403 register: result
错误输出
{ "module_stdout": "/etc/bashrc: line 94: TMOUT: readonly variable\r\nTraceback (most recent call last):\r\n File \"/home/ec2-user/.ansible/tmp/ansible-tmp-1628318438.5060062-180720-115929401717244/AnsiballZ_uri.py\", line 100, in <module>\r\n _ansiballz_main()\r\n File \"/home/ec2-user/.ansible/tmp/ansible-tmp-1628318438.5060062-180720-115929401717244/AnsiballZ_uri.py\", line 92, in _ansiballz_main\r\n invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\r\n File \"/home/ec2-user/.ansible/tmp/ansible-tmp-1628318438.5060062-180720-115929401717244/AnsiballZ_uri.py\", line 41, in invoke_module\r\n run_name='__main__', alter_sys=True)\r\n File \"/usr/lib64/python2.7/runpy.py\", line 176, in run_module\r\n fname, loader, pkg_name)\r\n File \"/usr/lib64/python2.7/runpy.py\", line 82, in _run_module_code\r\n mod_name, mod_fname, mod_loader, pkg_name)\r\n File \"/usr/lib64/python2.7/runpy.py\", line 72, in _run_code\r\n exec code in run_globals\r\n File \"/tmp/ansible_ansible.legacy.uri_payload_bBh_2I/ansible_ansible.legacy.uri_payload.zip/ansible/modules/uri.py\", line 423, in <module>\r\nImportError: cannot import name prepare_multipart\r\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1 }
我的环境情况:Ansible控制器仅通过pip安装,目标节点未安装Ansible,所有节点由Terraform部署。之前看到有博客说这个问题是多版本Ansible共存导致,但我的情况不符合,求排查方向。
排查与解决思路
别着急,这个问题虽然看起来是模块导入错误,但核心大概率和目标节点的Python环境(尤其是requests库版本)与Ansible uri模块不兼容有关——毕竟Ansible的uri模块依赖requests库,而且会把模块代码推到目标节点执行,哪怕目标节点没装Ansible,也会用到目标节点本地的Python环境和依赖库。
下面给你一步步的排查和解决步骤:
1. 检查目标节点的Python版本和requests库版本
先登录到出问题的目标节点,执行以下命令确认环境:
# 查看默认Python版本 python --version # 查看已安装的requests版本 pip show requests
从错误日志能看到目标节点用的是Python2.7,而prepare_multipart是requests库特定版本才有的方法,旧版本或过新版本都可能出现导入失败的情况(Python2.7本身已经停止维护,对应的requests兼容版本非常有限)。
2. 安装目标节点兼容的requests版本
如果目标节点的requests版本不符合要求,可以卸载现有版本,安装Python2.7支持的最后几个兼容版本之一,比如2.27.1:
# 卸载现有requests pip uninstall -y requests # 安装指定兼容版本 pip install requests==2.27.1
安装完成后重新运行Ansible playbook,看看问题是否解决。
3. 指定playbook使用Python3解释器
如果目标节点同时安装了Python3,可以在playbook中明确指定使用Python3执行模块,避开Python2.7的兼容性问题:
- name: result-test uri: url: "http://{{ jenkins_cm_hostname }}:{{ jenkins_http_port }}{{ jenkins_url_prefix }}" method: GET return_content: "yes" timeout: 5 body_format: raw follow_redirects: "no" status_code: 200,403 register: result vars: ansible_python_interpreter: /usr/bin/python3
Python3的requests库兼容性更好,大概率能解决这个导入错误。
4. 排除bash配置的干扰
错误日志里还提到了/etc/bashrc: line 94: TMOUT: readonly variable,虽然看起来和导入错误无关,但有可能是bash配置导致环境变量异常,影响了Python的库路径。可以临时注释掉目标节点/etc/bashrc中设置TMOUT的行,重新连接节点后再测试,排除这个干扰因素。
5. 确认控制器的requests版本(可选)
虽然核心问题在目标节点,但也可以检查控制器上的requests版本是否和目标节点差异过大——如果差异太大,可能会导致模块打包时引入不兼容代码。在控制器上执行pip show requests查看版本,必要时调整为和目标节点一致的兼容版本。
总结
这个问题和控制器是否多版本安装Ansible关系不大,核心是目标节点的Python2.7环境与requests库版本不兼容。按照上面的步骤一步步排查,应该能解决问题。
内容的提问来源于stack exchange,提问作者JPNagarajan

