diff --git a/docs/docsite/rst/dev_guide/developing_modules.rst b/docs/docsite/rst/dev_guide/developing_modules.rst index b1f58986c19..435eda6bb33 100644 --- a/docs/docsite/rst/dev_guide/developing_modules.rst +++ b/docs/docsite/rst/dev_guide/developing_modules.rst @@ -37,11 +37,11 @@ There are a lot of existing modules available, and more that are in development. Action plugins get run on the master instead of on the target. For modules like file/copy/template, some of the work needs to be done on the master before the module executes on the target. Action plugins execute first on the master and can then execute the normal module on the target if necessary. -For more information about action plugins, go `here `_. +For more information about action plugins, `read the action plugins documentation here `_. 3. Should you use a role instead? -Check out the `roles documentation `_. +Check out the roles documentation `available here `_. .. _developing_modules_all: diff --git a/docs/docsite/rst/galaxy.rst b/docs/docsite/rst/galaxy.rst index 598aba62137..0e9ec912591 100644 --- a/docs/docsite/rst/galaxy.rst +++ b/docs/docsite/rst/galaxy.rst @@ -162,7 +162,7 @@ The complex form can also be used as follows: name: composer When dependencies are encountered by ``ansible-galaxy``, it will automatically install each dependency to the *roles_path*. To understand how dependencies -are handled during play execution, see :doc:`playbooks_roles`. +are handled during play execution, see :doc:`playbooks_reuse_roles`. .. note:: @@ -471,7 +471,7 @@ Provide the ID of the integration to be disabled. You can find the ID by using t .. seealso:: - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` All about ansible roles `Mailing List `_ Questions? Help? Ideas? Stop by the list on Google Groups diff --git a/docs/docsite/rst/guide_rolling_upgrade.rst b/docs/docsite/rst/guide_rolling_upgrade.rst index 07f574532a2..e23445f94ab 100644 --- a/docs/docsite/rst/guide_rolling_upgrade.rst +++ b/docs/docsite/rst/guide_rolling_upgrade.rst @@ -100,7 +100,7 @@ your roles is up to you and your application, but most sites will have one or mo all systems, and then a series of application-specific roles that install and configure particular parts of the site. Roles can have variables and dependencies, and you can pass in parameters to roles to modify their behavior. -You can read more about roles in the :doc:`playbooks_roles` section. +You can read more about roles in the :doc:`playbooks_reuse_roles` section. .. _lamp_group_variables: @@ -276,7 +276,7 @@ This should give you a good idea of how to structure a multi-tier application wi The lamp_haproxy example discussed here. :doc:`playbooks` An introduction to playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` An introduction to playbook roles :doc:`playbooks_variables` An introduction to Ansible variables diff --git a/docs/docsite/rst/playbooks.rst b/docs/docsite/rst/playbooks.rst index c6f6e5057e5..a98967eb6d5 100644 --- a/docs/docsite/rst/playbooks.rst +++ b/docs/docsite/rst/playbooks.rst @@ -19,7 +19,7 @@ It is recommended to look at `Example Playbooks `_ Have a question? Stop by the google group! diff --git a/docs/docsite/rst/playbooks_conditionals.rst b/docs/docsite/rst/playbooks_conditionals.rst index c5d7ac887ef..e4875d6cb93 100644 --- a/docs/docsite/rst/playbooks_conditionals.rst +++ b/docs/docsite/rst/playbooks_conditionals.rst @@ -310,7 +310,7 @@ You may check the registered variable's string contents for emptiness:: :doc:`playbooks` An introduction to playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_delegation.rst b/docs/docsite/rst/playbooks_delegation.rst index 50066ba5561..ebbac468588 100644 --- a/docs/docsite/rst/playbooks_delegation.rst +++ b/docs/docsite/rst/playbooks_delegation.rst @@ -13,7 +13,7 @@ This section covers all of these features. For examples of these items in use, You should also consult the :doc:`modules` section, various modules like 'ec2_elb', 'nagios', and 'bigip_pool', and 'netscaler' dovetail neatly with the concepts mentioned here. -You'll also want to read up on :doc:`playbooks_roles`, as the 'pre_task' and 'post_task' concepts are the places where you would typically call these modules. +You'll also want to read up on :doc:`playbooks_reuse_roles`, as the 'pre_task' and 'post_task' concepts are the places where you would typically call these modules. .. _rolling_update_batch_size: diff --git a/docs/docsite/rst/playbooks_filters.rst b/docs/docsite/rst/playbooks_filters.rst index 27e7d8bbf5f..143ec8d69ed 100644 --- a/docs/docsite/rst/playbooks_filters.rst +++ b/docs/docsite/rst/playbooks_filters.rst @@ -696,7 +696,7 @@ to be added to core so everyone can make use of them. All about variables :doc:`playbooks_loops` Looping in playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_filters_ipaddr.rst b/docs/docsite/rst/playbooks_filters_ipaddr.rst index 946dcb72b62..89c0de9b7ae 100644 --- a/docs/docsite/rst/playbooks_filters_ipaddr.rst +++ b/docs/docsite/rst/playbooks_filters_ipaddr.rst @@ -496,7 +496,7 @@ convert it between various formats. Examples:: All about variables :doc:`playbooks_loops` Looping in playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_keywords.rst b/docs/docsite/rst/playbooks_keywords.rst new file mode 100644 index 00000000000..dbd92a829c3 --- /dev/null +++ b/docs/docsite/rst/playbooks_keywords.rst @@ -0,0 +1,143 @@ +Directives Glossary +=================== + +This section lists the common playbook objects and their directives. +Note that not all directives affect the object itself and might just be there to be inherited by other contained objects. +Aliases for the directives are not reflected here, nor are mutable ones; for example, `action` in task can be substituted by the name of any module plugin. + +Be aware that this reflects the 'current development branch' and that the keywords do not have 'version_added' information. + +.. contents:: + :local: + :depth: 1 + + +Play +---- + - **accelerate:** DEPRECATED. Set to True to use the accelerate connection plugin. + - **accelerate_ipv6:** DEPRECATED. Set to True to force the accelerate plugin to use ipv6 for it's connection. + - **accelerate_port:** DEPRECATED. Set to override default port use for accelerate connection. + - **always_run:** DEPRECATED. Forces a task to run even in check mode. Use check_mode directive instead. + - **any_errors_fatal:** Force any un-handled task errors on any host to propagate to all hosts and end the play. + - **become:** Boolean that controls if privilege escalation is used or not on Task execution. + - **become_flags:** A string of flag(s) to pass to the privilege escalation program when ``become`` is True. + - **become_method:** Which method of privilege escalation to use; for example sudo/su/etc. + - **become_user:** User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user. + - **check_mode:** A boolean that controls if a task is executed in 'check' mode. + - **connection:** Allows you to change the connection plugin used for tasks to execute on the target. + - **environment:** A dictionary that gets converted into environment vars to be provided for the task upon execution. + - **fact_path:** Set the fact path option for the fact gathering plugin controlled by ``gather_facts``. + - **force_handlers:** Force notified handler execution for hosts even if they failed during the play; it will not trigger if the play itself fails. + - **gather_facts:** A boolean that controls if the play will automatically run the 'setup' task to gather facts for the hosts. + - **gather_subset:** Allows you to pass subset options to the fact gathering plugin controlled by ``gather_facts``. + - **gather_timeout:** Allows you to set the timeout for the fact gathering plugin controlled by ``gather_facts``. + - **handlers:** A section with tasks that are treated as handlers, these won't get executed normally, only when notified. After each section of tasks is complete. + - **hosts:** A list of groups, hosts or host pattern that translates into a list of hosts that are the play's target. + - **ignore_errors:** Boolean that allows you to ignore task failures and continue with the play. It does not affect connection errors. + - **max_fail_percentage:** Used to abort the run after a given percentage of hosts in the current batch has failed. + - **name:** Identifier. Used mostly for documentation. In the case of tasks/handlers it can be an identifier. + - **no_log:** Boolean that controls information disclosure. + - **order:** UNDOCUMENTED!! + - **port:** Used to override the default port used in a connection. + - **post_tasks:** A list of tasks to execute after the ``tasks`` section. + - **pre_tasks:** A list of tasks to execute before ``roles``. + - **remote_user:** User used to log into the target via the connection plugin. AKA login user. + - **roles:** UNDOCUMENTED!! + - **run_once:** Boolean that will bypass the host loop, forcing the task to execute on the first host available and will also apply any facts to all active hosts. + - **serial:** Defines the 'batch' of hosts to execute the current play until the end. + - **strategy:** Allows you to choose the connection plugin to use for the play. + - **tags:** Tags applied to the task or included tasks, this allows selecting subsets of tasks from the command line. + - **tasks:** Main list of tasks to execute in the play, they run after ``roles`` and before ``post_tasks``. + - **vars:** Dictionary/map of variables + - **vars_files:** List of files that contain vars to include in the play. + - **vars_prompt:** List of variables to prompt for. + - **vault_password:** Secret used to decrypt vaulted files or variables. + + +Role +---- + - **always_run:** DEPRECATED. Forces a task to run even in check mode. Use check_mode directive instead. + - **any_errors_fatal:** Force any un-handled task errors on any host to propagate to all hosts and end the play. + - **become:** Boolean that controls if privilege escalation is used or not on Task execution. + - **become_flags:** A string of flag(s) to pass to the privilege escalation program when ``become`` is True. + - **become_method:** Which method of privilege escalation to use; for example sudo/su/etc. + - **become_user:** User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user. + - **check_mode:** A boolean that controls if a task is executed in 'check' mode + - **connection:** Allows you to change the connection plugin used for tasks to execute on the target. + - **delegate_facts:** Boolean that allows you to apply facts to delegated host instead of inventory_hostname. + - **delegate_to:** Host to execute task instead of the target (inventory_hostname). Connection vars from the delegated host will also be used for the task. + - **environment:** A dictionary that gets converted into environment vars to be provided for the task upon execution. + - **ignore_errors:** Boolean that allows you to ignore task failures and continue with play. It does not affect connection errors. + - **no_log:** Boolean that controls information disclosure. + - **port:** Used to override the default port used in a connection. + - **remote_user:** User used to log into the target via the connection plugin. Also known as login user. + - **run_once:** Boolean that will bypass the host loop, forcing the task to execute on the first host available and will also apply any facts to all active hosts. + - **tags:** Tags applied to the task or included tasks, this allows selecting subsets of tasks from the command line. + - **vars:** Dictionary/map of variables + - **when:** Conditional expression; determines if an iteration of a task is run or not. + + +Block +----- + - **always:** List of tasks, in a block, that execute no matter if there is an error in the block or not. + - **always_run:** DEPRECATED, forces a task to run even in check mode, use check_mode directive instead. + - **any_errors_fatal:** Force any un-handled task errors on any host to propagate to all hosts and end the play. + - **become:** Boolean that controls if privilege escalation is used or not on Task execution. + - **become_flags:** A string of flag(s) to pass to the privilege escalation program when ``become`` is True. + - **become_method:** Which method of privilege escalation to use. i.e. sudo/su/etc. + - **become_user:** User that you 'become' after using privilege escalation, the remote/login user must have permissions to become this user. + - **block:** List of tasks in a block. + - **check_mode:** A boolean that controls if a task is executed in 'check' mode + - **connection:** Allows you to change the connection plugin used for tasks to execute on the target. + - **delegate_facts:** Boolean that allows you to apply facts to delegated host instead of inventory_hostname. + - **delegate_to:** Host to execute task instead of the target (inventory_hostname), connection vars from the delegated host will also be used for the task. + - **environment:** A dictionary that gets converted into environment vars to be provided for the task upon execution. + - **ignore_errors:** Boolean that allows you to ignore task failures and continue with play. It does not affect connection errors. + - **name:** It's a name, works mostly for documentation, in the case of tasks/handlers it can be an identifier. + - **no_log:** Boolean that controls information disclosure. + - **port:** Used to override the default port used in a connection. + - **remote_user:** User used to log into the target via the connection plugin. AKA login user. + - **rescue:** List of tasks in a block that run if there is a task error in the main ``block`` list. + - **run_once:** Boolean that will bypass the host loop, forcing the task to execute on the first host available and will also apply any facts to all active hosts. + - **tags:** Tags applied to the task or included tasks, this allows selecting subsets of tasks from the command line. + - **vars:** Dictionary/map of variables + - **when:** Conditional expression; determines if an iteration of a task is run or not. + + +Task +---- + - **action:** The 'action' to execute for a task. This normally translates into a C(module) or action plugin. + - **always_run:** DEPRECATED. Forces a task to run even in check mode. Use check_mode directive instead. + - **any_errors_fatal:** Force any un-handled task errors on any host to propagate to all hosts and end the play. + - **args:** UNDOCUMENTED!! + - **async:** Run a task asyncronouslly if the C(action) supports this. + - **become:** Boolean that controls if privilege escalation is used or not on Task execution. + - **become_flags:** A string of flag(s) to pass to the privilege escalation program when ``become`` is True. + - **become_method:** Which method of privilege escalation to use (for example sudo/su/etc). + - **become_user:** User that you 'become' after using privilege escalation. The remote/login user must have permissions to become this user. + - **changed_when:** Conditional expression that overrides the task's normal 'changed' status. + - **check_mode:** A boolean that controls if a task is executed in 'check' mode + - **connection:** Allows you to change the connection plugin used for tasks to execute on the target. + - **delay:** UNDOCUMENTED!! + - **delegate_facts:** Boolean that allows you to apply facts to delegated host instead of inventory_hostname. + - **delegate_to:** Host to execute task instead of the target (inventory_hostname). Connection vars from the delegated host will also be used for the task. + - **environment:** A dictionary that gets converted into environment vars to be provided for the task upon execution. + - **failed_when:** Conditional expression that overrides the task's normal 'failed' status. + - **ignore_errors:** Boolean that allows you to ignore task failures and continue with play. It does not affect connection errors. + - **local_action:** Same as action but also implies ``delegate_to: localhost`` + - **loop_control:** UNDOCUMENTED + - **name:** It's a name, works mostly for documentation, in the case of tasks/handlers it can be an identifier. + - **no_log:** Boolean that controls information disclosure. + - **notify:** UNDOCUMENTED + - **poll:** UNDOCUMENTED + - **port:** Used to override the default port used in a connection. + - **register:** UNDOCUMENTED!! + - **remote_user:** User used to log into the target via the connection plugin. AKA login user. + - **retries:** UNDOCUMENTED + - **run_once:** Boolean that will bypass the host loop, forcing the task to execute on the first host available. This will also apply any facts to all active hosts. + - **tags:** Tags applied to the task or included tasks. This allows selecting subsets of tasks from the command line. + - **until:** UNDOCUMENTED + - **vars:** Dictionary/map of variables. + - **when:** Conditional expression; determines if an iteration of a task is run or not. + - **with_:** ``with_`` is how loops are defined. It can use any available lookup plugin to generate the item list. + diff --git a/docs/docsite/rst/playbooks_loops.rst b/docs/docsite/rst/playbooks_loops.rst index cfba1a506b3..8c09104923f 100644 --- a/docs/docsite/rst/playbooks_loops.rst +++ b/docs/docsite/rst/playbooks_loops.rst @@ -747,7 +747,7 @@ information. Each of the above features are implemented as plugins in ansible, :doc:`playbooks` An introduction to playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_reuse.rst b/docs/docsite/rst/playbooks_reuse.rst new file mode 100644 index 00000000000..e991b343f72 --- /dev/null +++ b/docs/docsite/rst/playbooks_reuse.rst @@ -0,0 +1,84 @@ +Creating Reusable Playbooks +=========================== + +.. toctree:: + :maxdepth: 1 + + playbooks_reuse_includes + playbooks_reuse_roles + +While it is possible to write a playbook in one very large file (and you might start out learning playbooks this way), eventually you'll want to reuse files and start to organize things. In Ansible, there are three ways to do this: includes, imports, and roles. + +Includes and imports allow users to break up large playbooks into into smaller files, which can be used across multiple parent Playbooks or even multiple times within the same Playbook. + +Roles allow more than just tasks to be packaged together, and can include variables, handlers, or even modules and other plugins. Roles can also be uploaded and shared via Ansible Galaxy, unlike includes and imports. + +Dynamic vs. Static +`````````````````` + +Ansible has two modes of operation for reusable content: dynamic and static. + +In Ansible 2.0, the concept of *dynamic* includes was introduced. Due to some limitations with making all includes dynamic in this way, the ability to force includes to be *static* was intrdouced in Ansible 2.1. Because the *include* task became overloaded to encompass both static and dynamic syntaxes, and because the default behavior of an include could change based on other options set on the Task, Ansible 2.4 introduces the concept of ``include`` vs. ``import``. + +If you use any ``import*`` Task (``import_playbook``, ``import_tasks``, etc.), it will be *static*. +If you use any ``include*`` Task (``include_tasks``, ``include_role``, etc.), it will be *dynamic*. + +The bare ``include`` task (which was used for both Task files and Playbook-level includes) is still available, however it is now considered *deprecated*. + +Differences Between Static and Dynamic +`````````````````````````````````````` + +The two modes of operation are pretty simple: + +* Ansible pre-processes all static imports during Playbook parsing time. +* Dynamic includes are processed during runtime at the point in which that task is encountered. + +When it comes to Ansible task options like ``tags`` and conditonal statements (``when:``): + +* For static imports, the parent task options will be copied to all child tasks contained within the import. +* For dynamic includes, the task options will *only* apply to the dynamic task as it is evaluated, and will not be copied to child tasks. + +.. note:: + Roles are a somewhat special case. Prior to Ansible 2.3, Roles were always statically included via the special ``roles:`` option for a given Play, and were always executed first before any other Play tasks (unless ``pre_tasks`` were used). Roles can still be used this way, however Ansible 2.3 introduced the ``include_role`` option to allow Roles to be executed in-line with other tasks. + +Tradeoffs and Pitfalls Between Includes and Imports +``````````````````````````````````````````````````` + +Using ``include*`` vs. ``import*`` has some advantages, as well as some tradeoffs, which users should consider when choosing to use each: + +The primary advantage of using ``include*`` statements is looping. When a loop is used with an include, the included tasks or role will be executed once for each item in the loop. + +Using ``include*`` does have some limitations when compared to ``import*`` statements: + +* Tags which only exist inside a dynamic include will not show up in --list-tags output. +* Tasks which only exist inside a dynamic include will not show up in --list-tasks output. +* You cannot use ``notify`` to trigger a handler name which comes from inside a dynamic include (see note below). +* You cannot use ``--start-at-task`` to begin execution at a task inside a dynamic include. + +Using ``import*`` can also have some limitations when compared to dynamic includes: + +* As noted above, loops cannot be used with imports at all. +* When using variables for the target file or role name, variables from inventory sources (host/group vars, etc.) cannot be used. + +.. note:: + Regarding the use of ``notify`` for dynamic tasks: it is still possible to trigger the dynamic include itself, which would result in all tasks within the include being run. + +.. seealso:: + + :doc:`playbooks` + Review the basic Playbook language features + :doc:`playbooks_variables` + All about variables in playbooks + :doc:`playbooks_conditionals` + Conditionals in playbooks + :doc:`playbooks_loops` + Loops in playbooks + :doc:`playbooks_best_practices` + Various tips about managing playbooks in the real world + :doc:`galaxy` + How to share roles on galaxy, role management + `GitHub Ansible examples `_ + 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/playbooks_reuse_includes.rst b/docs/docsite/rst/playbooks_reuse_includes.rst new file mode 100644 index 00000000000..98e934ba9da --- /dev/null +++ b/docs/docsite/rst/playbooks_reuse_includes.rst @@ -0,0 +1,117 @@ +Including and Importing +======================= + +.. contents:: Topics + +Includes vs. Imports +```````````````````` + +As noted in :doc:`playbooks_reuse`, include and import statements are very similar, however the Ansible executor engine treats them very differently. + +- All ``import*`` statements are pre-processed at the time playbooks are parsed. +- All ``include*`` statements are processed as they encountered during the execution of the playbook. + +Please refer to :doc:`playbooks_reuse` for documentation concerning the trade-offs one may encounter when using each type. + +Importing Playbooks +``````````````````` + +It is possible to include playbooks inside a master playbook. For example:: + + --- + import_playbook: webservers.yml + import_playbook: databases.yml + +Each playbook listed will be run in the order they are listed. + + +Including and Importing Task Files +`````````````````````````````````` + +Use of included task lists is a great way to define a role that system is going to fulfill. A task include file simply contains a flat list of tasks:: + + # common_tasks.yml + --- + - name: placeholder foo + command: /bin/foo + - name: placeholder bar + command: /bin/bar + +You can then use ``import_tasks`` or ``include_tasks`` to include this file in your main task list:: + + tasks: + - import_tasks: common_tasks.yml + # or + - include_tasks: common_tasks.yml + +You can also pass variables into imports and includes:: + + tasks: + - import_tasks: wordpress.yml wp_user=timmy + - import_tasks: wordpress.yml wp_user=alice + - import_tasks: wordpress.yml wp_user=bob + +Variables can also be passed to include files using an alternative syntax, which also supports structured variables like dictionaries and lists:: + + tasks: + - include_tasks: wordpress.yml + vars: + wp_user: timmy + ssh_keys: + - "{{ lookup('file', 'keys/one.pub') }}" + - "{{ lookup('file', 'keys/two.pub') }}" + +Using either syntax, variables passed in can then be used in the included files. These variables will only be available to tasks within the included file. See :doc:`variable_precedence` for more details on variable inheritance and precedence. + +Task include statements can be used at arbitrary depth. + +.. note:: + Static and dynamic can be mixed, however this is not recommended as it may lead to difficult-to-diagnose bugs in your playbooks. + +Includes and imports can also be used in the ``handlers:`` section; for instance, if you want to define how to restart apache, you only have to do that once for all of your playbooks. You might make a handlers.yml that looks like:: + + # more_handlers.yml + --- + - name: restart apache + service: name=apache state=restarted + +And in your main playbook file:: + + handlers: + - include_tasks: more_handlers.yml + # or + - import_tasks: more_handlers.yml + +.. note:: + Be sure to refer to the limitations/trade-offs for handlers noted in :doc:`playbooks_reuse`. + +You can mix in includes along with your regular non-included tasks and handlers. + +Including and Importing Roles +````````````````````````````` + +Please refer to :doc:`playbooks_reuse_roles` for details on including and importing roles. + +.. seealso:: + + :doc:`YAMLSyntax` + Learn about YAML syntax + :doc:`playbooks` + Review the basic Playbook language features + :doc:`playbooks_best_practices` + Various tips about managing playbooks in the real world + :doc:`playbooks_variables` + All about variables in playbooks + :doc:`playbooks_conditionals` + Conditionals in playbooks + :doc:`playbooks_loops` + Loops in playbooks + :doc:`modules` + Learn about available modules + :doc:`dev_guide/developing_modules` + Learn how to extend Ansible by writing your own modules + `GitHub Ansible examples `_ + 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/playbooks_reuse_roles.rst b/docs/docsite/rst/playbooks_reuse_roles.rst new file mode 100644 index 00000000000..535b5c1c88f --- /dev/null +++ b/docs/docsite/rst/playbooks_reuse_roles.rst @@ -0,0 +1,378 @@ +Roles +===== + +.. contents:: Topics + +.. versionadded:: 1.2 + +Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users. + +Role Directory Structure +```````````````````````` + +Example project structure:: + + site.yml + webservers.yml + fooservers.yml + roles/ + common/ + tasks/ + handlers/ + files/ + templates/ + vars/ + defaults/ + meta/ + webservers/ + tasks/ + defaults/ + meta/ + +Roles expect files to be in certain directory names. Roles must include at least one of these directories, however it is perfectly fine to exclude any which are not being used. When in use, each directory must contain a ``main.yml`` file, which contains the relevant content: + +- ``tasks`` - contains the main list of tasks to be executed by the role. +- ``handlers`` - contains handlers, which may be used by this role or even anywhere outside this role. +- ``defaults`` - default variables for the role (see :doc:`Variables` for more information). +- ``vars`` - other variables for the role (see :doc:`Variables` for more information). +- ``files`` - contains files which can be deployed via this role. +- ``templates`` - contains templates which can be deployed via this role. +- ``meta`` - defines some meta data for this role. See below for more details. + +Other YAML files may be included in certain directories. For example, it is common practice to have platform-specific tasks included from the ``tasks/main.yml`` file:: + + # roles/example/tasks/main.yml + - import_tasks: redhat.yml + when: ansible_os_platform|lower == 'redhat' + - import_tasks: debian.yml + when: ansible_os_platform|lower == 'debian' + + # roles/example/tasks/redhat.yml + - yum: + name: "httpd" + state: present + + # roles/example/tasks/debian.yml + - apt: + name: "apache2" + state: present + +Roles may also include modules and other plugin types. For more information, please refer to the :doc:`Embedding Modules and Plugins In Roles` section below. + +Using Roles +``````````` + +The classic (original) way to use roles is via the ``roles:`` option for a given play:: + + --- + - hosts: webservers + roles: + - common + - webservers + +This designates the following behaviors, for each role 'x': + +- If roles/x/tasks/main.yml exists, tasks listed therein will be added to the play. +- If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play. +- If roles/x/vars/main.yml exists, variables listed therein will be added to the play. +- If roles/x/defaults/main.yml exists, variables listed therein will be added to the play. +- If roles/x/meta/main.yml exists, any role dependencies listed therein will be added to the list of roles (1.3 and later). +- Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely. + +When used in this manner, the order of execution for your playbook is as follows: + +- Any ``pre_tasks`` defined in the play. +- Any handlers triggered so far will be run. +- Each role listed in ``roles`` will execute in turn. Any role dependencies defined in the roles ``meta/main.yml`` will be run first. +- Any ``tasks`` defined in the play. +- Any handlers triggered so far will be run. +- Any ``post_tasks`` defined in the play. +- Any handlers triggered so far will be run. + +.. note:: + See below for more information regarding role dependencies. + +.. note:: + If using tags with tasks (described later as a means of only running part of a playbook), be sure to also tag your pre_tasks and post_tasks and pass those along as well, especially if the pre and post tasks are used for monitoring outage window control or load balancing. + +As of Ansible 2.4, you can now use roles inline with any other tasks using ``import_role`` or ``include_role``:: + + --- + + - hosts: webservers + tasks: + - debug: + msg: "before we run our role" + - import_role: + name: example + - include_role: + name: example + - debug: + msg: "after we ran our role" + +When roles are defined in the classic manner, they are treated as static imports and processed during playbook parsing. + +.. note:: + The ``include_role`` option was introduced in Ansible 2.3. The usage has changed slightly as of Ansible 2.4 to match the include (dynamic) vs. import (static) usage. See :doc:`Dynamic vs. Static` for more details. + +The name used for the role can be a simple name (see :doc:`Role Search Path` below), or it can be a fully qualified path:: + + --- + + - hosts: webservers + roles: + - { role: '/path/to/my/roles/common' } + +Roles can accept parameters:: + + --- + + - hosts: webservers + roles: + - common + - { role: foo_app_instance, dir: '/opt/a', app_port: 5000 } + - { role: foo_app_instance, dir: '/opt/b', app_port: 5001 } + +Or, using the newer syntax:: + + --- + + - hosts: webservers + tasks: + - include_role: + name: foo_app_instance + args: + dir: '/opt/a' + app_port: 5000 + ... + +You can conditionally execute a role. This is not generally recommended with the classic syntax, but is common when using ``import_role`` or ``include_role``:: + + --- + + - hosts: webservers + tasks: + - include_role: + name: some_role + when: "ansible_os_family == 'RedHat'" + +Finally, you may wish to assign tags to the roles you specify. You can do so inline:: + + --- + + - hosts: webservers + roles: + - { role: foo, tags: ["bar", "baz"] } + +Or, again, using the newer syntax:: + + --- + + - hosts: webservers + tasks: + - import_role: + name: foo + tags: + - bar + - baz + +.. note:: + This *tags all of the tasks in that role with the tags specified*, overriding any tags that are specified inside the role. If you find yourself building a role with lots of tags and you want to call subsets of the role at different times, you should consider just splitting that role into multiple roles. + +Role Duplication and Execution +`````````````````````````````` + +Ansible will only allow a role to execute once, even if defined multiple times, if the parameters defined on the role are not different for each definition. For example:: + + --- + - hosts: webservers + roles: + - foo + - foo + +Given the above, the role ``foo`` will only be run once. + +To make roles run more than once, there are two options: + +1. Pass different parameters in each role definition. +2. Add ``allow_duplicates: true`` to the ``meta/main.yml`` file for the role. + +Example 1 - passing different paramters:: + + --- + - hosts: webservers + roles: + - { role: foo, message: "first" } + - { role: foo, message: "second" } + +In this example, because each role definition has different parameters, ``foo`` will run twice. + +Example 2 - using ``allow_duplicates: true``:: + + # playbook.yml + --- + - hosts: webservers + roles: + - foo + - foo + + # roles/foo/meta/main.yml + --- + allow_duplicates: true + +In this example, ``foo`` will run twice because we have explicitly enabled it to do so. + +Role Default Variables +`````````````````````` + +.. versionadded:: 1.3 + +Role default variables allow you to set default variables for included or dependent roles (see below). To create +defaults, simply add a ``defaults/main.yml`` file in your role directory. These variables will have the lowest priority +of any variables available, and can be easily overridden by any other variable, including inventory variables. + +Role Dependencies +````````````````` + +.. versionadded:: 1.3 + +Role dependencies allow you to automatically pull in other roles when using a role. Role dependencies are stored in the ``meta/main.yml`` file contained within the role directory, as noted above. This file should contain a list of roles and parameters to insert before the specified role, such as the following in an example ``roles/myapp/meta/main.yml``:: + + --- + dependencies: + - { role: common, some_parameter: 3 } + - { role: apache, apache_port: 80 } + - { role: postgres, dbname: blarg, other_parameter: 12 } + +.. note:: + Role dependencies must use the classic role definition style. + +Role dependencies are always executed before the role that includes them, and may be recursive. Dependencies also follow the duplication rules specified above. If another role also lists it as a dependency, it will not be run again based on the same rules given above. + +.. note:: + Always remember that when using ``allow_duplicates: true``, it needs to be in the dependent role's ``meta/main.yml``, not the parent. + +For example, a role named ``car`` depends on a role named ``wheel`` as follows:: + + --- + dependencies: + - { role: wheel, n: 1 } + - { role: wheel, n: 2 } + - { role: wheel, n: 3 } + - { role: wheel, n: 4 } + +And the ``wheel`` role depends on two roles: ``tire`` and ``brake``. The ``meta/main.yml`` for wheel would then contain the following:: + + --- + dependencies: + - { role: tire } + - { role: brake } + +And the ``meta/main.yml`` for ``tire`` and ``brake`` would contain the following:: + + --- + allow_duplicates: true + + +The resulting order of execution would be as follows:: + + tire(n=1) + brake(n=1) + wheel(n=1) + tire(n=2) + brake(n=2) + wheel(n=2) + ... + car + +Note that we did not have to use ``allow_duplicates: true`` for ``wheel``, because each instance defined by ``car`` uses different parameter values. + +.. note:: + Variable inheritance and scope are detailed in the :doc:`playbooks_variables`. + +Embedding Modules and Plugins In Roles +`````````````````````````````````````` + +This is an advanced topic that should not be relevant for most users. + +If you write a custom module (see :doc:`dev_guide/developing_modules`) or a plugin (see :doc:`dev_guide/developing_plugins`), you may wish to distribute it as part of a role. +Generally speaking, Ansible as a project is very interested in taking high-quality modules into ansible core for inclusion, so this shouldn't be the norm, but it's quite easy to do. + +A good example for this is if you worked at a company called AcmeWidgets, and wrote an internal module that helped configure your internal software, and you wanted other +people in your organization to easily use this module -- but you didn't want to tell everyone how to configure their Ansible library path. + +Alongside the 'tasks' and 'handlers' structure of a role, add a directory named 'library'. In this 'library' directory, then include the module directly inside of it. + +Assuming you had this:: + + roles/ + my_custom_modules/ + library/ + module1 + module2 + +The module will be usable in the role itself, as well as any roles that are called *after* this role, as follows:: + + + - hosts: webservers + roles: + - my_custom_modules + - some_other_role_using_my_custom_modules + - yet_another_role_using_my_custom_modules + +This can also be used, with some limitations, to modify modules in Ansible's core distribution, such as to use development versions of modules before they are released in production releases. This is not always advisable as API signatures may change in core components, however, and is not always guaranteed to work. It can be a handy way of carrying a patch against a core module, however, should you have good reason for this. Naturally the project prefers that contributions be directed back to github whenever possible via a pull request. + +The same mechanism can be used to embed and distribute plugins in a role, using the same schema. For example, for a filter plugin:: + + roles/ + my_custom_filter/ + filter_plugins + filter1 + filter2 + +They can then be used in a template or a jinja template in any role called after 'my_custom_filter' + +Role Search Path +```````````````` + +Ansible will search for roles in the following way: + +- A ``roles/`` directory, relative to the playbook file. +- By default, in ``/etc/ansible/roles`` + +In Ansible 1.4 and later you can configure an additional roles_path to search for roles. Use this to check all of your common roles out to one location, and share them easily between multiple playbook projects. See :doc:`intro_configuration` for details about how to set this up in ansible.cfg. + +Ansible Galaxy +`````````````` + +`Ansible Galaxy `_ is a free site for finding, downloading, rating, and reviewing all kinds of community developed Ansible roles and can be a great way to get a jumpstart on your automation projects. + +You can sign up with social auth, and the download client 'ansible-galaxy' is included in Ansible 1.4.2 and later. + +Read the "About" page on the Galaxy site for more information. + +.. seealso:: + + :doc:`galaxy` + How to share roles on galaxy, role management + :doc:`YAMLSyntax` + Learn about YAML syntax + :doc:`playbooks` + Review the basic Playbook language features + :doc:`playbooks_best_practices` + Various tips about managing playbooks in the real world + :doc:`playbooks_variables` + All about variables in playbooks + :doc:`playbooks_conditionals` + Conditionals in playbooks + :doc:`playbooks_loops` + Loops in playbooks + :doc:`modules` + Learn about available modules + :doc:`dev_guide/developing_modules` + Learn how to extend Ansible by writing your own modules + `GitHub Ansible examples `_ + 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/playbooks_roles.rst b/docs/docsite/rst/playbooks_roles.rst index d0c4c26285d..1df49d7688d 100644 --- a/docs/docsite/rst/playbooks_roles.rst +++ b/docs/docsite/rst/playbooks_roles.rst @@ -3,515 +3,15 @@ Playbook Roles and Include Statements .. contents:: Topics -Introduction -```````````` -While it is possible to write a playbook in one very large file (and you might start out learning playbooks this way), -eventually you'll want to reuse files and start to organize things. - -At a basic level, including task files allows you to break up bits of -configuration policy into smaller files. Task includes pull in tasks from other -files. Since handlers are tasks too, you can also include handler files from -the 'handler' section. - -See :doc:`playbooks` if you need a review of these concepts. - -Playbooks can also include plays from other playbook files. When that is done, the plays will be inserted into the playbook to form -a longer list of plays. - -When you start to think about it -- tasks, handlers, variables, and so on -- begin to form larger concepts. You start to think about modeling -what something is, rather than how to make something look like something. It's no longer "apply this handful of THINGS to these hosts", you say "these hosts are dbservers" or "these hosts are webservers". In programming, we might call that "encapsulating" how things work. For instance, -you can drive a car without knowing how the engine works. - -Roles in Ansible build on the idea of include files and combine them to form clean, reusable abstractions -- they allow you to focus -more on the big picture and only dive down into the details when needed. - -We'll start with understanding includes so roles make more sense, but our ultimate goal should be understanding roles -- roles -are great and you should use them every time you write playbooks. - -See the `ansible-examples `_ repository on GitHub for lots of examples of all of this -put together. You may wish to have this open in a separate tab as you dive in. - -Task versus Play includes -````````````````````````` -Tasks and plays both use the `include` keyword, but implement the keyword differently. The difference between them is determined by their positioning and content. If the include is inside a play it can only be a 'task' include and include a list of tasks; if it is at the top level, it can only include plays. For example:: - - # this is a 'play' include - - include: intro_example.yml - - - name: another play - hosts: all - tasks: - - debug: msg=hello - - # this is a 'task' include - - include: stuff.yml - -A 'task' include can appear anywhere a task can, but a 'play' include cannot be inside other plays only alongside them at the same level. -While 'task' includes can take other parameters and have the included tasks inherit them, 'play' includes are very limited and most directives do not work. - - -Task Include Files And Encouraging Reuse -```````````````````````````````````````` - -Suppose you want to reuse lists of tasks between plays or playbooks. You can use -include files to do this. Use of included task lists is a great way to define a role -that system is going to fulfill. Remember, the goal of a play in a playbook is to map -a group of systems into multiple roles. Let's see what this looks like... - -A task include file simply contains a flat list of tasks, like so:: - - --- - # possibly saved as tasks/foo.yml - - - name: placeholder foo - command: /bin/foo - - - name: placeholder bar - command: /bin/bar - -Include directives look like this, and can be mixed in with regular tasks in a playbook:: - - tasks: - - - include: tasks/foo.yml - -You can also pass variables into includes. We call this a 'parameterized include'. - -For instance, to deploy to multiple wordpress instances, I could -encapsulate all of my wordpress tasks in a single wordpress.yml file, and use -it like so:: - - tasks: - - include: wordpress.yml wp_user=timmy - - include: wordpress.yml wp_user=alice - - include: wordpress.yml wp_user=bob - -Starting in 1.0, variables can also be passed to include files using an alternative syntax, -which also supports structured variables:: - - tasks: - - - include: wordpress.yml - vars: - wp_user: timmy - ssh_keys: - - keys/one.txt - - keys/two.txt - -Using either syntax, variables passed in can then be used in the included files. We'll cover them in :doc:`playbooks_variables`. -You can reference them like this:: - - {{ wp_user }} - -(In addition to the explicitly passed-in parameters, all variables from -the vars section are also available for use here as well.) - - -.. note:: - As of 1.0, task include statements can be used at arbitrary depth. - They were previously limited to a single level, so task includes - could not include other files containing task includes. - -Includes can also be used in the 'handlers' section, for instance, if you -want to define how to restart apache, you only have to do that once for all -of your playbooks. You might make a handlers.yml that looks like:: - - --- - # this might be in a file like handlers/handlers.yml - - name: restart apache - service: name=apache state=restarted - -And in your main playbook file, just include it like so, at the bottom -of a play:: - - handlers: - - include: handlers/handlers.yml - -You can mix in includes along with your regular non-included tasks and handlers. - -Includes can also be used to import one playbook file into another. This allows -you to define a top-level playbook that is composed of other playbooks. - -For example:: - - - name: this is a play at the top level of a file - hosts: all - remote_user: root - - tasks: - - - name: say hi - tags: foo - shell: echo "hi..." - - - include: load_balancers.yml - - include: webservers.yml - - include: dbservers.yml - -Note that you cannot do variable substitution when including one playbook -inside another. - -.. note:: - You can not conditionally pass the location to an include file, - like you can with 'vars_files'. If you find yourself needing to do - this, consider how you can restructure your playbook to be more - class/role oriented. This is to say you cannot use a 'fact' to - decide what include file to use. All hosts contained within the - play are going to get the same tasks. ('*when*' provides some - ability for hosts to conditionally skip tasks). - - -.. _dynamic_static: - -Dynamic versus Static Includes -`````````````````````````````` - -In Ansible 2.0 there were changes on how 'task' includes are processed. The 'play' includes are still 'static' or unchanged. - -In previous versions of Ansible, all includes acted as a pre-processor statement and were read during playbook parsing time. -This created problems with things like inventory variables (like group and host vars, which are not available during the parsing time) were used in the included file name. - -After Ansible 2.0, 'task' includes can be 'dynamic', meaning they are not evaluated until the include task is reached during the play execution. -This change allows the reintroduction of loops on include statements, -such as the following:: - - - include: foo.yml param={{item}} - with_items: - - 1 - - 2 - - 3 - -It is also possible to use variables from any source with a dynamic include:: - - - include: "{{inventory_hostname}}.yml" - -Starting in 2.1, Ansible attempts to detect when a 'task' include should be dynamic (read below for details on how detection works). - -.. note:: - When an include statement loads different tasks for different hosts, - the ``linear`` strategy keeps the hosts in lock-step by alternating - which hosts are executing tasks while doing a ``noop`` for all other - hosts. For example, if you had hostA, hostB and hostC with the above - example, hostA would execute all of the tasks in hostA.yml while hostB - and hostC waited. It is generally better to do the above with the - ``free`` strategy, which does not force hosts to execute in lock-step. - -.. note:: - In Ansible 2.0 task includes were always considered dynamic, but since this - created problems in existing playbooks we changed the default in 2.1. - Continue reading below for more details. - -Dynamic includes introduced some other limitations due to the fact that the included -file is not read in until that task is reached during the execution of the play. When using dynamic includes, -it is important to keep these limitations in mind: - -* You cannot use ``notify`` to trigger a handler name which comes from a dynamic include. -* You cannot use ``--start-at-task`` to begin execution at a task inside a dynamic include. -* Tags which only exist inside a dynamic include will not show up in --list-tags output. -* Tasks which only exist inside a dynamic include will not show up in --list-tasks output. - -.. note:: - In Ansible 1.9.x and earlier, an error would be raised if a tag name was - used with ``--tags`` or ``--skip-tags``. This error was disabled in Ansible - 2.0 to prevent incorrect failures with tags which only existed inside of - dynamic includes. - -To work around these limitations, Ansible 2.1 introduces the ``static`` option for includes:: - - - include: foo.yml - static: - -By default, starting in Ansible 2.1, 'task' includes are automatically treated as static rather than -dynamic when the include meets the following conditions: - -* The include does not use any loops -* The included file name does not use any variables -* The ``static`` option is not explicitly disabled (``static: no`` is not present) -* The ansible.cfg options to force static includes (see below) are disabled - -Two options are available in the ansible.cfg configuration for static includes: - -* ``task_includes_static`` - forces all includes in tasks sections to be static. -* ``handler_includes_static`` - forces all includes in handlers sections to be static. - -These options allow users to force playbooks to behave exactly as they did in 1.9.x and before. - -One example on how 'static' vs 'dynamic' behaviour can impact your tasks:: - - - include: "stuff.yml" - static: no - when: verto is defined - -If this task were 'static' the `when` would be inherited by the tasks included, but forcing it to be dynamic, the `when` is now applied to the include task itself. - -.. _roles: - -Roles -````` - -.. versionadded:: 1.2 - -Now that you have learned about tasks and handlers, what is the best way to organize your playbooks? -The short answer is to use roles! Roles are ways of automatically loading certain vars_files, tasks, and -handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users. - -Roles are just automation around 'include' directives as described above, and really don't contain much -additional magic beyond some improvements to search path handling for referenced files. However, that can be a big thing! - -Example project structure:: - - site.yml - webservers.yml - fooservers.yml - roles/ - common/ - files/ - templates/ - tasks/ - handlers/ - vars/ - defaults/ - meta/ - webservers/ - files/ - templates/ - tasks/ - handlers/ - vars/ - defaults/ - meta/ - -In a playbook, it would look like this:: - - --- - - hosts: webservers - roles: - - common - - webservers - -This designates the following behaviors, for each role 'x': - -- If roles/x/tasks/main.yml exists, tasks listed therein will be added to the play -- If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play -- If roles/x/vars/main.yml exists, variables listed therein will be added to the play -- If roles/x/defaults/main.yml exists, variables listed therein will be added to the play -- If roles/x/meta/main.yml exists, any role dependencies listed therein will be added to the list of roles (1.3 and later) -- Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely - -In Ansible 1.4 and later you can configure a roles_path to search for roles. Use this to check all of your common roles out to one location, and share -them easily between multiple playbook projects. See :doc:`intro_configuration` for details about how to set this up in ansible.cfg. - -.. note:: - Role dependencies are discussed below. - -If any files are not present, they are just ignored. So it's ok to not have a 'vars/' subdirectory for the role, -for instance. - -Note, you are still allowed to list tasks, vars_files, and handlers "loose" in playbooks without using roles, -but roles are a good organizational feature and are highly recommended. If there are loose things in the playbook, -the roles are evaluated first. - -Also, should you wish to parameterize roles, by adding variables, you can do so, like this:: - - --- - - - hosts: webservers - roles: - - common - - { role: foo_app_instance, dir: '/opt/a', app_port: 5000 } - - { role: foo_app_instance, dir: '/opt/b', app_port: 5001 } - -While it's probably not something you should do often, you can also conditionally apply roles like so:: - - --- - - - hosts: webservers - roles: - - { role: some_role, when: "ansible_os_family == 'RedHat'" } - -This works by applying the conditional to every task in the role. Conditionals are covered later on in -the documentation. - -Finally, you may wish to assign tags to the roles you specify. You can do so inline:: - - --- - - - hosts: webservers - roles: - - { role: foo, tags: ["bar", "baz"] } - -Note that this *tags all of the tasks in that role with the tags specified*, overriding any tags that are specified inside the role. If you find yourself building a role with lots of tags and you want to call subsets of the role at different times, you should consider just splitting that role into multiple roles. - -If the play still has a 'tasks' section, those tasks are executed after roles are applied. - -If you want to define certain tasks to happen before AND after roles are applied, you can do this:: - - --- - - - hosts: webservers - - pre_tasks: - - shell: echo 'hello' - - roles: - - { role: some_role } - - tasks: - - shell: echo 'still busy' - - post_tasks: - - shell: echo 'goodbye' - -.. note:: - If using tags with tasks (described later as a means of only running part of a playbook), - be sure to also tag your pre_tasks and post_tasks and pass those along as well, especially if the pre - and post tasks are used for monitoring outage window control or load balancing. - -Role Default Variables -`````````````````````` - -.. versionadded:: 1.3 - -Role default variables allow you to set default variables for included or dependent roles (see below). To create -defaults, simply add a `defaults/main.yml` file in your role directory. These variables will have the lowest priority -of any variables available, and can be easily overridden by any other variable, including inventory variables. - -Role Dependencies -````````````````` - -.. versionadded:: 1.3 - -Role dependencies allow you to automatically pull in other roles when using a role. Role dependencies are stored in the -`meta/main.yml` file contained within the role directory. This file should contain -a list of roles and parameters to insert before the specified role, such as the following in an example -`roles/myapp/meta/main.yml`:: - - --- - dependencies: - - { role: common, some_parameter: 3 } - - { role: apache, apache_port: 80 } - - { role: postgres, dbname: blarg, other_parameter: 12 } - -Role dependencies can also be specified as a full path, just like top level roles:: - - --- - dependencies: - - { role: '/path/to/common/roles/foo', x: 1 } - -Role dependencies can also be installed from source control repos or tar files (via `galaxy`) using comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename). Both through the command line or via a requirements.yml passed to ansible-galaxy. - - -Roles dependencies are always executed before the role that includes them, and are recursive. By default, -roles can also only be added as a dependency once - if another role also lists it as a dependency it will -not be run again. This behavior can be overridden by adding `allow_duplicates: yes` to the `meta/main.yml` file. -For example, a role named 'car' could add a role named 'wheel' to its dependencies as follows:: - - --- - dependencies: - - { role: wheel, n: 1 } - - { role: wheel, n: 2 } - - { role: wheel, n: 3 } - - { role: wheel, n: 4 } - -And the `meta/main.yml` for wheel contained the following:: - - --- - allow_duplicates: yes - dependencies: - - { role: tire } - - { role: brake } - -The resulting order of execution would be as follows:: - - tire(n=1) - brake(n=1) - wheel(n=1) - tire(n=2) - brake(n=2) - wheel(n=2) - ... - car - -.. note:: - Variable inheritance and scope are detailed in the :doc:`playbooks_variables`. - -Embedding Modules and Plugins In Roles -`````````````````````````````````````` - -This is an advanced topic that should not be relevant for most users. - -If you write a custom module (see :doc:`dev_guide/developing_modules`) or a plugin (see :doc:`dev_guide/developing_plugins`), you may wish to distribute it as part of a role. -Generally speaking, Ansible as a project is very interested in taking high-quality modules into ansible core for inclusion, so this shouldn't be the norm, but it's quite easy to do. - -A good example for this is if you worked at a company called AcmeWidgets, and wrote an internal module that helped configure your internal software, and you wanted other -people in your organization to easily use this module -- but you didn't want to tell everyone how to configure their Ansible library path. - -Alongside the 'tasks' and 'handlers' structure of a role, add a directory named 'library'. In this 'library' directory, then include the module directly inside of it. - -Assuming you had this:: - - roles/ - my_custom_modules/ - library/ - module1 - module2 - -The module will be usable in the role itself, as well as any roles that are called *after* this role, as follows:: - - - - hosts: webservers - roles: - - my_custom_modules - - some_other_role_using_my_custom_modules - - yet_another_role_using_my_custom_modules - -This can also be used, with some limitations, to modify modules in Ansible's core distribution, such as to use development versions of modules before they are released -in production releases. This is not always advisable as API signatures may change in core components, however, and is not always guaranteed to work. It can be a handy -way of carrying a patch against a core module, however, should you have good reason for this. Naturally the project prefers that contributions be directed back -to github whenever possible via a pull request. - -The same mechanism can be used to embed and distribute plugins in a role, using the same schema. For example, for a filter plugin:: - - roles/ - my_custom_filter/ - filter_plugins - filter1 - filter2 - -They can then be used in a template or a jinja template in any role called after 'my_custom_filter' - -Ansible Galaxy -`````````````` - -`Ansible Galaxy `_ is a free site for finding, downloading, rating, and reviewing all kinds of community developed Ansible roles and can be a great way to get a jumpstart on your automation projects. - -You can sign up with social auth, and the download client 'ansible-galaxy' is included in Ansible 1.4.2 and later. - -Read the "About" page on the Galaxy site for more information. +The documentation regarding roles and includes for playbooks have moved. Their new location is here: :doc:`playbooks_reuse`. Please update any links you may have made directly to this page. .. seealso:: :doc:`galaxy` How to share roles on galaxy, role management - :doc:`YAMLSyntax` - Learn about YAML syntax :doc:`playbooks` Review the basic Playbook language features - :doc:`playbooks_best_practices` - Various tips about managing playbooks in the real world - :doc:`playbooks_variables` - All about variables in playbooks - :doc:`playbooks_conditionals` - Conditionals in playbooks - :doc:`playbooks_loops` - Loops in playbooks - :doc:`modules` - Learn about available modules - :doc:`dev_guide/developing_modules` - Learn how to extend Ansible by writing your own modules - `GitHub Ansible examples `_ - Complete playbook files from the GitHub project source - `Mailing List `_ - Questions? Help? Ideas? Stop by the list on Google Groups + :doc:`playbooks_reuse` + Creating reusable Playbooks. diff --git a/docs/docsite/rst/playbooks_strategies.rst b/docs/docsite/rst/playbooks_strategies.rst index a34daa1c1f7..8d79f038a45 100644 --- a/docs/docsite/rst/playbooks_strategies.rst +++ b/docs/docsite/rst/playbooks_strategies.rst @@ -32,7 +32,7 @@ One example is ``debug`` strategy. See :doc:`playbooks_debugger` for details. :doc:`playbooks` An introduction to playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles `User Mailing List `_ Have a question? Stop by the google group! diff --git a/docs/docsite/rst/playbooks_tags.rst b/docs/docsite/rst/playbooks_tags.rst index 1d294687988..3038541fd17 100644 --- a/docs/docsite/rst/playbooks_tags.rst +++ b/docs/docsite/rst/playbooks_tags.rst @@ -119,7 +119,7 @@ By default ansible runs as if ``--tags all`` had been specified. :doc:`playbooks` An introduction to playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles `User Mailing List `_ Have a question? Stop by the google group! diff --git a/docs/docsite/rst/playbooks_templating.rst b/docs/docsite/rst/playbooks_templating.rst index 62a6f04ea08..1b55f232ea8 100644 --- a/docs/docsite/rst/playbooks_templating.rst +++ b/docs/docsite/rst/playbooks_templating.rst @@ -25,7 +25,7 @@ Please note that all templating happens on the Ansible controller before the tas Conditional statements in playbooks :doc:`playbooks_loops` Looping in playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_tests.rst b/docs/docsite/rst/playbooks_tests.rst index 27f6cb340ac..523ece79221 100644 --- a/docs/docsite/rst/playbooks_tests.rst +++ b/docs/docsite/rst/playbooks_tests.rst @@ -175,7 +175,7 @@ The following tasks are illustrative of the tests meant to check the status of t All about variables :doc:`playbooks_loops` Looping in playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/docs/docsite/rst/playbooks_variables.rst b/docs/docsite/rst/playbooks_variables.rst index 39496530710..227235fb2e5 100644 --- a/docs/docsite/rst/playbooks_variables.rst +++ b/docs/docsite/rst/playbooks_variables.rst @@ -90,7 +90,7 @@ Variables defined from included files and roles It turns out we've already talked about variables in another place too. -As described in :doc:`playbooks_roles`, variables can also be included in the playbook via include files, which may or may +As described in :doc:`playbooks_reuse_roles`, variables can also be included in the playbook via include files, which may or may not be part of an "Ansible Role". Usage of roles is preferred as it provides a nice organizational system. .. _about_jinja2: @@ -946,7 +946,7 @@ roles aren't you? Hint hint. Ok, so if you are writing a redistributable role with reasonable defaults, put those in the ``roles/x/defaults/main.yml`` file. This means the role will bring along a default value but ANYTHING in Ansible will override it. It's just a default. That's why it says "defaults" :) -See :doc:`playbooks_roles` for more info about this:: +See :doc:`playbooks_reuse_roles` for more info about this:: --- # file: roles/x/defaults/main.yml @@ -1029,7 +1029,7 @@ For information about advanced YAML syntax used to declare variables and have mo Jinja2 filters and their uses :doc:`playbooks_loops` Looping in playbooks - :doc:`playbooks_roles` + :doc:`playbooks_reuse_roles` Playbook organization by roles :doc:`playbooks_best_practices` Best practices in playbooks diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index cc8276ec0d3..7df69f48aa8 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -432,7 +432,7 @@ class TaskExecutor: if self._loop_eval_error is not None: raise self._loop_eval_error # skip conditional exception in the case of includes as the vars needed might not be available except in the included tasks or due to tags - if self._task.action not in ['include', 'include_role']: + if self._task.action not in ['include', 'include_tasks', 'include_role']: raise # Not skipping, if we had loop error raised earlier we need to raise it now to halt the execution of this task @@ -445,7 +445,7 @@ class TaskExecutor: # if this task is a TaskInclude, we just return now with a success code so the # main thread can expand the task list for the given host - if self._task.action == 'include': + if self._task.action in ('include', 'include_tasks'): include_variables = self._task.args.copy() include_file = include_variables.pop('_raw_params', None) if not include_file: diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index 3e24853b19c..f5a6886e1cc 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -83,11 +83,11 @@ class TaskQueueManager: self._callback_plugins = [] self._start_at_done = False - # make sure the module path (if specified) is parsed and - # added to the module_loader object - if options.module_path is not None: - for path in options.module_path.split(os.pathsep): - module_loader.add_directory(path) + # make sure any module paths (if specified) are added to the module_loader + if isinstance(options.module_path, list): + for path in options.module_path: + if path is not None: + module_loader.add_directory(path) # a special flag to help us exit cleanly self._terminated = False diff --git a/lib/ansible/parsing/mod_args.py b/lib/ansible/parsing/mod_args.py index 2ed4b9202cc..d48a3d2468b 100644 --- a/lib/ansible/parsing/mod_args.py +++ b/lib/ansible/parsing/mod_args.py @@ -36,6 +36,10 @@ RAW_PARAM_MODULES = ([ 'script', 'include', 'include_vars', + 'include_tasks', + 'include_role', + 'import_tasks', + 'import_role', 'add_host', 'group_by', 'set_fact', @@ -281,7 +285,7 @@ class ModuleArgsParser: # walk the input dictionary to see we recognize a module name for (item, value) in iteritems(self._task_ds): - if item in module_loader or item in ['meta', 'include', 'include_role']: + if item in module_loader or item in ['meta', 'include', 'include_tasks', 'include_role', 'import_tasks', 'import_role']: # finding more than one module name is a problem if action is not None: raise AnsibleParserError("conflicting action statements: %s, %s" % (action, item), obj=self._task_ds) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index c47025d5eb7..cc0451a2f80 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -89,7 +89,9 @@ class Playbook: self._loader.set_basedir(cur_basedir) raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry) - if 'include' in entry: + if 'include' in entry or 'import_playbook' in entry: + if 'include' in entry: + display.deprecated("You should use 'import_playbook' instead of 'include' for playbook includes") pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader) if pb is not None: self._entries.extend(pb._entries) diff --git a/lib/ansible/playbook/helpers.py b/lib/ansible/playbook/helpers.py index f2a5b6fb334..7b500de44a5 100644 --- a/lib/ansible/playbook/helpers.py +++ b/lib/ansible/playbook/helpers.py @@ -108,7 +108,11 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h ) task_list.append(t) else: - if 'include' in task_ds: + if 'include' in task_ds or 'import_tasks' in task_ds or 'include_tasks' in task_ds: + if 'include' in task_ds: + display.deprecated("The use of 'include' for tasks has been deprecated. " + "Use 'import_tasks' for static inclusions or 'include_tasks' for dynamic inclusions") + if use_handlers: include_class = HandlerTaskInclude else: @@ -129,7 +133,13 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # check to see if this include is dynamic or static: # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set - if t.static is not None: + if 'include_tasks' in task_ds: + is_static = False + elif 'import_tasks' in task_ds: + is_static = True + elif t.static is not None: + display.deprecated("The use of 'static' has been deprecated. " + "Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion") is_static = t.static else: is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ @@ -138,7 +148,10 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h if is_static: if t.loop is not None: - raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds) + if 'import_tasks' in task_ds: + raise AnsibleParserError("You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.", obj=task_ds) + else: + raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds) # we set a flag to indicate this include was static t.statically_loaded = True @@ -202,7 +215,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # the same fashion used by the on_include callback. We also do it here, # because the recursive nature of helper methods means we may be loading # nested includes, and we want the include order printed correctly - display.vv("statically included: %s" % include_file) + display.vv("statically imported: %s" % include_file) except AnsibleFileNotFound: if t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ @@ -267,8 +280,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h else: task_list.append(t) - elif 'include_role' in task_ds: - + elif 'include_role' in task_ds or 'import_role' in task_ds: ir = IncludeRole.load( task_ds, block=block, @@ -280,7 +292,11 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set + if 'import_role' in task_ds: + is_static = True if ir.static is not None: + display.deprecated("The use of 'static' for 'include_role' has been deprecated. " + "Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion") is_static = ir.static else: display.debug('Determine if include_role is static') diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py index 5a7902db745..5784ea06507 100644 --- a/lib/ansible/playbook/included_file.py +++ b/lib/ansible/playbook/included_file.py @@ -64,7 +64,7 @@ class IncludedFile: original_host = res._host original_task = res._task - if original_task.action == 'include': + if original_task.action in ('include', 'include_tasks'): if original_task.loop: if 'results' not in res._result: continue diff --git a/lib/ansible/playbook/playbook_include.py b/lib/ansible/playbook/playbook_include.py index 3cf6aca1882..04423ff6ec8 100644 --- a/lib/ansible/playbook/playbook_include.py +++ b/lib/ansible/playbook/playbook_include.py @@ -35,7 +35,7 @@ from ansible.template import Templar class PlaybookInclude(Base, Conditional, Taggable): _name = FieldAttribute(isa='string') - _include = FieldAttribute(isa='string') + _import_playbook = FieldAttribute(isa='string') _vars = FieldAttribute(isa='dict', default=dict()) @staticmethod @@ -66,7 +66,7 @@ class PlaybookInclude(Base, Conditional, Taggable): # then we use the object to load a Playbook pb = Playbook(loader=loader) - file_name = templar.template(new_obj.include) + file_name = templar.template(new_obj.import_playbook) if not os.path.isabs(file_name): file_name = os.path.join(basedir, file_name) @@ -114,35 +114,35 @@ class PlaybookInclude(Base, Conditional, Taggable): new_ds.ansible_pos = ds.ansible_pos for (k, v) in iteritems(ds): - if k == 'include': - self._preprocess_include(ds, new_ds, k, v) + if k in ('include', 'import_playbook'): + self._preprocess_import(ds, new_ds, k, v) else: # some basic error checking, to make sure vars are properly # formatted and do not conflict with k=v parameters if k == 'vars': if 'vars' in new_ds: - raise AnsibleParserError("include parameters cannot be mixed with 'vars' entries for include statements", obj=ds) + raise AnsibleParserError("import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) elif not isinstance(v, dict): - raise AnsibleParserError("vars for include statements must be specified as a dictionary", obj=ds) + raise AnsibleParserError("vars for import_playbook statements must be specified as a dictionary", obj=ds) new_ds[k] = v return super(PlaybookInclude, self).preprocess_data(new_ds) - def _preprocess_include(self, ds, new_ds, k, v): + def _preprocess_import(self, ds, new_ds, k, v): ''' - Splits the include line up into filename and parameters + Splits the playbook import line up into filename and parameters ''' if v is None: - raise AnsibleParserError("include parameter is missing", obj=ds) + raise AnsibleParserError("playbook import parameter is missing", obj=ds) - # The include line must include at least one item, which is the filename - # to include. Anything after that should be regarded as a parameter to the include + # The import_playbook line must include at least one item, which is the filename + # to import. Anything after that should be regarded as a parameter to the import items = split_args(v) if len(items) == 0: - raise AnsibleParserError("include statements must specify the file name to include", obj=ds) + raise AnsibleParserError("import_playbook statements must specify the file name to import", obj=ds) else: - new_ds['include'] = items[0] + new_ds['import_playbook'] = items[0] if len(items) > 1: # rejoin the parameter portion of the arguments and # then use parse_kv() to get a dict of params back @@ -150,5 +150,5 @@ class PlaybookInclude(Base, Conditional, Taggable): if 'tags' in params: new_ds['tags'] = params.pop('tags') if 'vars' in new_ds: - raise AnsibleParserError("include parameters cannot be mixed with 'vars' entries for include statements", obj=ds) + raise AnsibleParserError("import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) new_ds['vars'] = params diff --git a/lib/ansible/playbook/role_include.py b/lib/ansible/playbook/role_include.py index 44d57fab575..ccb2b1cf9ff 100644 --- a/lib/ansible/playbook/role_include.py +++ b/lib/ansible/playbook/role_include.py @@ -23,7 +23,7 @@ from os.path import basename from ansible.errors import AnsibleParserError from ansible.playbook.attribute import FieldAttribute -from ansible.playbook.task import Task +from ansible.playbook.task_include import TaskInclude from ansible.playbook.role import Role from ansible.playbook.role.include import RoleInclude @@ -36,7 +36,7 @@ except ImportError: __all__ = ['IncludeRole'] -class IncludeRole(Task): +class IncludeRole(TaskInclude): """ A Role include is derived from a regular role to handle the special @@ -55,7 +55,6 @@ class IncludeRole(Task): super(IncludeRole, self).__init__(block=block, role=role, task_include=task_include) - self.statically_loaded = False self._from_files = {} self._parent_role = role self._role_name = None diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 979f0ce8a58..a40cd79b287 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -215,10 +215,10 @@ class Task(Base, Conditional, Taggable, Become): # top level of the task, so we move those into the 'vars' dictionary # here, and show a deprecation message as we will remove this at # some point in the future. - if action == 'include' and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES: - display.deprecated("Specifying include variables at the top-level of the task is deprecated. " - "Please see:\nhttp://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n " - "for currently supported syntax regarding included files and variables", version="2.7") + if action in ('include', 'include_tasks') and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES: + display.deprecated("Specifying include variables at the top-level of the task is deprecated." + " Please see:\nhttp://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n" + " for currently supported syntax regarding included files and variables", version="2.7") new_ds['vars'][k] = v else: new_ds[k] = v @@ -331,7 +331,7 @@ class Task(Base, Conditional, Taggable, Become): all_vars = dict() if self._parent: all_vars.update(self._parent.get_include_params()) - if self.action in ('include', 'include_role'): + if self.action in ('include', 'include_tasks', 'include_role'): all_vars.update(self.vars) return all_vars diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py index 8313c395130..87d7e336427 100644 --- a/lib/ansible/plugins/callback/default.py +++ b/lib/ansible/plugins/callback/default.py @@ -27,6 +27,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type from ansible import constants as C +from ansible.playbook.task_include import TaskInclude from ansible.plugins.callback import CallbackBase from ansible.utils.color import colorize, hostcolor @@ -80,7 +81,7 @@ class CallbackModule(CallbackBase): delegated_vars = result._result.get('_ansible_delegated_vars', None) self._clean_results(result._result, result._task.action) - if result._task.action in ('include', 'include_role'): + if isinstance(result._task, TaskInclude): return elif result._result.get('changed', False): if delegated_vars: @@ -194,7 +195,7 @@ class CallbackModule(CallbackBase): def v2_runner_item_on_ok(self, result): delegated_vars = result._result.get('_ansible_delegated_vars', None) - if result._task.action in ('include', 'include_role'): + if isinstance(result._task, TaskInclude): return elif result._result.get('changed', False): msg = 'changed' diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index a26074b5b9a..2b10f87b7a1 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -535,7 +535,7 @@ class StrategyBase: if self._diff: self._tqm.send_callback('v2_on_file_diff', task_result) - if original_task.action not in ['include', 'include_role']: + if not isinstance(original_task, TaskInclude): self._tqm._stats.increment('ok', original_host.name) if 'changed' in task_result._result and task_result._result['changed']: self._tqm._stats.increment('changed', original_host.name)