diff --git a/docs/docsite/rst/ansible_index.rst b/docs/docsite/rst/ansible_index.rst index 51df636f78..e6b97450c5 100644 --- a/docs/docsite/rst/ansible_index.rst +++ b/docs/docsite/rst/ansible_index.rst @@ -41,7 +41,6 @@ Ansible releases a new major release approximately twice a year. The core applic :maxdepth: 2 :caption: Using Ansible - user_guide/index inventory_guide/index command_guide/index playbook_guide/index diff --git a/docs/docsite/rst/core_index.rst b/docs/docsite/rst/core_index.rst index b286a1983f..37e76ce755 100644 --- a/docs/docsite/rst/core_index.rst +++ b/docs/docsite/rst/core_index.rst @@ -48,7 +48,6 @@ This documentation covers the version of ``ansible-core`` noted in the upper lef :maxdepth: 2 :caption: Using Ansible Core - user_guide/index inventory_guide/index command_guide/index playbook_guide/index diff --git a/docs/docsite/rst/locales/ja/LC_MESSAGES/porting_guides.po b/docs/docsite/rst/locales/ja/LC_MESSAGES/porting_guides.po index 17566e883d..d9e9c03767 100644 --- a/docs/docsite/rst/locales/ja/LC_MESSAGES/porting_guides.po +++ b/docs/docsite/rst/locales/ja/LC_MESSAGES/porting_guides.po @@ -3134,95 +3134,95 @@ msgstr "キーワードの完全なリストは、「:ref:`playbook_keywords`」 msgid "Migrating from with_X to loop" msgstr "with_X から loop への移行" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:1 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:1 msgid "In most cases, loops work best with the ``loop`` keyword instead of ``with_X`` style loops. The ``loop`` syntax is usually best expressed using filters instead of more complex use of ``query`` or ``lookup``." msgstr "ほとんどの場合、ループは、``with_X`` スタイルのループではなく、``loop`` キーワードで最適に機能します。``loop`` 構文は通常、``query`` や ``lookup`` の複雑な使用ではなく、フィルターを使用して表現できます。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:3 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:3 msgid "These examples show how to convert many common ``with_`` style loops to ``loop`` and filters." msgstr "以下の例では、一般的な ``with_`` スタイルのループを ``loop`` およびフィルターに変換する方法を示しています。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:6 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:6 msgid "with_list" msgstr "with_list" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:8 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:8 msgid "``with_list`` is directly replaced by ``loop``." msgstr "``with_list`` は、直接 ``loop`` に置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:27 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:27 msgid "with_items" msgstr "with_items" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:29 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:29 msgid "``with_items`` is replaced by ``loop`` and the ``flatten`` filter." msgstr "``with_items`` は、``loop`` および ``flatten`` フィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:44 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:44 msgid "with_indexed_items" msgstr "with_indexed_items" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:46 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:46 msgid "``with_indexed_items`` is replaced by ``loop``, the ``flatten`` filter and ``loop_control.index_var``." msgstr "``with_indexed_items`` は、``loop``、``flatten`` フィルター、および ``loop_control.index_var`` に置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:63 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:63 msgid "with_flattened" msgstr "with_flattened" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:65 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:65 msgid "``with_flattened`` is replaced by ``loop`` and the ``flatten`` filter." msgstr "``with_flattened`` は、``loop`` および ``flatten`` フィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:80 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:80 msgid "with_together" msgstr "with_together" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:82 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:82 msgid "``with_together`` is replaced by ``loop`` and the ``zip`` filter." msgstr "``with_together`` は、``loop`` および ``zip`` フィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:98 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:98 msgid "Another example with complex data" msgstr "複雑なデータがある別の例" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:113 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:113 msgid "with_dict" msgstr "with_dict" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:115 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:115 msgid "``with_dict`` can be substituted by ``loop`` and either the ``dictsort`` or ``dict2items`` filters." msgstr "``with_dict`` は、``loop`` と、``dictsort`` または``dict2items`` のいずれかのフィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:135 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:135 msgid "with_sequence" msgstr "with_sequence" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:137 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:137 msgid "``with_sequence`` is replaced by ``loop`` and the ``range`` function, and potentially the ``format`` filter." msgstr "``with_sequence`` は、``loop`` と``range`` の関数、そして潜在的には``format`` フィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:153 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:153 msgid "with_subelements" msgstr "with_subelements" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:155 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:155 msgid "``with_subelements`` is replaced by ``loop`` and the ``subelements`` filter." msgstr "``with_subelements`` は、``loop`` および ``subelements`` フィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:172 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:172 msgid "with_nested/with_cartesian" msgstr "with_nested/with_cartesian" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:174 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:174 msgid "``with_nested`` and ``with_cartesian`` are replaced by loop and the ``product`` filter." msgstr "``with_nested`` と``with_cartesian`` は、ループと ``product`` のフィルターに置き換えられました。" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:191 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:191 msgid "with_random_choice" msgstr "with_random_choice" -#: ../../rst/user_guide/shared_snippets/with2loop.txt:193 +#: ../../rst/playbook_guide/shared_snippets/with2loop.txt:193 msgid "``with_random_choice`` is replaced by just use of the ``random`` filter, without need of ``loop``." msgstr "``with_random_choice`` は、``random`` フィルターを使用するだけで、``loop`` を必要とせずに置き換えることができます。" diff --git a/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst b/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst new file mode 100644 index 0000000000..2f508e48f8 --- /dev/null +++ b/docs/docsite/rst/playbook_guide/complex_data_manipulation.rst @@ -0,0 +1,314 @@ +.. _complex_data_manipulation: + +Manipulating data +################# + +In many cases, you need to do some complex operation with your variables, while Ansible is not recommended as a data processing/manipulation tool, you can use the existing Jinja2 templating in conjunction with the many added Ansible filters, lookups and tests to do some very complex transformations. + +Let's start with a quick definition of each type of plugin: + - lookups: Mainly used to query 'external data', in Ansible these were the primary part of loops using the ``with_`` construct, but they can be used independently to return data for processing. They normally return a list due to their primary function in loops as mentioned previously. Used with the ``lookup`` or ``query`` Jinja2 operators. + - filters: used to change/transform data, used with the ``|`` Jinja2 operator. + - tests: used to validate data, used with the ``is`` Jinja2 operator. + +.. _note: + * Some tests and filters are provided directly by Jinja2, so their availability depends on the Jinja2 version, not Ansible. + +.. _for_loops_or_list_comprehensions: + +Loops and list comprehensions +============================= + +Most programming languages have loops (``for``, ``while``, and so on) and list comprehensions to do transformations on lists including lists of objects. Jinja2 has a few filters that provide this functionality: ``map``, ``select``, ``reject``, ``selectattr``, ``rejectattr``. + +- map: this is a basic for loop that just allows you to change every item in a list, using the 'attribute' keyword you can do the transformation based on attributes of the list elements. +- select/reject: this is a for loop with a condition, that allows you to create a subset of a list that matches (or not) based on the result of the condition. +- selectattr/rejectattr: very similar to the above but it uses a specific attribute of the list elements for the conditional statement. + + +.. _exponential_backoff: + +Use a loop to create exponential backoff for retries/until. + +.. code-block:: yaml + + - name: retry ping 10 times with exponential backoff delay + ping: + retries: 10 + delay: '{{item|int}}' + loop: '{{ range(1, 10)|map('pow', 2) }}' + + +.. _keys_from_dict_matching_list: + +Extract keys from a dictionary matching elements from a list +------------------------------------------------------------ + +The Python equivalent code would be: + +.. code-block:: python + + chains = [1, 2] + for chain in chains: + for config in chains_config[chain]['configs']: + print(config['type']) + +There are several ways to do it in Ansible, this is just one example: + +.. code-block:: YAML+Jinja + :emphasize-lines: 4 + :caption: Way to extract matching keys from a list of dictionaries + + tasks: + - name: Show extracted list of keys from a list of dictionaries + ansible.builtin.debug: + msg: "{{ chains | map('extract', chains_config) | map(attribute='configs') | flatten | map(attribute='type') | flatten }}" + vars: + chains: [1, 2] + chains_config: + 1: + foo: bar + configs: + - type: routed + version: 0.1 + - type: bridged + version: 0.2 + 2: + foo: baz + configs: + - type: routed + version: 1.0 + - type: bridged + version: 1.1 + + +.. code-block:: ansible-output + :caption: Results of debug task, a list with the extracted keys + + ok: [localhost] => { + "msg": [ + "routed", + "bridged", + "routed", + "bridged" + ] + } + + +.. code-block:: YAML+Jinja + :caption: Get the unique list of values of a variable that vary per host + + vars: + unique_value_list: "{{ groups['all'] | map ('extract', hostvars, 'varname') | list | unique}}" + + +.. _find_mount_point: + +Find mount point +---------------- + +In this case, we want to find the mount point for a given path across our machines, since we already collect mount facts, we can use the following: + +.. code-block:: YAML+Jinja + :caption: Use selectattr to filter mounts into list I can then sort and select the last from + :emphasize-lines: 8 + + - hosts: all + gather_facts: True + vars: + path: /var/lib/cache + tasks: + - name: The mount point for {{path}}, found using the Ansible mount facts, [-1] is the same as the 'last' filter + ansible.builtin.debug: + msg: "{{(ansible_facts.mounts | selectattr('mount', 'in', path) | list | sort(attribute='mount'))[-1]['mount']}}" + + +.. _omit_elements_from_list: + +Omit elements from a list +------------------------- + +The special ``omit`` variable ONLY works with module options, but we can still use it in other ways as an identifier to tailor a list of elements: + +.. code-block:: YAML+Jinja + :caption: Inline list filtering when feeding a module option + :emphasize-lines: 3, 6 + + - name: Enable a list of Windows features, by name + ansible.builtin.set_fact: + win_feature_list: "{{ namestuff | reject('equalto', omit) | list }}" + vars: + namestuff: + - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}" + - "foo" + - "bar" + + +Another way is to avoid adding elements to the list in the first place, so you can just use it directly: + +.. code-block:: YAML+Jinja + :caption: Using set_fact in a loop to increment a list conditionally + :emphasize-lines: 3, 4, 6 + + - name: Build unique list with some items conditionally omitted + ansible.builtin.set_fact: + namestuff: ' {{ (namestuff | default([])) | union([item]) }}' + when: item != omit + loop: + - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}" + - "foo" + - "bar" + + + +.. _combine_optional_values: + +Combine values from same list of dicts +--------------------------------------- +Combining positive and negative filters from examples above, you can get a 'value when it exists' and a 'fallback' when it doesn't. + +.. code-block:: YAML+Jinja + :caption: Use selectattr and rejectattr to get the ansible_host or inventory_hostname as needed + + - hosts: localhost + tasks: + - name: Check hosts in inventory that respond to ssh port + wait_for: + host: "{{ item }}" + port: 22 + loop: '{{ has_ah + no_ah }}' + vars: + has_ah: '{{ hostvars|dictsort|selectattr("1.ansible_host", "defined")|map(attribute="1.ansible_host")|list }}' + no_ah: '{{ hostvars|dictsort|rejectattr("1.ansible_host", "defined")|map(attribute="0")|list }}' + + +.. _custom_fileglob_variable: + +Custom Fileglob Based on a Variable +----------------------------------- + +This example uses `Python argument list unpacking `_ to create a custom list of fileglobs based on a variable. + +.. code-block:: YAML+Jinja + :caption: Using fileglob with a list based on a variable. + + - hosts: all + vars: + mygroups: + - prod + - web + tasks: + - name: Copy a glob of files based on a list of groups + copy: + src: "{{ item }}" + dest: "/tmp/{{ item }}" + loop: '{{ q("fileglob", *globlist) }}' + vars: + globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf") | list }}' + + +.. _complex_type_transformations: + +Complex Type transformations +============================= + +Jinja provides filters for simple data type transformations (``int``, ``bool``, and so on), but when you want to transform data structures things are not as easy. +You can use loops and list comprehensions as shown above to help, also other filters and lookups can be chained and used to achieve more complex transformations. + + +.. _create_dictionary_from_list: + +Create dictionary from list +--------------------------- + +In most languages it is easy to create a dictionary (a.k.a. map/associative array/hash and so on) from a list of pairs, in Ansible there are a couple of ways to do it and the best one for you might depend on the source of your data. + + +These example produces ``{"a": "b", "c": "d"}`` + +.. code-block:: YAML+Jinja + :caption: Simple list to dict by assuming the list is [key, value , key, value, ...] + + vars: + single_list: [ 'a', 'b', 'c', 'd' ] + mydict: "{{ dict(single_list | slice(2)) }}" + + +.. code-block:: YAML+Jinja + :caption: It is simpler when we have a list of pairs: + + vars: + list_of_pairs: [ ['a', 'b'], ['c', 'd'] ] + mydict: "{{ dict(list_of_pairs) }}" + +Both end up being the same thing, with ``slice(2)`` transforming ``single_list`` to a ``list_of_pairs`` generator. + + + +A bit more complex, using ``set_fact`` and a ``loop`` to create/update a dictionary with key value pairs from 2 lists: + +.. code-block:: YAML+Jinja + :caption: Using set_fact to create a dictionary from a set of lists + :emphasize-lines: 3, 4 + + - name: Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists + ansible.builtin.set_fact: + mydict: "{{ mydict | default({}) | combine({item[0]: item[1]}) }}" + loop: "{{ (keys | zip(values)) | list }}" + vars: + keys: + - foo + - var + - bar + values: + - a + - b + - c + +This results in ``{"foo": "a", "var": "b", "bar": "c"}``. + + +You can even combine these simple examples with other filters and lookups to create a dictionary dynamically by matching patterns to variable names: + +.. code-block:: YAML+Jinja + :caption: Using 'vars' to define dictionary from a set of lists without needing a task + + vars: + xyz_stuff: 1234 + xyz_morestuff: 567 + myvarnames: "{{ q('varnames', '^xyz_') }}" + mydict: "{{ dict(myvarnames|map('regex_replace', '^xyz_', '')|list | zip(q('vars', *myvarnames))) }}" + +A quick explanation, since there is a lot to unpack from these two lines: + + - The ``varnames`` lookup returns a list of variables that match "begin with ``xyz_``". + - Then feeding the list from the previous step into the ``vars`` lookup to get the list of values. + The ``*`` is used to 'dereference the list' (a pythonism that works in Jinja), otherwise it would take the list as a single argument. + - Both lists get passed to the ``zip`` filter to pair them off into a unified list (key, value, key2, value2, ...). + - The dict function then takes this 'list of pairs' to create the dictionary. + + +An example on how to use facts to find a host's data that meets condition X: + + +.. code-block:: YAML+Jinja + + vars: + uptime_of_host_most_recently_rebooted: "{{ansible_play_hosts_all | map('extract', hostvars, 'ansible_uptime_seconds') | sort | first}}" + +An example to show a host uptime in days/hours/minutes/seconds (assumes facts where gathered). + +.. code-block:: YAML+Jinja + + - name: Show the uptime in days/hours/minutes/seconds + ansible.builtin.debug: + msg: Uptime {{ now().replace(microsecond=0) - now().fromtimestamp(now(fmt='%s') | int - ansible_uptime_seconds) }} + + +.. seealso:: + + :ref:`playbooks_filters` + Jinja2 filters included with Ansible + :ref:`playbooks_tests` + Jinja2 tests included with Ansible + `Jinja2 Docs `_ + Jinja2 documentation, includes lists for core filters and tests diff --git a/docs/docsite/rst/playbook_guide/index.rst b/docs/docsite/rst/playbook_guide/index.rst index 02246bf528..56f4d2e9b4 100644 --- a/docs/docsite/rst/playbook_guide/index.rst +++ b/docs/docsite/rst/playbook_guide/index.rst @@ -28,4 +28,5 @@ You can also learn how to use Ansible playbooks more effectively by creating re- playbooks_intro playbooks playbooks_execution - playbooks_advanced_syntax \ No newline at end of file + playbooks_advanced_syntax + complex_data_manipulation \ No newline at end of file diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.5.rst b/docs/docsite/rst/porting_guides/porting_guide_2.5.rst index e5b99a52ea..7a58229d35 100644 --- a/docs/docsite/rst/porting_guides/porting_guide_2.5.rst +++ b/docs/docsite/rst/porting_guides/porting_guide_2.5.rst @@ -94,7 +94,7 @@ For a full list of keywords see :ref:`playbook_keywords`. Migrating from with_X to loop ----------------------------- -.. include:: ../user_guide/shared_snippets/with2loop.txt +.. include:: ../playbook_guide/shared_snippets/with2loop.txt Deprecated diff --git a/docs/docsite/rst/tips_tricks/index.rst b/docs/docsite/rst/tips_tricks/index.rst index dd44f90f35..36d8325269 100644 --- a/docs/docsite/rst/tips_tricks/index.rst +++ b/docs/docsite/rst/tips_tricks/index.rst @@ -19,4 +19,5 @@ Ultimately, though, you should use Ansible in the way that makes most sense for .. toctree:: :maxdepth: 2 - ansible_tips_tricks \ No newline at end of file + ansible_tips_tricks + sample_setup \ No newline at end of file diff --git a/docs/docsite/rst/tips_tricks/sample_setup.rst b/docs/docsite/rst/tips_tricks/sample_setup.rst new file mode 100644 index 0000000000..1f267a72c7 --- /dev/null +++ b/docs/docsite/rst/tips_tricks/sample_setup.rst @@ -0,0 +1,297 @@ +.. _sample_setup: + +******************** +Sample Ansible setup +******************** + +You have learned about playbooks, inventory, roles, and variables. This section pulls all those elements together, outlining a sample setup for automating a web service. You can find more example playbooks illustrating these patterns in our `ansible-examples repository `_. (NOTE: These may not use all of the features in the latest release, but are still an excellent reference!). + +The sample setup organizes playbooks, roles, inventory, and variables files by function, with tags at the play and task level for greater granularity and control. This is a powerful and flexible approach, but there are other ways to organize Ansible content. Your usage of Ansible should fit your needs, not ours, so feel free to modify this approach and organize your content as you see fit. + +.. contents:: + :local: + +Sample directory layout +----------------------- + +This layout organizes most tasks in roles, with a single inventory file for each environment and a few playbooks in the top-level directory: + +.. code-block:: console + + production # inventory file for production servers + staging # inventory file for staging environment + + group_vars/ + group1.yml # here we assign variables to particular groups + group2.yml + host_vars/ + hostname1.yml # here we assign variables to particular systems + hostname2.yml + + library/ # if any custom modules, put them here (optional) + module_utils/ # if any custom module_utils to support modules, put them here (optional) + filter_plugins/ # if any custom filter plugins, put them here (optional) + + site.yml # main playbook + webservers.yml # playbook for webserver tier + dbservers.yml # playbook for dbserver tier + tasks/ # task files included from playbooks + webservers-extra.yml # <-- avoids confusing playbook with task files +.. include:: shared_snippets/role_directory.txt + +.. note:: By default, Ansible assumes your playbooks are stored in one directory with roles stored in a sub-directory called ``roles/``. As you use Ansible to automate more tasks, you may want to move your playbooks into a sub-directory called ``playbooks/``. If you do this, you must configure the path to your ``roles/`` directory using the ``roles_path`` setting in ansible.cfg. + +Alternative directory layout +---------------------------- + +Alternatively you can put each inventory file with its ``group_vars``/``host_vars`` in a separate directory. This is particularly useful if your ``group_vars``/``host_vars`` don't have that much in common in different environments. The layout could look something like this: + + .. code-block:: console + + inventories/ + production/ + hosts # inventory file for production servers + group_vars/ + group1.yml # here we assign variables to particular groups + group2.yml + host_vars/ + hostname1.yml # here we assign variables to particular systems + hostname2.yml + + staging/ + hosts # inventory file for staging environment + group_vars/ + group1.yml # here we assign variables to particular groups + group2.yml + host_vars/ + stagehost1.yml # here we assign variables to particular systems + stagehost2.yml + + library/ + module_utils/ + filter_plugins/ + + site.yml + webservers.yml + dbservers.yml + + roles/ + common/ + webtier/ + monitoring/ + fooapp/ + +This layout gives you more flexibility for larger environments, as well as a total separation of inventory variables between different environments. However, this approach is harder to maintain, because there are more files. For more information on organizing group and host variables, see :ref:`splitting_out_vars`. + +.. _groups_and_hosts: + +Sample group and host variables +------------------------------- + +These sample group and host variables files record the variable values that apply to each machine or group of machines. For instance, the data center in Atlanta has its own NTP servers, so when setting up ntp.conf, we should use them: + + .. code-block:: yaml + + --- + # file: group_vars/atlanta + ntp: ntp-atlanta.example.com + backup: backup-atlanta.example.com + +Similarly, the webservers have some configuration that does not apply to the database servers: + + .. code-block:: yaml + + --- + # file: group_vars/webservers + apacheMaxRequestsPerChild: 3000 + apacheMaxClients: 900 + +Default values, or values that are universally true, belong in a file called group_vars/all: + + .. code-block:: yaml + + --- + # file: group_vars/all + ntp: ntp-boston.example.com + backup: backup-boston.example.com + +If necessary, you can define specific hardware variance in systems in a host_vars file: + + .. code-block:: yaml + + --- + # file: host_vars/db-bos-1.example.com + foo_agent_port: 86 + bar_agent_port: 99 + +Again, if you are using :ref:`dynamic inventory `, Ansible creates many dynamic groups automatically. So a tag like "class:webserver" would load in variables from the file "group_vars/ec2_tag_class_webserver" automatically. + +.. _split_by_role: + +Sample playbooks organized by function +-------------------------------------- + +With this setup, a single playbook can define all the infrastructure. The site.yml playbook imports two other playbooks, one for the webservers and one for the database servers: + + .. code-block:: yaml + + --- + # file: site.yml + - import_playbook: webservers.yml + - import_playbook: dbservers.yml + +The webservers.yml file, also at the top level, maps the configuration of the webservers group to the roles related to the webservers group: + + .. code-block:: yaml + + --- + # file: webservers.yml + - hosts: webservers + roles: + - common + - webtier + +With this setup, you can configure your whole infrastructure by "running" site.yml, or run a subset by running webservers.yml. This is analogous to the Ansible "--limit" parameter but a little more explicit: + + .. code-block:: shell + + ansible-playbook site.yml --limit webservers + ansible-playbook webservers.yml + +.. _role_organization: + +Sample task and handler files in a function-based role +------------------------------------------------------ + +Ansible loads any file called ``main.yml`` in a role sub-directory. This sample ``tasks/main.yml`` file is simple - it sets up NTP, but it could do more if we wanted: + + .. code-block:: yaml + + --- + # file: roles/common/tasks/main.yml + + - name: be sure ntp is installed + yum: + name: ntp + state: present + tags: ntp + + - name: be sure ntp is configured + template: + src: ntp.conf.j2 + dest: /etc/ntp.conf + notify: + - restart ntpd + tags: ntp + + - name: be sure ntpd is running and enabled + service: + name: ntpd + state: started + enabled: yes + tags: ntp + +Here is an example handlers file. As a review, handlers are only fired when certain tasks report changes, and are run at the end +of each play: + + .. code-block:: yaml + + --- + # file: roles/common/handlers/main.yml + - name: restart ntpd + service: + name: ntpd + state: restarted + +See :ref:`playbooks_reuse_roles` for more information. + + +.. _organization_examples: + +What the sample setup enables +----------------------------- + +The basic organizational structure described above enables a lot of different automation options. To reconfigure your entire infrastructure: + + .. code-block:: shell + + ansible-playbook -i production site.yml + +To reconfigure NTP on everything: + + .. code-block:: shell + + ansible-playbook -i production site.yml --tags ntp + +To reconfigure only the webservers: + + .. code-block:: shell + + ansible-playbook -i production webservers.yml + +To reconfigure only the webservers in Boston: + + .. code-block:: shell + + ansible-playbook -i production webservers.yml --limit boston + +To reconfigure only the first 10 webservers in Boston, and then the next 10: + + .. code-block:: shell + + ansible-playbook -i production webservers.yml --limit boston[0:9] + ansible-playbook -i production webservers.yml --limit boston[10:19] + +The sample setup also supports basic ad hoc commands: + + .. code-block:: shell + + ansible boston -i production -m ping + ansible boston -i production -m command -a '/sbin/reboot' + +To discover what tasks would run or what hostnames would be affected by a particular Ansible command: + + .. code-block:: shell + + # confirm what task names would be run if I ran this command and said "just ntp tasks" + ansible-playbook -i production webservers.yml --tags ntp --list-tasks + + # confirm what hostnames might be communicated with if I said "limit to boston" + ansible-playbook -i production webservers.yml --limit boston --list-hosts + +.. _dep_vs_config: + +Organizing for deployment or configuration +------------------------------------------ + +The sample setup models a typical configuration topology. When doing multi-tier deployments, there are going +to be some additional playbooks that hop between tiers to roll out an application. In this case, 'site.yml' +may be augmented by playbooks like 'deploy_exampledotcom.yml' but the general concepts still apply. Ansible allows you to deploy and configure using the same tool, so you would likely reuse groups and keep the OS configuration in separate playbooks or roles from the app deployment. + +Consider "playbooks" as a sports metaphor -- you can have one set of plays to use against all your infrastructure and situational plays that you use at different times and for different purposes. + +.. _ship_modules_with_playbooks: + +Using local Ansible modules +--------------------------- + +If a playbook has a :file:`./library` directory relative to its YAML file, this directory can be used to add Ansible modules that will +automatically be in the Ansible module path. This is a great way to keep modules that go with a playbook together. This is shown +in the directory structure example at the start of this section. + +.. seealso:: + + :ref:`yaml_syntax` + Learn about YAML syntax + :ref:`working_with_playbooks` + Review the basic playbook features + :ref:`list_of_collections` + Browse existing collections, modules, and plugins + :ref:`developing_modules` + Learn how to extend Ansible by writing your own modules + :ref:`intro_patterns` + Learn about how to select hosts + `GitHub examples directory `_ + Complete playbook files from the github project source + `Mailing List `_ + Questions? Help? Ideas? Stop by the list on Google Groups diff --git a/docs/docsite/rst/user_guide/shared_snippets/role_directory.txt b/docs/docsite/rst/tips_tricks/shared_snippets/role_directory.txt similarity index 100% rename from docs/docsite/rst/user_guide/shared_snippets/role_directory.txt rename to docs/docsite/rst/tips_tricks/shared_snippets/role_directory.txt diff --git a/docs/docsite/rst/user_guide/complex_data_manipulation.rst b/docs/docsite/rst/user_guide/complex_data_manipulation.rst index 933fbf91d1..1cfa0d22f7 100644 --- a/docs/docsite/rst/user_guide/complex_data_manipulation.rst +++ b/docs/docsite/rst/user_guide/complex_data_manipulation.rst @@ -1,314 +1,6 @@ -.. _complex_data_manipulation: +:orphan: Data manipulation -######################### +################# -In many cases, you need to do some complex operation with your variables, while Ansible is not recommended as a data processing/manipulation tool, you can use the existing Jinja2 templating in conjunction with the many added Ansible filters, lookups and tests to do some very complex transformations. - -Let's start with a quick definition of each type of plugin: - - lookups: Mainly used to query 'external data', in Ansible these were the primary part of loops using the ``with_`` construct, but they can be used independently to return data for processing. They normally return a list due to their primary function in loops as mentioned previously. Used with the ``lookup`` or ``query`` Jinja2 operators. - - filters: used to change/transform data, used with the ``|`` Jinja2 operator. - - tests: used to validate data, used with the ``is`` Jinja2 operator. - -.. _note: - * Some tests and filters are provided directly by Jinja2, so their availability depends on the Jinja2 version, not Ansible. - -.. _for_loops_or_list_comprehensions: - -Loops and list comprehensions -============================= - -Most programming languages have loops (``for``, ``while``, and so on) and list comprehensions to do transformations on lists including lists of objects. Jinja2 has a few filters that provide this functionality: ``map``, ``select``, ``reject``, ``selectattr``, ``rejectattr``. - -- map: this is a basic for loop that just allows you to change every item in a list, using the 'attribute' keyword you can do the transformation based on attributes of the list elements. -- select/reject: this is a for loop with a condition, that allows you to create a subset of a list that matches (or not) based on the result of the condition. -- selectattr/rejectattr: very similar to the above but it uses a specific attribute of the list elements for the conditional statement. - - -.. _exponential_backoff: - -Use a loop to create exponential backoff for retries/until. - -.. code-block:: yaml - - - name: retry ping 10 times with exponential backoff delay - ping: - retries: 10 - delay: '{{item|int}}' - loop: '{{ range(1, 10)|map('pow', 2) }}' - - -.. _keys_from_dict_matching_list: - -Extract keys from a dictionary matching elements from a list ------------------------------------------------------------- - -The Python equivalent code would be: - -.. code-block:: python - - chains = [1, 2] - for chain in chains: - for config in chains_config[chain]['configs']: - print(config['type']) - -There are several ways to do it in Ansible, this is just one example: - -.. code-block:: YAML+Jinja - :emphasize-lines: 4 - :caption: Way to extract matching keys from a list of dictionaries - - tasks: - - name: Show extracted list of keys from a list of dictionaries - ansible.builtin.debug: - msg: "{{ chains | map('extract', chains_config) | map(attribute='configs') | flatten | map(attribute='type') | flatten }}" - vars: - chains: [1, 2] - chains_config: - 1: - foo: bar - configs: - - type: routed - version: 0.1 - - type: bridged - version: 0.2 - 2: - foo: baz - configs: - - type: routed - version: 1.0 - - type: bridged - version: 1.1 - - -.. code-block:: ansible-output - :caption: Results of debug task, a list with the extracted keys - - ok: [localhost] => { - "msg": [ - "routed", - "bridged", - "routed", - "bridged" - ] - } - - -.. code-block:: YAML+Jinja - :caption: Get the unique list of values of a variable that vary per host - - vars: - unique_value_list: "{{ groups['all'] | map ('extract', hostvars, 'varname') | list | unique}}" - - -.. _find_mount_point: - -Find mount point ----------------- - -In this case, we want to find the mount point for a given path across our machines, since we already collect mount facts, we can use the following: - -.. code-block:: YAML+Jinja - :caption: Use selectattr to filter mounts into list I can then sort and select the last from - :emphasize-lines: 8 - - - hosts: all - gather_facts: True - vars: - path: /var/lib/cache - tasks: - - name: The mount point for {{path}}, found using the Ansible mount facts, [-1] is the same as the 'last' filter - ansible.builtin.debug: - msg: "{{(ansible_facts.mounts | selectattr('mount', 'in', path) | list | sort(attribute='mount'))[-1]['mount']}}" - - -.. _omit_elements_from_list: - -Omit elements from a list -------------------------- - -The special ``omit`` variable ONLY works with module options, but we can still use it in other ways as an identifier to tailor a list of elements: - -.. code-block:: YAML+Jinja - :caption: Inline list filtering when feeding a module option - :emphasize-lines: 3, 6 - - - name: Enable a list of Windows features, by name - ansible.builtin.set_fact: - win_feature_list: "{{ namestuff | reject('equalto', omit) | list }}" - vars: - namestuff: - - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}" - - "foo" - - "bar" - - -Another way is to avoid adding elements to the list in the first place, so you can just use it directly: - -.. code-block:: YAML+Jinja - :caption: Using set_fact in a loop to increment a list conditionally - :emphasize-lines: 3, 4, 6 - - - name: Build unique list with some items conditionally omitted - ansible.builtin.set_fact: - namestuff: ' {{ (namestuff | default([])) | union([item]) }}' - when: item != omit - loop: - - "{{ (fs_installed_smb_v1 | default(False)) | ternary(omit, 'FS-SMB1') }}" - - "foo" - - "bar" - - - -.. _combine_optional_values: - -Combine values from same list of dicts ---------------------------------------- -Combining positive and negative filters from examples above, you can get a 'value when it exists' and a 'fallback' when it doesn't. - -.. code-block:: YAML+Jinja - :caption: Use selectattr and rejectattr to get the ansible_host or inventory_hostname as needed - - - hosts: localhost - tasks: - - name: Check hosts in inventory that respond to ssh port - wait_for: - host: "{{ item }}" - port: 22 - loop: '{{ has_ah + no_ah }}' - vars: - has_ah: '{{ hostvars|dictsort|selectattr("1.ansible_host", "defined")|map(attribute="1.ansible_host")|list }}' - no_ah: '{{ hostvars|dictsort|rejectattr("1.ansible_host", "defined")|map(attribute="0")|list }}' - - -.. _custom_fileglob_variable: - -Custom Fileglob Based on a Variable ------------------------------------ - -This example uses `Python argument list unpacking `_ to create a custom list of fileglobs based on a variable. - -.. code-block:: YAML+Jinja - :caption: Using fileglob with a list based on a variable. - - - hosts: all - vars: - mygroups: - - prod - - web - tasks: - - name: Copy a glob of files based on a list of groups - copy: - src: "{{ item }}" - dest: "/tmp/{{ item }}" - loop: '{{ q("fileglob", *globlist) }}' - vars: - globlist: '{{ mygroups | map("regex_replace", "^(.*)$", "files/\1/*.conf") | list }}' - - -.. _complex_type_transformations: - -Complex Type transformations -============================= - -Jinja provides filters for simple data type transformations (``int``, ``bool``, and so on), but when you want to transform data structures things are not as easy. -You can use loops and list comprehensions as shown above to help, also other filters and lookups can be chained and used to achieve more complex transformations. - - -.. _create_dictionary_from_list: - -Create dictionary from list ---------------------------- - -In most languages it is easy to create a dictionary (a.k.a. map/associative array/hash and so on) from a list of pairs, in Ansible there are a couple of ways to do it and the best one for you might depend on the source of your data. - - -These example produces ``{"a": "b", "c": "d"}`` - -.. code-block:: YAML+Jinja - :caption: Simple list to dict by assuming the list is [key, value , key, value, ...] - - vars: - single_list: [ 'a', 'b', 'c', 'd' ] - mydict: "{{ dict(single_list | slice(2)) }}" - - -.. code-block:: YAML+Jinja - :caption: It is simpler when we have a list of pairs: - - vars: - list_of_pairs: [ ['a', 'b'], ['c', 'd'] ] - mydict: "{{ dict(list_of_pairs) }}" - -Both end up being the same thing, with ``slice(2)`` transforming ``single_list`` to a ``list_of_pairs`` generator. - - - -A bit more complex, using ``set_fact`` and a ``loop`` to create/update a dictionary with key value pairs from 2 lists: - -.. code-block:: YAML+Jinja - :caption: Using set_fact to create a dictionary from a set of lists - :emphasize-lines: 3, 4 - - - name: Uses 'combine' to update the dictionary and 'zip' to make pairs of both lists - ansible.builtin.set_fact: - mydict: "{{ mydict | default({}) | combine({item[0]: item[1]}) }}" - loop: "{{ (keys | zip(values)) | list }}" - vars: - keys: - - foo - - var - - bar - values: - - a - - b - - c - -This results in ``{"foo": "a", "var": "b", "bar": "c"}``. - - -You can even combine these simple examples with other filters and lookups to create a dictionary dynamically by matching patterns to variable names: - -.. code-block:: YAML+Jinja - :caption: Using 'vars' to define dictionary from a set of lists without needing a task - - vars: - xyz_stuff: 1234 - xyz_morestuff: 567 - myvarnames: "{{ q('varnames', '^xyz_') }}" - mydict: "{{ dict(myvarnames|map('regex_replace', '^xyz_', '')|list | zip(q('vars', *myvarnames))) }}" - -A quick explanation, since there is a lot to unpack from these two lines: - - - The ``varnames`` lookup returns a list of variables that match "begin with ``xyz_``". - - Then feeding the list from the previous step into the ``vars`` lookup to get the list of values. - The ``*`` is used to 'dereference the list' (a pythonism that works in Jinja), otherwise it would take the list as a single argument. - - Both lists get passed to the ``zip`` filter to pair them off into a unified list (key, value, key2, value2, ...). - - The dict function then takes this 'list of pairs' to create the dictionary. - - -An example on how to use facts to find a host's data that meets condition X: - - -.. code-block:: YAML+Jinja - - vars: - uptime_of_host_most_recently_rebooted: "{{ansible_play_hosts_all | map('extract', hostvars, 'ansible_uptime_seconds') | sort | first}}" - -An example to show a host uptime in days/hours/minutes/seconds (assumes facts where gathered). - -.. code-block:: YAML+Jinja - - - name: Show the uptime in days/hours/minutes/seconds - ansible.builtin.debug: - msg: Uptime {{ now().replace(microsecond=0) - now().fromtimestamp(now(fmt='%s') | int - ansible_uptime_seconds) }} - - -.. seealso:: - - :ref:`playbooks_filters` - Jinja2 filters included with Ansible - :ref:`playbooks_tests` - Jinja2 tests included with Ansible - `Jinja2 Docs `_ - Jinja2 documentation, includes lists for core filters and tests +This page has moved to :ref:`complex_data_manipulation`. \ No newline at end of file diff --git a/docs/docsite/rst/user_guide/index.rst b/docs/docsite/rst/user_guide/index.rst index a4cf9b2cd2..9330382f21 100644 --- a/docs/docsite/rst/user_guide/index.rst +++ b/docs/docsite/rst/user_guide/index.rst @@ -1,3 +1,5 @@ +:orphan: + .. _user_guide_index: ########## @@ -11,10 +13,14 @@ User Guide Red Hat is committed to replacing problematic language in our code, documentation, and web properties. We are beginning with these four terms: master, slave, blacklist, and whitelist. We ask that you open an issue or pull request if you come upon a term that we have missed. For more details, see `our CTO Chris Wright's message `_. Welcome to the Ansible User Guide! -Here is the complete list of resources in the Ansible User Guide: +This guide is now deprecated to improve navigation and organization. +You can find all the user guide content in the following sections: -.. toctree:: - :maxdepth: 2 - - complex_data_manipulation - sample_setup +* :ref:`inventory_guide_index` +* :ref:`command_guide_index` +* :ref:`playbook_guide_index` +* :ref:`vault_guide_index` +* :ref:`modules_plugins_index` +* :ref:`collections_index` +* :ref:`os_guide_index` +* :ref:`tips_tricks_index` diff --git a/docs/docsite/rst/user_guide/sample_setup.rst b/docs/docsite/rst/user_guide/sample_setup.rst index 1f267a72c7..40c3df7d37 100644 --- a/docs/docsite/rst/user_guide/sample_setup.rst +++ b/docs/docsite/rst/user_guide/sample_setup.rst @@ -1,297 +1,7 @@ -.. _sample_setup: +:orphan: ******************** Sample Ansible setup ******************** -You have learned about playbooks, inventory, roles, and variables. This section pulls all those elements together, outlining a sample setup for automating a web service. You can find more example playbooks illustrating these patterns in our `ansible-examples repository `_. (NOTE: These may not use all of the features in the latest release, but are still an excellent reference!). - -The sample setup organizes playbooks, roles, inventory, and variables files by function, with tags at the play and task level for greater granularity and control. This is a powerful and flexible approach, but there are other ways to organize Ansible content. Your usage of Ansible should fit your needs, not ours, so feel free to modify this approach and organize your content as you see fit. - -.. contents:: - :local: - -Sample directory layout ------------------------ - -This layout organizes most tasks in roles, with a single inventory file for each environment and a few playbooks in the top-level directory: - -.. code-block:: console - - production # inventory file for production servers - staging # inventory file for staging environment - - group_vars/ - group1.yml # here we assign variables to particular groups - group2.yml - host_vars/ - hostname1.yml # here we assign variables to particular systems - hostname2.yml - - library/ # if any custom modules, put them here (optional) - module_utils/ # if any custom module_utils to support modules, put them here (optional) - filter_plugins/ # if any custom filter plugins, put them here (optional) - - site.yml # main playbook - webservers.yml # playbook for webserver tier - dbservers.yml # playbook for dbserver tier - tasks/ # task files included from playbooks - webservers-extra.yml # <-- avoids confusing playbook with task files -.. include:: shared_snippets/role_directory.txt - -.. note:: By default, Ansible assumes your playbooks are stored in one directory with roles stored in a sub-directory called ``roles/``. As you use Ansible to automate more tasks, you may want to move your playbooks into a sub-directory called ``playbooks/``. If you do this, you must configure the path to your ``roles/`` directory using the ``roles_path`` setting in ansible.cfg. - -Alternative directory layout ----------------------------- - -Alternatively you can put each inventory file with its ``group_vars``/``host_vars`` in a separate directory. This is particularly useful if your ``group_vars``/``host_vars`` don't have that much in common in different environments. The layout could look something like this: - - .. code-block:: console - - inventories/ - production/ - hosts # inventory file for production servers - group_vars/ - group1.yml # here we assign variables to particular groups - group2.yml - host_vars/ - hostname1.yml # here we assign variables to particular systems - hostname2.yml - - staging/ - hosts # inventory file for staging environment - group_vars/ - group1.yml # here we assign variables to particular groups - group2.yml - host_vars/ - stagehost1.yml # here we assign variables to particular systems - stagehost2.yml - - library/ - module_utils/ - filter_plugins/ - - site.yml - webservers.yml - dbservers.yml - - roles/ - common/ - webtier/ - monitoring/ - fooapp/ - -This layout gives you more flexibility for larger environments, as well as a total separation of inventory variables between different environments. However, this approach is harder to maintain, because there are more files. For more information on organizing group and host variables, see :ref:`splitting_out_vars`. - -.. _groups_and_hosts: - -Sample group and host variables -------------------------------- - -These sample group and host variables files record the variable values that apply to each machine or group of machines. For instance, the data center in Atlanta has its own NTP servers, so when setting up ntp.conf, we should use them: - - .. code-block:: yaml - - --- - # file: group_vars/atlanta - ntp: ntp-atlanta.example.com - backup: backup-atlanta.example.com - -Similarly, the webservers have some configuration that does not apply to the database servers: - - .. code-block:: yaml - - --- - # file: group_vars/webservers - apacheMaxRequestsPerChild: 3000 - apacheMaxClients: 900 - -Default values, or values that are universally true, belong in a file called group_vars/all: - - .. code-block:: yaml - - --- - # file: group_vars/all - ntp: ntp-boston.example.com - backup: backup-boston.example.com - -If necessary, you can define specific hardware variance in systems in a host_vars file: - - .. code-block:: yaml - - --- - # file: host_vars/db-bos-1.example.com - foo_agent_port: 86 - bar_agent_port: 99 - -Again, if you are using :ref:`dynamic inventory `, Ansible creates many dynamic groups automatically. So a tag like "class:webserver" would load in variables from the file "group_vars/ec2_tag_class_webserver" automatically. - -.. _split_by_role: - -Sample playbooks organized by function --------------------------------------- - -With this setup, a single playbook can define all the infrastructure. The site.yml playbook imports two other playbooks, one for the webservers and one for the database servers: - - .. code-block:: yaml - - --- - # file: site.yml - - import_playbook: webservers.yml - - import_playbook: dbservers.yml - -The webservers.yml file, also at the top level, maps the configuration of the webservers group to the roles related to the webservers group: - - .. code-block:: yaml - - --- - # file: webservers.yml - - hosts: webservers - roles: - - common - - webtier - -With this setup, you can configure your whole infrastructure by "running" site.yml, or run a subset by running webservers.yml. This is analogous to the Ansible "--limit" parameter but a little more explicit: - - .. code-block:: shell - - ansible-playbook site.yml --limit webservers - ansible-playbook webservers.yml - -.. _role_organization: - -Sample task and handler files in a function-based role ------------------------------------------------------- - -Ansible loads any file called ``main.yml`` in a role sub-directory. This sample ``tasks/main.yml`` file is simple - it sets up NTP, but it could do more if we wanted: - - .. code-block:: yaml - - --- - # file: roles/common/tasks/main.yml - - - name: be sure ntp is installed - yum: - name: ntp - state: present - tags: ntp - - - name: be sure ntp is configured - template: - src: ntp.conf.j2 - dest: /etc/ntp.conf - notify: - - restart ntpd - tags: ntp - - - name: be sure ntpd is running and enabled - service: - name: ntpd - state: started - enabled: yes - tags: ntp - -Here is an example handlers file. As a review, handlers are only fired when certain tasks report changes, and are run at the end -of each play: - - .. code-block:: yaml - - --- - # file: roles/common/handlers/main.yml - - name: restart ntpd - service: - name: ntpd - state: restarted - -See :ref:`playbooks_reuse_roles` for more information. - - -.. _organization_examples: - -What the sample setup enables ------------------------------ - -The basic organizational structure described above enables a lot of different automation options. To reconfigure your entire infrastructure: - - .. code-block:: shell - - ansible-playbook -i production site.yml - -To reconfigure NTP on everything: - - .. code-block:: shell - - ansible-playbook -i production site.yml --tags ntp - -To reconfigure only the webservers: - - .. code-block:: shell - - ansible-playbook -i production webservers.yml - -To reconfigure only the webservers in Boston: - - .. code-block:: shell - - ansible-playbook -i production webservers.yml --limit boston - -To reconfigure only the first 10 webservers in Boston, and then the next 10: - - .. code-block:: shell - - ansible-playbook -i production webservers.yml --limit boston[0:9] - ansible-playbook -i production webservers.yml --limit boston[10:19] - -The sample setup also supports basic ad hoc commands: - - .. code-block:: shell - - ansible boston -i production -m ping - ansible boston -i production -m command -a '/sbin/reboot' - -To discover what tasks would run or what hostnames would be affected by a particular Ansible command: - - .. code-block:: shell - - # confirm what task names would be run if I ran this command and said "just ntp tasks" - ansible-playbook -i production webservers.yml --tags ntp --list-tasks - - # confirm what hostnames might be communicated with if I said "limit to boston" - ansible-playbook -i production webservers.yml --limit boston --list-hosts - -.. _dep_vs_config: - -Organizing for deployment or configuration ------------------------------------------- - -The sample setup models a typical configuration topology. When doing multi-tier deployments, there are going -to be some additional playbooks that hop between tiers to roll out an application. In this case, 'site.yml' -may be augmented by playbooks like 'deploy_exampledotcom.yml' but the general concepts still apply. Ansible allows you to deploy and configure using the same tool, so you would likely reuse groups and keep the OS configuration in separate playbooks or roles from the app deployment. - -Consider "playbooks" as a sports metaphor -- you can have one set of plays to use against all your infrastructure and situational plays that you use at different times and for different purposes. - -.. _ship_modules_with_playbooks: - -Using local Ansible modules ---------------------------- - -If a playbook has a :file:`./library` directory relative to its YAML file, this directory can be used to add Ansible modules that will -automatically be in the Ansible module path. This is a great way to keep modules that go with a playbook together. This is shown -in the directory structure example at the start of this section. - -.. seealso:: - - :ref:`yaml_syntax` - Learn about YAML syntax - :ref:`working_with_playbooks` - Review the basic playbook features - :ref:`list_of_collections` - Browse existing collections, modules, and plugins - :ref:`developing_modules` - Learn how to extend Ansible by writing your own modules - :ref:`intro_patterns` - Learn about how to select hosts - `GitHub examples directory `_ - Complete playbook files from the github project source - `Mailing List `_ - Questions? Help? Ideas? Stop by the list on Google Groups +This page has moved to :ref:`sample_setup`. \ No newline at end of file diff --git a/docs/docsite/rst/user_guide/shared_snippets/with2loop.txt b/docs/docsite/rst/user_guide/shared_snippets/with2loop.txt deleted file mode 100644 index 5217f9429e..0000000000 --- a/docs/docsite/rst/user_guide/shared_snippets/with2loop.txt +++ /dev/null @@ -1,205 +0,0 @@ -In most cases, loops work best with the ``loop`` keyword instead of ``with_X`` style loops. The ``loop`` syntax is usually best expressed using filters instead of more complex use of ``query`` or ``lookup``. - -These examples show how to convert many common ``with_`` style loops to ``loop`` and filters. - -with_list ---------- - -``with_list`` is directly replaced by ``loop``. - -.. code-block:: yaml+jinja - - - name: with_list - ansible.builtin.debug: - msg: "{{ item }}" - with_list: - - one - - two - - - name: with_list -> loop - ansible.builtin.debug: - msg: "{{ item }}" - loop: - - one - - two - -with_items ----------- - -``with_items`` is replaced by ``loop`` and the ``flatten`` filter. - -.. code-block:: yaml+jinja - - - name: with_items - ansible.builtin.debug: - msg: "{{ item }}" - with_items: "{{ items }}" - - - name: with_items -> loop - ansible.builtin.debug: - msg: "{{ item }}" - loop: "{{ items|flatten(levels=1) }}" - -with_indexed_items ------------------- - -``with_indexed_items`` is replaced by ``loop``, the ``flatten`` filter and ``loop_control.index_var``. - -.. code-block:: yaml+jinja - - - name: with_indexed_items - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - with_indexed_items: "{{ items }}" - - - name: with_indexed_items -> loop - ansible.builtin.debug: - msg: "{{ index }} - {{ item }}" - loop: "{{ items|flatten(levels=1) }}" - loop_control: - index_var: index - -with_flattened --------------- - -``with_flattened`` is replaced by ``loop`` and the ``flatten`` filter. - -.. code-block:: yaml+jinja - - - name: with_flattened - ansible.builtin.debug: - msg: "{{ item }}" - with_flattened: "{{ items }}" - - - name: with_flattened -> loop - ansible.builtin.debug: - msg: "{{ item }}" - loop: "{{ items|flatten }}" - -with_together -------------- - -``with_together`` is replaced by ``loop`` and the ``zip`` filter. - -.. code-block:: yaml+jinja - - - name: with_together - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - with_together: - - "{{ list_one }}" - - "{{ list_two }}" - - - name: with_together -> loop - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - loop: "{{ list_one|zip(list_two)|list }}" - -Another example with complex data - -.. code-block:: yaml+jinja - - - name: with_together -> loop - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }} - {{ item.2 }}" - loop: "{{ data[0]|zip(*data[1:])|list }}" - vars: - data: - - ['a', 'b', 'c'] - - ['d', 'e', 'f'] - - ['g', 'h', 'i'] - -with_dict ---------- - -``with_dict`` can be substituted by ``loop`` and either the ``dictsort`` or ``dict2items`` filters. - -.. code-block:: yaml+jinja - - - name: with_dict - ansible.builtin.debug: - msg: "{{ item.key }} - {{ item.value }}" - with_dict: "{{ dictionary }}" - - - name: with_dict -> loop (option 1) - ansible.builtin.debug: - msg: "{{ item.key }} - {{ item.value }}" - loop: "{{ dictionary|dict2items }}" - - - name: with_dict -> loop (option 2) - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - loop: "{{ dictionary|dictsort }}" - -with_sequence -------------- - -``with_sequence`` is replaced by ``loop`` and the ``range`` function, and potentially the ``format`` filter. - -.. code-block:: yaml+jinja - - - name: with_sequence - ansible.builtin.debug: - msg: "{{ item }}" - with_sequence: start=0 end=4 stride=2 format=testuser%02x - - - name: with_sequence -> loop - ansible.builtin.debug: - msg: "{{ 'testuser%02x' | format(item) }}" - # range is exclusive of the end point - loop: "{{ range(0, 4 + 1, 2)|list }}" - -with_subelements ----------------- - -``with_subelements`` is replaced by ``loop`` and the ``subelements`` filter. - -.. code-block:: yaml+jinja - - - name: with_subelements - ansible.builtin.debug: - msg: "{{ item.0.name }} - {{ item.1 }}" - with_subelements: - - "{{ users }}" - - mysql.hosts - - - name: with_subelements -> loop - ansible.builtin.debug: - msg: "{{ item.0.name }} - {{ item.1 }}" - loop: "{{ users|subelements('mysql.hosts') }}" - -with_nested/with_cartesian --------------------------- - -``with_nested`` and ``with_cartesian`` are replaced by loop and the ``product`` filter. - -.. code-block:: yaml+jinja - - - name: with_nested - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - with_nested: - - "{{ list_one }}" - - "{{ list_two }}" - - - name: with_nested -> loop - ansible.builtin.debug: - msg: "{{ item.0 }} - {{ item.1 }}" - loop: "{{ list_one|product(list_two)|list }}" - -with_random_choice ------------------- - -``with_random_choice`` is replaced by just use of the ``random`` filter, without need of ``loop``. - -.. code-block:: yaml+jinja - - - name: with_random_choice - ansible.builtin.debug: - msg: "{{ item }}" - with_random_choice: "{{ my_list }}" - - - name: with_random_choice -> loop (No loop is needed here) - ansible.builtin.debug: - msg: "{{ my_list|random }}" - tags: random