Ansible - with_dict: dictionary - Как использовать переменные, определенные в каждом словаре, которые зависят от других

Среда: Ansible 1.9.2, CentOS 6.5

Я создал роль для загрузки файлов артефактов JAVA (.tar.gz) для 3 разных версий JAVA из Artifactory. Я пытаюсь использовать функцию with_dict от Ansible (вместо использования with_items).

Созданы следующие файлы:

$ cat role /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, которые зависят от других переменных, определенных в том же ключе (скажем, в ключе java7_60)?

Прямо сейчас, когда я пытаюсь использовать текущие переменные dist_file или dist_url ИЛИ закомментированные строки, как их установить (т. Е. С помощью item.value.), Это не устанавливает значение этих 2-х переменных как желаемое, т.е. зависит от версии других переменных ., group_path, classifier, ext и artifactory_url (который определен в файле defaults / main.yml другой распространенной роли).

Я видел, что для использования with_dict: в playbook/task, я должен использовать {{ item.value. имя_переменной }} но как я могу определить переменную, которая зависит от других в том же разделе KEY словаря.

Сообщение об ошибке, которое я получаю при использовании вышеуказанного словаря в следующей задаче:

$ cat role / 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 (с текущей настройкой в ​​role /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 (со строками, которые в данный момент закомментированы в role /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"]}

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 есть блоки, с помощью которых вы сможете циклически выполнять несколько задач по определению и определять переменные для всех задач в этом блоке.

Мне было любопытно по этому поводу, поэтому я немного покопался и при этом натолкнулся на этот похожий ответ. Это только часть решения в вашем случае.

Похоже, что 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"
            }
        }
    }
} 

Благодаря Брюсу П и Сеперу Н. я сделал следующее после того, как принял решения / подсказки из их ответа. Теперь моя задача - работать с несколькими инструментами (jdk, mvn, gradle, maven и т. Д.) С несколькими версиями с минимальными изменениями в файлах и без необходимости (определение 2-го словаря).

Что я сделал:

На верхнем уровне / по умолчанию для некоторых общих ролей / main.yml у меня будет следующее:

Роли $ cat /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 /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

В ролях отдельных инструментов / default / 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 }}"

Если я использую описанный выше подход, я могу определить переменную "tool" на заданных ролях // defaults / main.yml, и она будет доступна dist_file / dist_url.

Также вместо использования команды: wget -q "...", я могу использовать get_url (модуль) в Ansible.

- 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 }}"

и убедитесь, что с УДАЛЕННОЙ машины вы можете пропинговать сервер Artifactory (проверьте ping.... или /etc/resolv.conf файл для записей поиска / сервера имен) и если все выглядит хорошо, то решения, опубликованные здесь, будут РАБОТАТЬ!

Мне лично понравилось решение для передачи уровня переменной инструмента (на уровне роли / задачи) (таким образом, отказ от использования инструмента определения: на уровне словаря) и отсутствие необходимости определять индивидуальные переменные уровня _dist_file/dist_url.

Другие вопросы по тегам