Ansible. переопределить единственный ключ словаря

Я использую ansible для управления конфигурацией как для производства, так и для бродячей коробки. У меня есть файл со значениями по умолчанию: group_vars / all.

---
env: prod
wwwuser: www-data

db:
    root_pwd: root_pwd
    pdo_driver: pdo_mysql
    host: localhost
    name: test
    user: test
    pwd: test
    charset: utf8

domain: somedomain
projectdir: /var/www/application
webrootdir: "{{ projectdir }}/web"

В host_vars / vagrantbox я хочу иметь что-то вроде:

db:
    root_pwd: super_easy_password

Но этот полностью переопределяет db dictrionary, а я хочу переопределить одиночный ключ. Как этого добиться?

ОБНОВЛЕНИЕ 1 Только что проверил с помощью ansible.cfg:

[defaults]
host_key_checking=false
hash_behaviour=merge

группы_вари / все

db:
    root_pwd: some_strong_pwd
    pdo_driver: pdo_mysql
    host: localhost
    name: dbname
    user: dbuser
    pwd: some password
    charset: utf8

host_vars / vagrantbox

db:
    root_pwd: root

Я получаю следующую ошибку:

One or more undefined variables: 'dict object' has no attribute 'name'

Что я делаю не так?


person Jevgeni Smirnov    schedule 05.08.2014    source источник


Ответы (4)


По умолчанию Ansible переопределяет переменные на первом уровне. Если вы хотите иметь возможность объединять словари, вам нужно изменить свой ansible.cfg файл и установить:

hash_behaviour=merge

(значение по умолчанию replace).

Обратите внимание, что команда Ansible не рекомендует это (но не объясняет почему). Думаю, это настоящий разделитель между пользователями. Это своего рода решение, которое принимается раз и навсегда: когда вы начинаете использовать эту функцию, вы не можете вернуться, и вы, вероятно, не сможете поделиться своим playbook с людьми типа replace.

Тем не менее, вы все равно можете извлечь выгоду из имеющихся playbooks (я не думаю, что playbooks используют поведение replace как «функцию»). Это как иметь группу крови AB, быть универсальным получателем ... но поскольку магия обычно происходит при переменном разрешении, а не внутри задач или шаблонов, я думаю, что часто можно делиться своими ролями без каких-либо изменений.

Если вам нужно переопределить один ключ, скажем, из параметров роли, вам придется передавать параметры некоторым запутанным способом.

Например, чтобы переопределить ключи post_max_size и upload_max_size в php5 словаре для конкретной роли, вам нужно будет сделать это следующим образом:

- { role: php5-fpm, php5: { post_max_size: 40M,
                            upload_max_filesize: 20M }}

При этом я использую поведение merge с самого начала, и мне оно очень нравится. Очень удобно хранить переменные в порядке.

person leucos    schedule 05.08.2014
comment
пожалуйста, проверьте комментарий. - person Jevgeni Smirnov; 06.08.2014
comment
не могли бы вы добавить - debug: var=db в начало своей пьесы? Также убедитесь, что ваш файл ansible.cfg находится в нужном месте (docs.ansible.com/intro_configuration.html) и опубликуйте свою доступную версию. - person leucos; 06.08.2014
comment
Посмотрите эту ветку, чтобы узнать, почему это считается плохой практикой: groups.google.com/forum / #! topic / ansible-project / 1f8Y1JCkwvI TL; DR: он поощряет сложное поведение, которое создатели ansible считают плохим. - person Lewis; 22.10.2014
comment
Я полагаю, что все считают сложное поведение плохим. Но сказать, что использовать хеши сложнее, чем простые вары, на самом деле субъективно. Лично я не думаю, что использование хэшей сложно или приводит к сложному поведению. Опять же, вы можете сгруппировать свои конфигурации, и в конечном итоге это сделает вещи более удобочитаемыми, когда у вас есть сложные настройки и есть интересный побочный эффект области видимости. Однако у него есть один БОЛЬШОЙ недостаток: вы не можете повторно использовать какое-то значение хеш-ключа в качестве другого значения для ключа в том же хеш-коде (что действительно может раздражать!) - person leucos; 22.10.2014
comment
Я не понимаю обратную сторону, хотите уточнить? Вы никогда не сможете использовать один и тот же ключ дважды в одном и том же хэше. - person grasshopper; 06.03.2015
comment
@grasshopper: например, вы не можете определить: foo.bar: 5 и в том же dict foo.baz: {{foo.bar}} (извините, нельзя помещать код в комментарии; вы должны представить это "ямл-стиль". - person leucos; 06.03.2015
comment
Ах хорошо. Спасибо за разъяснения. Однако я не вижу варианта использования, где бы я это сделал. - person grasshopper; 06.03.2015
comment
Конечно, есть смысл добавить оператор, чтобы явное слияние стало возможным только там, где это необходимо? Иметь это как некую глобальную магию просто странно. - person Craig Ringer; 22.04.2015
comment
Это поведение слияния хэшей по умолчанию в Chef через _1 _ class и Chef::Mixin::DeepMerge. Существуют правила приоритета, которые обеспечивают довольно простой порядок переопределения для атрибутов из нескольких мест и уровней переопределения. С чисто объектно-ориентированной точки зрения, при работе с несколькими кулинарными книгами очень полезно иметь это поведение глубокого слияния, потому что оно позволяет следовать шаблону переопределения определенного хеш-ключа в значимом месте. - person TrinitronX; 28.10.2015
comment
Вот один пример, который я привел: поваренная книга-оболочка (наследующая атрибуты из другой поваренной книги базового класса) способна переопределить определенный хэш-ключ глубоко внутри хэш-структуры атрибутов узла, чтобы обеспечить определенное поведение или конфигурацию. Это можно метафорически сравнить с шаблоном переопределения метода более конкретного класса (который наследует от базового класса), переопределив метод базового класса, чтобы предоставить больше специфическое поведение - person TrinitronX; 28.10.2015
comment
Я понимаю, что это кажется сложным для тех, кто не знаком с ООП, но он позволяет более элегантно, конкретно, декларативно и многократно использовать конфигурацию сервера с помощью кулинарных книг или ролей, которые следуют за Принцип единой ответственности. Это позволяет просто обернуть и наследовать атрибуты / переменные из другой роли / поваренной книги и переопределить только то, что вам нужно, чтобы предоставить более краткое описание ресурсов, которые нужно настроить / объединить. Однако это связано со сложностью приоритета. - person TrinitronX; 28.10.2015
comment
вероятно, можно было бы привести те же аргументы о наследовании CSS или любой концепции иерархии наследования ООП. - person TrinitronX; 28.10.2015

Начиная с Ansible 2.0, вы можете использовать фильтр Jinja2 combine для объединения хэшей / словарей YAML без необходимости устанавливать hash_behavior=merge на глобальном уровне в вашем ansible.cfg файле.

Соответствующие документы: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#sts=Combining%20hashes/dictionaries%C2%B6.

person Jeff Widman    schedule 26.09.2015
comment
Для Ansible ‹2.0 можно использовать include_vars_merged: gist.github.com/udondan/b730206056a018cf9838 - person udondan; 26.09.2015
comment
Вы случайно не знаете, как применить combine к параметру роли, как описано в stackoverflow.com/a/25131711/107158? - person Derek Mahar; 20.12.2016
comment
Заголовок stackoverflow.com/questions/41247778/ отвечает на этот вопрос. - person Derek Mahar; 20.12.2016

Лучший способ, который я нашел, - использовать переменные в качестве значения элемента словаря и переопределить это. Я считаю, что это обеспечивает простой и мощный приоритет переменных в отношении порядка переменных в анзибле.

роль / родитель / по умолчанию / main.yml

---
root_pw_value: ParentPassword

parent_dict:
  - root_pw: "{{ root_pw_value }}"

роль / ребенок / значения по умолчанию / main.yml

Примечание: role/child/meta/main.yml содержит dependencies: - { role: parent }

---
root_pw_value: ChildPassword

play-me.yml

---
  - hosts: all
    roles:
      - child

роли / родитель / задачи / main.yml и роли / дочерний / задачи / main.yml

- debug: var=parent_dict

Запустите ansible -i localhost, --connection="local" play-me.yml, и вы получите следующий результат:

PLAY [all] ******************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [localhost]

TASK: [parent | debug var=parent_dict] **************************************** 
ok: [localhost] => {
    "var": {
        "parent_dict": [
            {
                "root_pw": "ParentPassword"
            }
        ]
    }
}

TASK: [child | debug var=parent_dict] ***************************************** 
ok: [localhost] => {
    "var": {
        "parent_dict": [
            {
                "root_pw": "ChildPassword"
            }
        ]
    }
}

PLAY RECAP ******************************************************************** 
localhost                  : ok=3    changed=0    unreachable=0    failed=0

И это значения по умолчанию. Если вы укажете root_pw_value на более конкретных уровнях приоритета, таких как переменные группы инвентаризации / хоста, переменные роли, extra_vars в командной строке или что-нибудь из порядка приоритета [0], вы получите их.

[0] http://docs.ansible.com/ansible/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable

person MeanderingCode    schedule 13.10.2015

Просто попробовал с ansible 1.9.3, и он отлично работает. Не уверен, но похоже, что у вас просто опечатка в имени каталога group_vars (не groups_vars).

person Borys Borysenko    schedule 04.10.2015