【问题标题】:Ansible - with_dict: dictionary - How to use variables defined in each dictionary which depends upon othersAnsible - with_dict: 字典 - 如何使用每个字典中定义的变量,这些变量依赖于其他字典
【发布时间】:2016-02-25 22:53:51
【问题描述】:

环境为:Ansible 1.9.2、CentOS 6.5

我创建了一个角色,用于从 Artifactory 下载 3 个不同 JAVA 版本的 JAVA (.tar.gz) 工件文件。我正在尝试使用 Ansible 的 with_dict 功能(而不是使用 with_items)。

创建了以下文件:

$ cat roles/java/defaults/main.yml

---
java_versions:
  java7_60:
    version: 1.7.60
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz
    dist_file: "jdk-{{ version }}-{{ classifier }}-{{ ext }}"
#    dist_file: "jdk-{{item.value.version }}-{{ item.value.classifier }}-{{ item.value.ext }}"
    dist_url: "{{ artifactory_url }}/{{ group_path }}/{{ version }}/{{ dist_file }}"
#    dist_url: "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}"

  java7_67:
    version: 1.7.67
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz
    dist_file: "jdk-{{item.value.version }}-{{ item.value.classifier }}-{{ item.value.ext }}"
    dist_url: "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}"
  java8_45:
    version: 1.8.45
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz
    dist_file: "jdk-{{item.value.version }}-{{ item.value.classifier }}-{{ item.value.ext }}"
    dist_url: "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}"

如何设置或使用 dist_file 或 dist_url 变量,这些变量取决于同一 KEY 中定义的其他变量(比如说在 KEY java7_60 )?

现在,当我尝试当前的 dist_file 或 dist_url 变量或注释掉的行设置它们的方式(即使用 item.value。)时,它没有设置这些值2 个变量,即取决于其他变量 version、group_path、classifier、ext 和 artifactory_url(在另一个常见角色的 defaults/main.yml 文件中定义)。

我看到要使用 with_dict: 在剧本/任务中,我必须使用 {{ item.value.variable_name }} 但是如何定义一个依赖于其他变量的变量字典的相同 KEY 部分。

我在以下任务中使用上述字典时收到的错误消息是:

$ cat roles/java/tasks/main.yml:

- name: Download Java/JDK Versions
  command: wget -q "{{ item.value.dist_url }}"
    chdir="{{ common_download_dir }}"
    creates="{{ common_download_dir }}/{{ item.value.dist_file }}"
  with_dict: "{{ java_versions }}"
  become_user: "{{ build_user }}"

错误消息与使用 dist_file / dist_url(当前设置在角色/java/defaults/main.yml 中):

TASK: [java | Download Java/JDK Versions] *************************************
failed: [server01.poc.jenkins] => (item={'key': 'java7_60', 'value': {'dist_file': u'jdk-{# version #}-{# classifier #}-{# ext #}', 'ext': 'tar.gz', 'version': '1.7.60', 'dist_url': u'{# artifactory_ur #}/{# group_path #}/{# version #}/{# dist_file #}', 'group_path': 'com/oracle/jdk', 'classifier': 'linux-x64'}}) => {"changed": true, "cmd": ["wget", "-q", "{# artifactory_url #}/{# group_path #}/{# version }/{# dist_file #}"], "delta": "0:00:00.006081", "end": "2015-11-23 12:50:18.383728", "item": {"key": "java7_60", "value": {"classifier": "linux-x64", "dist_file": "jdk-{# version #}-{# classifier #}-{# ext #}, "dist_url": "{# artifactory_url #}/{# group_path #}/{# version #}/{# dist_file #}", "ext": "tar.gz", "group_path": "com/oracle/jdk", "version": "1.7.60"}}, "rc": 4, "start": "2015-11-23 12:50:18.377647", "wrnings": ["Consider using get_url module rather than running wget"]}

错误消息与使用 dist_file / dist_url (在角色/java/defaults/main.yml 中当前已注释掉行):

TASK: [java | Download Java/JDK Versions] *************************************
failed: [server01.poc.jenkins] => (item={'key': 'java7_60', 'value': {'dist_file': u'jdk-{#item.value.version #}-{# item.value.classifier #}-{# item.value.ext #}', 'ext': 'tar.gz', 'version': '1.7.60' , 'dist_url': u'{# artifactory_url #}/{# item.value.group_path #}/{# item.value.version #}/{# dist_file #}', 'group_path': 'com/oracle/jdk', 'classifier': 'linux-x64'}}) => {"changed": true, "cmd": ["wget",  "-q", "{# artifactory_url #}/{# item.value.group_path #}/{# item.value.version #}/{# dist_file #}"], "delta": "0:00:00.005900", "end": "2015-11-23 12:36:24.131327", "item": {"key": "java7_60", "value": {"cla ssifier": "linux-x64", "dist_file": "jdk-{#item.value.version #}-{# item.value.classifier #}-{# item.value.ext #}", "dist_url": "{# artifactory_url #}/{# item.value.group_path #}/{# item.value.version #}/{#  dist_file #}", "ext": "tar.gz", "group_path": "com/oracle/jdk", "version": "1.7.60"}}, "rc": 4, "start": "2015-11-23 12:36:24.125427", "warnings": ["Consider using get_url module rather than running wget"]}

【问题讨论】:

  • 与其将所有变量组合到变量 dict_url 中并在任务中引用它,不如让任务自己执行 wget -q "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}" 更容易吗?
  • 感谢您的评论。我试过了,但它使任务看起来有点难看。即使我这样做,我认为问题在于您是否可以使用同一个 KEY 中的另一个字典变量来设置字典变量。无论哪种方式,问题似乎是字典变量中的变量没有被替换。我正在尝试你说的,看看是否有效!想知道有没有一种方法可以在字典的 KEY 部分中组合/重用变量。
  • 我相信最简单(也许是最好)的方法是在角色中创建 dist_file 和 dist_url 字符串,而不是在字典中定义它。
  • @SepehrNazari 我喜欢这个主意!,你能发布如何做到这一点吗?你的意思是,我仍然可以使用 with_dict: 只有初始 4 个字典变量(版本、组路径、分类器和 ext)并创建 2 个变量:dist_file 和 dist_url 在角色/java/tasks/main.yml 中?

标签: ansible roles ansible-playbook ordereddictionary


【解决方案1】:

感谢 Bruce P 和 Sepehr N。在从他们的回复中获取解决方案/提示后,我做了以下事情。现在,我的任务是为具有多个版本的多个工具(jdk、mvn、gradle、maven 等)工作,对文件所做的更改最少并且不需要(第二个字典定义)。

我做了什么:

在顶级/一些常见角色的 default/main.yml 中,我将拥有以下内容:

$ cat roles/some_common_global_role/defaults/main.yml

---
a_var: giga
other_var: fifa

dist_file: "{{ item.value.tool }}-{{item.value.version }}-{{ item.value.classifier }}.{{ item.value.ext }}"
dist_url: "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}"

在 $ cat roles/java/defaults/main.yml

---
tool: jdk

java_versions:
  java7_60:
    version: 1.7.60
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz
  java7_67:
    version: 1.7.67
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz
  java8_45:
    version: 1.8.45
    group_path: com/oracle/jdk
    classifier: linux-x64
    ext: tar.gz

在单个工具的角色/默认/main.yml 中,我将设置相同的字典和工具变量,它会下载多个工具/版本。在这种情况下,您的一些通用级别角色的 default/main.yml 将如下所示:

---
dist_file: "{{ tool }}-{{item.value.version }}-{{ item.value.classifier }}.{{ item.value.ext }}"
dist_url: "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/{{ dist_file }}"

如果我使用上述方法,我可以在给定的角色//defaults/main.yml 任务级别定义“工具”变量,并且它可用于 dist_file / dist_url。

除了使用命令:wget -q "...",我还可以在 Ansible 中使用 get_url (module)。

- name: Download Java/JDK Versions
#  command: wget -q "{{ dist_url }}"
#    chdir="{{ common_download_dir }}"
#    creates="{{ common_download_dir }}/{{ dist_file }}"
  get_url: url="{{ dist_url }}" dest="{{ common_download_dir }}"
  become_user: "{{ build_user }}"
  with_dict: "{{ java_versions }}"

并确保您可以从远程机器上 ping Artifactory 服务器(检查 ping .... 或 /etc/resolv.conf 文件中的搜索/名称服务器条目),如果一切正常,则在此处发布解决方案会工作的!

我个人喜欢传递 tool 变量(在角色/任务)级别的解决方案(因此,否定使用定义工具:在字典级别)并且不需要定义个人级别 _dist_file/dist_url变量。

【讨论】:

    【解决方案2】:

    我对此很好奇,所以我进行了一些挖掘,并在这样做时遇到了this similar answer。不过,这只是您的情况下解决方案的一部分。

    Ansible 似乎不允许您从自己的定义中引用变量,我认为这是有道理的,因为它没有完全定义。所以这样的事情是行不通的,实际上会在引用变量时引发一些令人困惑的错误:

    ---
    myvar:
        param1: foo
        param2: "{{ myvar['foo'] }} bar"
    

    从您自己的示例中也可以看出,Ansible 不允许您在变量中使用 item 构造来引用其他复杂变量。这对我来说很有意义,因为似乎 Ansible 在定义变量时解析了变量中的 jinja2 构造,而不是在引用变量的运行时解析。

    因此,尽管这不是您要寻找的东西,但我认为如果您将变量分成两部分,您可以通过以下方式使其工作:

    ---
    artifactory_url: "http://path.to.jarfile"
    java_versions:
      java7_60:
        version: 1.7.60
        group_path: com/oracle/jdk
        classifier: linux-x64
        ext: tar.gz
    
    java_downloads:
      java7_60:
        dist_url: "{{ artifactory_url }}/{{ java_versions['java7_60']['group_path'] }}/{{ java_versions['java7_60']['version'] }}/jdk-{{ java_versions['java7_60']['version'] }}-{{ java_versions['java7_60']['classifier'] }}.{{ java_versions['java7_60']['ext'] }}"
    

    当您以这种方式调试 java_downloads 时,您将获得所需的完整 URL:

    TASK: [debug var=item] ********************************************************
    ok: [localhost] => (item={'key': 'java7_60', 'value': {'dist_url': u'http://path.to.jarfile/com/oracle/jdk/1.7.60/jdk-1.7.60-linux-x64.tar.gz'}}) => {
        "item": {
            "key": "java7_60",
            "value": {
                "dist_url": "http://path.to.jarfile/com/oracle/jdk/1.7.60/jdk-1.7.60-linux-x64.tar.gz"
            }
        },
        "var": {
            "item": {
                "key": "java7_60",
                "value": {
                    "dist_url": "http://path.to.jarfile/com/oracle/jdk/1.7.60/jdk-1.7.60-linux-x64.tar.gz"
                }
            }
        }
    } 
    

    【讨论】:

    • 谢谢布鲁斯。我认为这是我最好的选择。同样,当我拥有 Ansible 2.0 时,我想,它会更容易。
    【解决方案3】:

    我相信做你想做的最好的方法(因为我不相信循环变量可以引用自己)是让你的任务是:

    - name: Download Java/JDK Versions
      command: wget -q "{{ artifactory_url }}/{{ item.value.group_path }}/{{ item.value.version }}/jdk-{{item.value.version }}-{{ item.value.classifier }}-{{ item.value.ext }}"
        chdir="{{ common_download_dir }}"
        creates="{{ common_download_dir }}/jdk-{{item.value.version }}-{{ item.value.classifier }}-{{ item.value.ext }}"
      with_dict: "{{ java_versions }}"
      become_user: "{{ build_user }}"
    

    基本上是手动将变量插入到您的任务中。

    如果您需要将变量用于多个任务,这并不漂亮,但在 ansible 1.x 中,我认为没有办法让它变得漂亮。 Ansible 2.0 具有块,您可以使用这些块在一个字典上将多个任务一起循环,并且您可以为该块中的所有任务定义变量以供使用。

    【讨论】:

    • Bruce P 的答案是当您有几个不同的 Java 版本并经常使用变量时的最佳选择。我的答案最适合当您有许多 Java 版本但不经常使用变量时。如果您有许多 Java 版本并经常使用变量,那么获得漂亮解决方案的唯一方法是使用 Ansible 2.0
    • 感谢 Sepehr。我同意你的看法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 2019-06-06
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多