Ansible 如何在 rescue 刷新 token 后复用 block 中的任务?
结论
可以将 API 调用放进独立的任务文件,在 block 中通过 ansible.builtin.include_tasks 执行。请求返回 401 后,在 rescue 中更新 token,再次包含同一个任务文件。
rescue 中的 set_fact 会为当前主机设置变量,后续同一主机上的任务可以读取。重试需要显式安排:rescue 成功后,Ansible 会继续执行整个 block 结构之后的任务,不会回到失败位置,也不会补跑原 block 中跳过的任务。参见 set_fact 文档和 block 错误处理文档。
当前片段中需要先修正的问题
status_code: [200, 401]会同时接受这两种状态。没有额外的失败条件时,返回401不会触发rescue。可以改为status_code: [200],再在rescue中判断失败是否由401引起。- 请求头读取的是
central_access_token,而set_fact写入的是access_token,两者不会自动关联。注册结果的名称是central_token_response,取值时也应使用这个名称。 - token 请求缺少 URL,需要补充服务实际使用的 token endpoint。
- 请求体参数应为
body。ansible.builtin.uri使用body和body_format: form-urlencoded发送表单,不使用payload。 - YAML 层级需要恢复:
block下的任务必须缩进,rescue与block对齐。
status_code、body 和 body_format 的含义可查阅 uri 模块文档。
将 API 调用提取到任务文件
创建 api_tasks.yml,只保存业务请求,不在这里处理 token 刷新:
---
- name: Get sites from api
ansible.builtin.uri:
url: "{{ central_base_url }}/network-monitoring/v1/sites-health"
method: GET
headers:
Authorization: "Bearer {{ central_access_token }}"
Accept: application/json
status_code: [200]
register: central_sites_response
no_log: true
- name: Get devices per site
ansible.builtin.uri:
url: "{{ central_base_url }}/network-monitoring/v1/aps?filter=siteId eq {{ siteId | urlencode }}"
method: GET
headers:
Authorization: "Bearer {{ central_access_token }}"
Accept: application/json
status_code: [200]
register: central_devices_response
no_log: true
这里保留了原来的请求路径和 siteId 参数。运行前需要提供 central_base_url、初始 central_access_token 和 siteId;示例没有添加从站点响应中提取 siteId 的逻辑。
include_tasks 在执行时加载任务列表,因此更新变量后可以再次执行这个文件。参见 include_tasks 文档。
在 rescue 中更新 token 并重试一次
下面的调用入口任务文件可放入 role 的 tasks/main.yml,也可由现有 playbook 包含。将 api_tasks.yml 放在对应位置,并预先将 central_token_url 定义为真实的 token endpoint。
---
- name: Call API with token recovery
block:
- name: Run API tasks
ansible.builtin.include_tasks: api_tasks.yml
rescue:
- name: Stop on errors other than HTTP 401
ansible.builtin.fail:
msg: "API 调用失败,且状态不是 HTTP 401,请检查接口响应或连接问题。"
when: (ansible_failed_result.status | default(-1) | int) != 401
- name: Request a new access token
ansible.builtin.uri:
url: "{{ central_token_url }}"
method: POST
headers:
Accept: application/json
body_format: form-urlencoded
body:
grant_type: client_credentials
client_id: "{{ client_id }}"
client_secret: "{{ client_secret }}"
status_code: [200]
register: central_token_response
no_log: true
- name: Check token response
ansible.builtin.assert:
that:
- central_token_response.json is defined
- central_token_response.json.access_token is defined
- (central_token_response.json.access_token | default('') | length) > 0
fail_msg: "token 响应中没有可用的 access_token。"
no_log: true
- name: Store refreshed token
ansible.builtin.set_fact:
central_access_token: "{{ central_token_response.json.access_token }}"
no_log: true
- name: Retry API tasks once
ansible.builtin.include_tasks: api_tasks.yml
- name: Show devices request status
ansible.builtin.debug:
msg: "设备请求完成,HTTP 状态为 {{ central_devices_response.status }}"
先执行 API 任务。如果其中一个请求因 401 失败,就获取新 token,覆盖 central_access_token,然后重新执行 API 任务文件。其他错误会通过 fail 报告,不会发起 token 请求。ansible_failed_result 是 Ansible 在 rescue 中提供的失败结果变量,见 block 错误处理文档。
重试在 rescue 内进行。如果重试仍然失败,恢复流程也会失败;Ansible 不会再次进入同一个 rescue,因此不会在这里无限重试。
使用时的几个边界
- 确认 token endpoint 的实际协议。示例沿用输入中的
grant_type: client_credentials,前提是服务支持这种方式,并接受表单中的凭据。该请求会重新申请 access token;如果服务要求使用refresh_token,应按服务文档调整请求,不能仅凭任务名称认定两种流程相同。 - 重试会重新执行整个任务文件。如果获取站点成功、获取设备失败,刷新后会再次获取站点。这两个请求都是读取操作,通常容易接受;如果加入写操作,应先确认重复执行是否安全,或将恢复逻辑缩小到单个请求。
- 注意变量覆盖。如果通过
--extra-vars固定传入central_access_token,其优先级可能使请求无法读取set_fact设置的新值。避免用高优先级变量固定需要在运行中更新的 token,参见 set_fact 的变量优先级说明。 - 控制敏感日志。示例为含凭据和 token 的任务设置了
no_log: true。后续调试时也应避免直接输出 token 响应或请求头。
备注:内容仅供参考。
