首页 > 解决方案 > ansible.builtin.uri 模块 - 读取 JSON 文件内容并将其格式化为要在有效负载中使用的字符串

问题描述

我正在尝试使用ansible.builtin.lookup插件从本地目录读取 JSON 文件,然后将其作为有效负载传递给ansible.builtin.uri模块以将 POST 消息发送到 URI 端点。

以下是我的 JSON 文件 ( config.json ) 的内容:

    {
      "Configuration": {
        "Components": [
          {
            "Name": "A",
            "Attributes": [
              {
                "Name": "A1",
                "Value": "1",
                "Set On Import": "True",
                "Comment": "Read and Write"
              },
              {
                "Name": "A2",
                "Value": "2",
                "Set On Import": "True",
                "Comment": "Read and Write"
              }
            ]
          }
        ]
      }
    }

我需要将上述 JSON 内容作为有效负载中的以下字符串发送到 ansible.builtin.uri 模块:

"{\"Configuration\": {\"Components\": [{\"Name\": \"A\", \"Attributes\": [{\"Name\": \"A1\", \"Value\": \"1\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}, {\"Name\": \"A2\", \"Value\": \"2\", \"Set On Import\": \"True\", \"Comment\": \"Read and Write\"}]}]}}"

我正在尝试使用带有to_json过滤器的查找插件来读取和格式化 JSON 内容。以下是我的剧本:

- name: import scp
  ansible.builtin.uri:
    url: "https://{{ inventory_hostname }}/api/config/actions/import"
    user: "{{ user }}"
    password: "{{ password }}"
    method: POST
    headers:
      Accept: "application/json"
      Content-Type: "application/json"
    body:
      Parameters:
        Type: "LOCAL_FILE"
        Target: "ALL"
        IgnoreCertificateWarning: "Enabled"
      Buffer: "{{ lookup('file', 'config.json') | to_json }}"
    body_format: json
    status_code: 202
    validate_certs: no
    force_basic_auth: yes

但是,uri 模块会双重转义所有换行符和制表符。以下是我运行 playbook 时发送有效负载的方式:

    "invocation": {
        "module_args": {
            "attributes": null,
            "body": {
                "Buffer": "\"{\\n\\t\\\"Configuration\\\": {\\n\\t\\t\\\"Components\\\": [\\n\\t\\t\\t{\\n\\t\\t\\t\\t\\\"Name\\\": \\\"A\\\",\\n\\t\\t\\t\\t\\\"Attributes\\\": [\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"1\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t},\\n\\t\\t\\t\\t\\t{\\n\\t\\t\\t\\t\\t\\t\\\"Name\\\": \\\"A2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Value\\\": \\\"2\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Set On Import\\\": \\\"True\\\",\\n\\t\\t\\t\\t\\t\\t\\\"Comment\\\": \\\"Read and Write\\\"\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t]\\n\\t\\t\\t}\\n\\t\\t]\\n\\t}\\n}\"",
                "Parameters": {
                    "IgnoreCertificateWarning": "Enabled",
                    "Type": "LOCAL_FILE",
                    "Target": "ALL"
                },
            },
            "body_format": "json",
...
    },

您能否让我知道如何使用 uri 模块格式化有效负载?感谢任何帮助。

已编辑(2021 年 5 月 11 日): 我按照@mdaniel 在他的回复中的建议进行了更改,并使用了stringfilter 而不是to_json. 通过建议的更改,我可以看到 JSON 被正确格式化为带有换行符 ('\n') 和制表符 ('\t') 字符的字符串。我尝试使用replace过滤器删除\n\t字符。但是,现在整个字符串被转换回 JSON。

string以下是单独使用过滤器时的剧本和输出:

...
        body:
          Parameters:
            Type: "LOCAL_FILE"
            Target: "ALL"
            IgnoreCertificateWarning: "Enabled"
          Buffer: "{{ lookup('file', 'config.json') | string }}"
$ ansible-playbook import_file.yml -i hosts --tags

...
            "body": {
                "HostPowerState": "On",
                "Buffer": "{\n\t\"Configuration\": {\n\t\t\"Components\": [\n\t\t\t{\n\t\t\t\t\"Name\": \"A\",\n\t\t\t\t\"Attributes\": [\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A1\",\n\t\t\t\t\t\t\"Value\": \"1\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\t\"Name\": \"A2\",\n\t\t\t\t\t\t\"Value\": \"2\",\n\t\t\t\t\t\t\"Set On Import\": \"True\",\n\t\t\t\t\t\t\"Comment\": \"Read and Write\"\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t}\n\t\t]\n\t}\n}",
                "Parameters": {
                    "IgnoreCertificateWarning": "Enabled",
                    "Type": "LOCAL_FILE",
                    "Target": "ALL"
                },
            },

replace以下是过滤器与过滤器结合使用时的剧本和输出string

...
        body:
          Parameters:
            Type: "LOCAL_FILE"
            Target: "ALL"
            IgnoreCertificateWarning: "Enabled"
          Buffer: "{{ lookup('file', 'config.json') | string | replace('\n', '') | replace('\t', '') }}"
...

$ ansible-playbook import_file.yml -i hosts --tags

...
            "body": {
                "Buffer":  {
                    "Configuration": {
                        "Components": [
                            {
                                "Attributes": [
                                    {
                                        "Comment": "Read and Write",
                                        "Name": "A1",
                                        "Set On Import": "True",
                                        "Value": "1"
                                    },
                                    {
                                        "Comment": "Read and Write",
                                        "Name": "A2",
                                        "Set On Import": "True",
                                        "Value": "2"
                                    }
                                ],
                                "Name": "A"
                            }
                        ]
                    }
                },
                "Parameters": {
                    "IgnoreCertificateWarning": "Enabled",
                    "Type": "LOCAL_FILE",
                    "Target": "ALL"
                },
            },
...

关于如何从字符串中删除\n\t字符的任何指示?

标签: jsonansibleuri

解决方案


您已经使用to_json了一个dict值,该值本身将被to_json-ed;ansible 无法dict通过 HTTP 传输 python,因此任何不是字符串的 yaml 结构都需要先转换为一个

您需要的只是查找结果(它将返回 a str,而不是 a dict),然后由于上述原因,ansible 将应用于to_json整个值body:

但是,因为 ansible 试图“提供帮助”,所以它会自动将它发现的 yaml 值强制{ 转换为 a dict- 这就是为什么你只需要lookup通过| string过滤器发送结果来强化 ansible 是的,你真的希望它str在那种情况下保持不变

...

    body:
      Parameters:
        Type: "LOCAL_FILE"
        Target: "ALL"
        IgnoreCertificateWarning: "Enabled"
      Buffer: "{{ lookup('file', 'config.json') | string }}"

更新的答案方法

鉴于dict强制仍然是一个问题的评论讨论,并且领先的空间涉及 OP,替代方法是完全建立实际的有效负载结构,并且在传输之前只“JSON-ify”它,以保持ansible 和 jinja 在同一页面上关于数据类型:

- name: import scp
  vars:
    body_dict:
      Parameters:
        Type: "LOCAL_FILE"
        Target: "ALL"
        IgnoreCertificateWarning: "Enabled"
      # this will be filled in before submission
      # Buffer:
    whitespace_free_config_json: >-
          {{ lookup('file', 'config.json') 
          | regex_replace('[\t\n]', '')
          | string
          }}
  ansible.builtin.uri:
    ...
    body: >-
      {{ body_dict
      | combine({"Buffer": whitespace_free_config_json})
      | to_json }}
    body_format: json
    status_code: 202

推荐阅读